ordercheck passes.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@34 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 23e9e66..fc5e56e 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JAbsentArrayDimension;
@@ -26,14 +40,22 @@
  */
 public class ArrayNormalizer {
 
-  private final JMethod setCheckMethod;
-  private final JMethod initDims;
-  private final JMethod initValues;
-
   private class ArrayVisitor extends JVisitor {
 
     private final ChangeList changeList = new ChangeList(
-      "Transform array accesses to check bounds.");
+        "Transform array accesses to check bounds.");
+
+    // @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;
@@ -53,7 +75,7 @@
         // see if we need to do a checked store
         // primitives and (effectively) final are statically correct
         if (elementType instanceof JReferenceType
-          && !((JReferenceType) elementType).isFinal()) {
+            && !((JReferenceType) elementType).isFinal()) {
           // replace this assignment with a call to setCheck()
 
           // DON'T VISIT ARRAYREF, but do visit its children
@@ -63,7 +85,7 @@
 
           JMethodCall call = new JMethodCall(program, null, setCheckMethod);
           ChangeList myChanges = new ChangeList("Replace " + x
-            + " with a call to Array.setCheck()");
+              + " with a call to Array.setCheck()");
           myChanges.replaceExpression(m, call);
           myChanges.addExpression(arrayRef.instance, call.args);
           myChanges.addExpression(arrayRef.indexExpr, call.args);
@@ -75,22 +97,24 @@
       return true;
     }
 
-    // @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);
+    private char[] calcClassName(JArrayType type) {
+      String leafName = type.getLeafType().getJsniSignatureName();
+      leafName = leafName.replace('/', '.');
+      int leafLength = leafName.length();
+      int nDims = type.getDims();
+      char[] className = new char[leafLength + nDims];
+      for (int i = 0; i < nDims; ++i) {
+        className[i] = '[';
       }
+
+      leafName.getChars(0, leafLength, className, nDims);
+      return className;
     }
 
     private void processDims(JNewArray x, Mutator m, JArrayType arrayType,
         JLiteral litTypeName) {
       ChangeList myChanges = new ChangeList("Replace " + x
-        + " with a call to Array.initDims()");
+          + " 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);
       JsonArray typeIdList = new JsonArray(program);
@@ -103,7 +127,7 @@
         if (dim.get() instanceof JAbsentArrayDimension) {
           break;
         }
-        
+
         /*
          * For each non-empty dimension, reduce the number of dims on the end
          * type.
@@ -126,7 +150,7 @@
       if (outstandingDims > 0) {
         targetType = program.getTypeArray(targetType, outstandingDims);
       }
-      
+
       myChanges.addExpression(litTypeName, call.args);
       myChanges.addExpression(typeIdList, call.args);
       myChanges.addExpression(queryIdList, call.args);
@@ -144,7 +168,7 @@
       JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(arrayType));
       JsonArray initList = new JsonArray(program);
       ChangeList myChanges = new ChangeList("Replace " + x
-        + " with a call to Array.initValues()");
+          + " 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);
@@ -165,31 +189,18 @@
       }
       return leafTypeId;
     }
-
-    private char[] calcClassName(JArrayType type) {
-      String leafName = type.getLeafType().getJsniSignatureName();
-      leafName = leafName.replace('/', '.');
-      int leafLength = leafName.length();
-      int nDims = type.getDims();
-      char[] className = new char[leafLength + nDims];
-      for (int i = 0; i < nDims; ++i) {
-        className[i] = '[';
-      }
-      
-      leafName.getChars(0, leafLength, className, nDims);
-      return className;
-    }
   }
 
-  private void execImpl() {
-    ArrayVisitor visitor = new ArrayVisitor();
-    program.traverse(visitor);
-    ChangeList changes = visitor.getChangeList();
-    if (!changes.empty()) {
-      changes.apply();
-    }
+  public static void exec(JProgram program) {
+    new ArrayNormalizer(program).execImpl();
   }
 
+  private final JMethod setCheckMethod;
+
+  private final JMethod initDims;
+
+  private final JMethod initValues;
+
   private final JProgram program;
 
   private ArrayNormalizer(JProgram program) {
@@ -199,8 +210,13 @@
     initValues = program.getSpecialMethod("Array.initValues");
   }
 
-  public static void exec(JProgram program) {
-    new ArrayNormalizer(program).execImpl();
+  private void execImpl() {
+    ArrayVisitor visitor = new ArrayVisitor();
+    program.traverse(visitor);
+    ChangeList changes = visitor.getChangeList();
+    if (!changes.empty()) {
+      changes.apply();
+    }
   }
 
 }
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 a235ceb..abb4fe0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JClassType;
@@ -58,117 +72,6 @@
  */
 public class BuildTypeMap {
 
-  public static TypeDeclaration[] exec(TypeMap typeMap,
-      CompilationUnitDeclaration[] unitDecls, JsProgram jsProgram) {
-    createPeersForTypes(unitDecls, typeMap);
-    return createPeersForNonTypeDecls(unitDecls, typeMap, jsProgram);
-  }
-
-  private static void createPeersForTypes(
-      CompilationUnitDeclaration[] unitDecls, TypeMap typeMap) {
-    // Traverse once to create our JNode peers for each type
-    BuildTypeMapVisitor v1 = new BuildTypeMapVisitor(typeMap);
-    for (int i = 0; i < unitDecls.length; ++i) {
-      unitDecls[i].traverse(v1, unitDecls[i].scope);
-    }
-  }
-
-  private static TypeDeclaration[] createPeersForNonTypeDecls(
-      CompilationUnitDeclaration[] unitDecls, TypeMap typeMap,
-      JsProgram jsProgram) {
-    // Traverse again to create our JNode peers for each method, field,
-    // parameter, and local
-    BuildDeclMapVisitor v2 = new BuildDeclMapVisitor(typeMap, jsProgram);
-    for (int i = 0; i < unitDecls.length; ++i) {
-      unitDecls[i].traverse(v2, unitDecls[i].scope);
-    }
-    return v2.getTypeDeclarataions();
-  }
-
-  /**
-   * Creates JNodes for every type and memorizes the mapping from the JDT
-   * Binding to the corresponding JNode for each created type. Note that since
-   * there could be forward references, it is not possible to set up supertypes;
-   * it must be done is a subsequent pass.
-   */
-  private static class BuildTypeMapVisitor extends ASTVisitor {
-
-    private final TypeMap fTypeMap;
-    private final JProgram fProgram;
-
-    public BuildTypeMapVisitor(TypeMap typeMap) {
-      fTypeMap = typeMap;
-      fProgram = fTypeMap.getProgram();
-    }
-
-    private boolean process(TypeDeclaration typeDeclaration) {
-      char[][] name = typeDeclaration.binding.compoundName;
-      SourceTypeBinding binding = typeDeclaration.binding;
-      if (binding instanceof LocalTypeBinding) {
-        char[] localName = binding.constantPoolName();
-        if (localName == null) {
-          /*
-           * Weird case: if JDT determines that this local class is totally
-           * uninstantiable, it won't bother allocating a local name.
-           */
-          return false;
-        }
-
-        for (int i = 0, c = localName.length; i < c; ++i) {
-          if (localName[i] == '/') {
-            localName[i] = '.';
-          }
-        }
-        name = new char[1][0];
-        name[0] = localName;
-      }
-
-      JReferenceType newType;
-      if (binding.isClass()) {
-        newType = fProgram.createClass(name, binding.isAbstract(),
-          binding.isFinal());
-      } else if (binding.isInterface()) {
-        newType = fProgram.createInterface(name);
-      } else {
-        assert (false);
-        return false;
-      }
-
-      /**
-       * We emulate static initializers and intance initializers as methods. As
-       * in other cases, this gives us: simpler AST, easier to optimize, more
-       * like output JavaScript. Clinit is always in slot 0, init (if it exists)
-       * is always in slot 1.
-       */
-      JMethod clinit = fProgram.createMethod("$clinit".toCharArray(), newType,
-        fProgram.getTypeVoid(), false, true, true, true, false);
-      clinit.freezeParamTypes();
-
-      if (newType instanceof JClassType) {
-        JMethod init = fProgram.createMethod("$init".toCharArray(), newType,
-          fProgram.getTypeVoid(), false, false, true, true, false);
-        init.freezeParamTypes();
-      }
-
-      fTypeMap.put(binding, newType);
-      return true;
-    }
-
-    public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
-      assert (localTypeDeclaration.kind() != IGenericType.INTERFACE_DECL);
-      return process(localTypeDeclaration);
-    }
-
-    public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
-      return process(memberTypeDeclaration);
-    }
-
-    public boolean visit(TypeDeclaration typeDeclaration,
-        CompilationUnitScope scope) {
-      return process(typeDeclaration);
-    }
-  }
-
   /**
    * Creates JNodes for every method, field, initializer, parameter, and local
    * and memorizes the mapping from the JDT Binding to the corresponding JNode
@@ -186,55 +89,33 @@
    */
   private static class BuildDeclMapVisitor extends ASTVisitor {
 
-    private ArrayList/* <TypeDeclaration> */fTypeDecls = new ArrayList/* <TypeDeclaration> */();
-    private final TypeMap fTypeMap;
-    private JProgram fProgram;
+    private ArrayList/* <TypeDeclaration> */typeDecls = new ArrayList/* <TypeDeclaration> */();
+    private final TypeMap typeMap;
+    private JProgram program;
     private final JsProgram jsProgram;
     private final JsParser jsParser = new JsParser();
 
     public BuildDeclMapVisitor(TypeMap typeMap, JsProgram jsProgram) {
-      fTypeMap = typeMap;
-      fProgram = fTypeMap.getProgram();
+      this.typeMap = typeMap;
+      program = this.typeMap.getProgram();
       this.jsProgram = jsProgram;
     }
 
     public TypeDeclaration[] getTypeDeclarataions() {
-      return (TypeDeclaration[]) fTypeDecls.toArray(new TypeDeclaration[fTypeDecls.size()]);
-    }
-
-    private void mapParameters(JMethod method, AbstractMethodDeclaration x) {
-      MethodBinding b = x.binding;
-      int paramCount = (b.parameters != null ? b.parameters.length : 0);
-      if (paramCount > 0) {
-        for (int i = 0, n = x.arguments.length; i < n; ++i) {
-          createParameter(x.arguments[i].binding, method);
-        }
-      }
-      method.freezeParamTypes();
-    }
-
-    private void mapThrownExceptions(JMethod method, AbstractMethodDeclaration x) {
-      MethodBinding b = x.binding;
-      if (b.thrownExceptions != null) {
-        for (int i = 0; i < b.thrownExceptions.length; ++i) {
-          ReferenceBinding refBinding = b.thrownExceptions[i];
-          JClassType thrownException = (JClassType) fTypeMap.get(refBinding);
-          method.thrownExceptions.add(thrownException);
-        }
-      }
+      return (TypeDeclaration[]) typeDecls.toArray(new TypeDeclaration[typeDecls.size()]);
     }
 
     public boolean visit(Argument argument, BlockScope scope) {
       if (scope == scope.methodScope()) {
         return true;
       }
-      
+
       LocalVariableBinding b = argument.binding;
-      JType localType = (JType) fTypeMap.get(b.type);
+      JType localType = (JType) typeMap.get(b.type);
       JMethod enclosingMethod = findEnclosingMethod(scope);
-      JLocal newLocal = fProgram.createLocal(argument.name, localType,
-        b.isFinal(), enclosingMethod);
-      fTypeMap.put(b, newLocal);
+      JLocal newLocal = program.createLocal(argument.name, localType,
+          b.isFinal(), enclosingMethod);
+      typeMap.put(b, newLocal);
       return true;
     }
 
@@ -247,10 +128,11 @@
      */
     public boolean visit(ConstructorDeclaration ctorDecl, ClassScope scope) {
       MethodBinding b = ctorDecl.binding;
-      JClassType enclosingType = (JClassType) fTypeMap.get(scope.enclosingSourceType());
+      JClassType enclosingType = (JClassType) typeMap.get(scope.enclosingSourceType());
       String name = enclosingType.getShortName();
-      JMethod newMethod = fProgram.createMethod(name.toCharArray(),
-        enclosingType, enclosingType, false, false, true, b.isPrivate(), false);
+      JMethod newMethod = program.createMethod(name.toCharArray(),
+          enclosingType, enclosingType, false, false, true, b.isPrivate(),
+          false);
       mapThrownExceptions(newMethod, ctorDecl);
 
       int syntheticParamCount = 0;
@@ -271,7 +153,7 @@
             alreadyNamedVariables.add(argName);
           }
         }
-        
+
         if (nestedBinding.outerLocalVariables != null) {
           for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
@@ -294,48 +176,65 @@
         newMethod.getOriginalParamTypes().remove(0);
       }
 
-      fTypeMap.put(b, newMethod);
+      typeMap.put(b, newMethod);
+      return true;
+    }
+
+    public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
+      FieldBinding b = fieldDeclaration.binding;
+      JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.enclosingSourceType());
+      createField(b, enclosingType, fieldDeclaration.initialization != null);
+      return true;
+    }
+
+    public boolean visit(LocalDeclaration localDeclaration, BlockScope scope) {
+      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);
+      typeMap.put(b, newLocal);
       return true;
     }
 
     public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
       MethodBinding b = methodDeclaration.binding;
-      JType returnType = (JType) fTypeMap.get(methodDeclaration.returnType.resolvedType);
-      JReferenceType enclosingType = (JReferenceType) fTypeMap.get(scope.enclosingSourceType());
-      JMethod newMethod = fProgram.createMethod(methodDeclaration.selector,
-        enclosingType, returnType, b.isAbstract(), b.isStatic(), b.isFinal(),
-        b.isPrivate(), b.isNative());
+      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());
 
       mapThrownExceptions(newMethod, methodDeclaration);
       mapParameters(newMethod, methodDeclaration);
-      fTypeMap.put(b, newMethod);
+      typeMap.put(b, newMethod);
 
       if (newMethod.isNative()) {
         // Handle JSNI block
         char[] source = methodDeclaration.compilationResult().getCompilationUnit().getContents();
         String jsniCode = String.valueOf(source, methodDeclaration.bodyStart,
-          methodDeclaration.bodyEnd - methodDeclaration.bodyStart + 1);
+            methodDeclaration.bodyEnd - methodDeclaration.bodyStart + 1);
         int startPos = jsniCode.indexOf("/*-{");
         int endPos = jsniCode.lastIndexOf("}-*/");
         if (startPos < 0 && endPos < 0) {
           GenerateJavaAST.reportJsniError(
-            methodDeclaration,
-            "Native methods require a JavaScript implementation enclosed with /*-{ and }-*/",
-            0);
+              methodDeclaration,
+              "Native methods require a JavaScript implementation enclosed with /*-{ and }-*/",
+              0);
           return true;
         }
         if (startPos < 0) {
           GenerateJavaAST.reportJsniError(
-            methodDeclaration,
-            "Unable to find start of native block; begin your JavaScript block with: /*-{",
-            0);
+              methodDeclaration,
+              "Unable to find start of native block; begin your JavaScript block with: /*-{",
+              0);
           return true;
         }
         if (endPos < 0) {
           GenerateJavaAST.reportJsniError(
-            methodDeclaration,
-            "Unable to find end of native block; terminate your JavaScript block with: }-*/",
-            0);
+              methodDeclaration,
+              "Unable to find end of native block; terminate your JavaScript block with: }-*/",
+              0);
           return true;
         }
 
@@ -368,8 +267,8 @@
           ((JsniMethod) newMethod).setFunc(jsFunction);
         } catch (IOException e) {
           throw new InternalCompilerException(
-            "Internal error parsing JSNI in method '" + newMethod
-              + "' in type '" + enclosingType.getName() + "'", e);
+              "Internal error parsing JSNI in method '" + newMethod
+                  + "' in type '" + enclosingType.getName() + "'", e);
         } catch (JsParserException e) {
 
           /*
@@ -387,10 +286,10 @@
                 if (i + 1 < n && chars[i + 1] == '\n') {
                   ++i;
                 }
-              // intentional fall-through
+                // intentional fall-through
               case '\n':
                 --line;
-              // intentional fall-through
+                // intentional fall-through
               default:
                 ++i;
             }
@@ -406,21 +305,95 @@
       return true;
     }
 
-    public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
-      FieldBinding b = fieldDeclaration.binding;
-      JReferenceType enclosingType = (JReferenceType) fTypeMap.get(scope.enclosingSourceType());
-      createField(b, enclosingType, fieldDeclaration.initialization != null);
-      return true;
+    public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
+      return process(localTypeDeclaration);
     }
 
-    public boolean visit(LocalDeclaration localDeclaration, BlockScope scope) {
-      LocalVariableBinding b = localDeclaration.binding;
-      JType localType = (JType) fTypeMap.get(localDeclaration.type.resolvedType);
-      JMethod enclosingMethod = findEnclosingMethod(scope);
-      JLocal newLocal = fProgram.createLocal(localDeclaration.name, localType,
-        b.isFinal(), enclosingMethod);
-      fTypeMap.put(b, newLocal);
-      return true;
+    public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
+      return process(memberTypeDeclaration);
+    }
+
+    public boolean visit(TypeDeclaration typeDeclaration,
+        CompilationUnitScope scope) {
+      return process(typeDeclaration);
+    }
+
+    private JField createField(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);
+      typeMap.put(binding, field);
+      return field;
+    }
+
+    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);
+      if (binding.matchingField != null) {
+        typeMap.put(binding.matchingField, field);
+      }
+      typeMap.put(binding, field);
+      return field;
+    }
+
+    private JParameter createParameter(LocalVariableBinding binding,
+        JMethod enclosingMethod) {
+      JType type = (JType) typeMap.get(binding.type);
+      JParameter param = program.createParameter(binding.name, type,
+          binding.isFinal(), enclosingMethod);
+      typeMap.put(binding, param);
+      return param;
+    }
+
+    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);
+      return param;
+    }
+
+    private JMethod findEnclosingMethod(BlockScope scope) {
+      MethodScope methodScope = scope.methodScope();
+      if (methodScope.isInsideInitializer()) {
+        JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.classScope().referenceContext.binding);
+        if (methodScope.isStatic) {
+          // clinit
+          return (JMethod) enclosingType.methods.get(0);
+        } else {
+          // init
+          assert (enclosingType instanceof JClassType);
+          return (JMethod) enclosingType.methods.get(1);
+        }
+      }
+
+      AbstractMethodDeclaration referenceMethod = methodScope.referenceMethod();
+      return (JMethod) typeMap.get(referenceMethod.binding);
+    }
+
+    private void mapParameters(JMethod method, AbstractMethodDeclaration x) {
+      MethodBinding b = x.binding;
+      int paramCount = (b.parameters != null ? b.parameters.length : 0);
+      if (paramCount > 0) {
+        for (int i = 0, n = x.arguments.length; i < n; ++i) {
+          createParameter(x.arguments[i].binding, method);
+        }
+      }
+      method.freezeParamTypes();
+    }
+
+    private void mapThrownExceptions(JMethod method, AbstractMethodDeclaration x) {
+      MethodBinding b = x.binding;
+      if (b.thrownExceptions != null) {
+        for (int i = 0; i < b.thrownExceptions.length; ++i) {
+          ReferenceBinding refBinding = b.thrownExceptions[i];
+          JClassType thrownException = (JClassType) typeMap.get(refBinding);
+          method.thrownExceptions.add(thrownException);
+        }
+      }
     }
 
     /**
@@ -442,7 +415,7 @@
          */
         return false;
       }
-      JReferenceType type = (JReferenceType) fTypeMap.get(binding);
+      JReferenceType type = (JReferenceType) typeMap.get(binding);
 
       if (binding.isNestedType() && !binding.isStatic()) {
         // add synthentic fields for outer this and locals
@@ -456,7 +429,7 @@
             }
           }
         }
-        
+
         if (nestedBinding.outerLocalVariables != null) {
           for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
@@ -468,7 +441,7 @@
       ReferenceBinding superClassBinding = binding.superclass();
       if (superClassBinding != null) {
         assert (binding.superclass().isClass());
-        JClassType superClass = (JClassType) fTypeMap.get(superClassBinding);
+        JClassType superClass = (JClassType) typeMap.get(superClassBinding);
         type.extnds = superClass;
       }
 
@@ -476,80 +449,122 @@
       for (int i = 0; i < superInterfaces.length; ++i) {
         ReferenceBinding superInterfaceBinding = superInterfaces[i];
         assert (superInterfaceBinding.isInterface());
-        JInterfaceType superInterface = (JInterfaceType) fTypeMap.get(superInterfaceBinding);
+        JInterfaceType superInterface = (JInterfaceType) typeMap.get(superInterfaceBinding);
         type.implments.add(superInterface);
       }
-      fTypeDecls.add(typeDeclaration);
+      typeDecls.add(typeDeclaration);
       return true;
     }
+  }
 
-    private JField createField(FieldBinding binding,
-        JReferenceType enclosingType, boolean hasInitializer) {
-      JType type = (JType) fTypeMap.get(binding.type);
-      JField field = fProgram.createField(binding.name, enclosingType, type,
-        binding.isStatic(), binding.isFinal(), hasInitializer);
-      fTypeMap.put(binding, field);
-      return field;
+  /**
+   * Creates JNodes for every type and memorizes the mapping from the JDT
+   * Binding to the corresponding JNode for each created type. Note that since
+   * there could be forward references, it is not possible to set up supertypes;
+   * it must be done is a subsequent pass.
+   */
+  private static class BuildTypeMapVisitor extends ASTVisitor {
+
+    private final TypeMap typeMap;
+    private final JProgram program;
+
+    public BuildTypeMapVisitor(TypeMap typeMap) {
+      this.typeMap = typeMap;
+      program = this.typeMap.getProgram();
     }
 
-    private JField createField(SyntheticArgumentBinding binding,
-        JReferenceType enclosingType) {
-      JType type = (JType) fTypeMap.get(binding.type);
-      JField field = fProgram.createField(binding.name, enclosingType, type,
-        false, true, true);
-      if (binding.matchingField != null) {
-        fTypeMap.put(binding.matchingField, field);
-      }
-      fTypeMap.put(binding, field);
-      return field;
-    }
-
-    private JParameter createParameter(LocalVariableBinding binding,
-        JMethod enclosingMethod) {
-      JType type = (JType) fTypeMap.get(binding.type);
-      JParameter param = fProgram.createParameter(binding.name, type,
-        binding.isFinal(), enclosingMethod);
-      fTypeMap.put(binding, param);
-      return param;
-    }
-
-    private JParameter createParameter(SyntheticArgumentBinding arg,
-        String argName, JMethod enclosingMethod) {
-      JType type = (JType) fTypeMap.get(arg.type);
-      JParameter param = fProgram.createParameter(argName.toCharArray(), type,
-        true, enclosingMethod);
-      return param;
-    }
-
-    private JMethod findEnclosingMethod(BlockScope scope) {
-      MethodScope methodScope = scope.methodScope();
-      if (methodScope.isInsideInitializer()) {
-        JReferenceType enclosingType = (JReferenceType) fTypeMap.get(scope.classScope().referenceContext.binding);
-        if (methodScope.isStatic) {
-          // clinit
-          return (JMethod) enclosingType.methods.get(0);
-        } else {
-          // init
-          assert (enclosingType instanceof JClassType);
-          return (JMethod) enclosingType.methods.get(1);
-        }
-      }
-
-      AbstractMethodDeclaration referenceMethod = methodScope.referenceMethod();
-      return (JMethod) fTypeMap.get(referenceMethod.binding);
+    public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
+      assert (localTypeDeclaration.kind() != IGenericType.INTERFACE_DECL);
+      return process(localTypeDeclaration);
     }
 
     public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
       return process(memberTypeDeclaration);
     }
 
-    public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
-      return process(localTypeDeclaration);
-    }
-
     public boolean visit(TypeDeclaration typeDeclaration,
         CompilationUnitScope scope) {
       return process(typeDeclaration);
     }
+
+    private boolean process(TypeDeclaration typeDeclaration) {
+      char[][] name = typeDeclaration.binding.compoundName;
+      SourceTypeBinding binding = typeDeclaration.binding;
+      if (binding instanceof LocalTypeBinding) {
+        char[] localName = binding.constantPoolName();
+        if (localName == null) {
+          /*
+           * Weird case: if JDT determines that this local class is totally
+           * uninstantiable, it won't bother allocating a local name.
+           */
+          return false;
+        }
+
+        for (int i = 0, c = localName.length; i < c; ++i) {
+          if (localName[i] == '/') {
+            localName[i] = '.';
+          }
+        }
+        name = new char[1][0];
+        name[0] = localName;
+      }
+
+      JReferenceType newType;
+      if (binding.isClass()) {
+        newType = program.createClass(name, binding.isAbstract(),
+            binding.isFinal());
+      } else if (binding.isInterface()) {
+        newType = program.createInterface(name);
+      } else {
+        assert (false);
+        return false;
+      }
+
+      /**
+       * We emulate static initializers and intance initializers as methods. As
+       * in other cases, this gives us: simpler AST, easier to optimize, more
+       * 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);
+      clinit.freezeParamTypes();
+
+      if (newType instanceof JClassType) {
+        JMethod init = program.createMethod("$init".toCharArray(), newType,
+            program.getTypeVoid(), false, false, true, true, false);
+        init.freezeParamTypes();
+      }
+
+      typeMap.put(binding, newType);
+      return true;
+    }
+  }
+
+  public static TypeDeclaration[] exec(TypeMap typeMap,
+      CompilationUnitDeclaration[] unitDecls, JsProgram jsProgram) {
+    createPeersForTypes(unitDecls, typeMap);
+    return createPeersForNonTypeDecls(unitDecls, typeMap, jsProgram);
+  }
+
+  private static TypeDeclaration[] createPeersForNonTypeDecls(
+      CompilationUnitDeclaration[] unitDecls, TypeMap typeMap,
+      JsProgram jsProgram) {
+    // Traverse again to create our JNode peers for each method, field,
+    // parameter, and local
+    BuildDeclMapVisitor v2 = new BuildDeclMapVisitor(typeMap, jsProgram);
+    for (int i = 0; i < unitDecls.length; ++i) {
+      unitDecls[i].traverse(v2, unitDecls[i].scope);
+    }
+    return v2.getTypeDeclarataions();
+  }
+
+  private static void createPeersForTypes(
+      CompilationUnitDeclaration[] unitDecls, TypeMap typeMap) {
+    // Traverse once to create our JNode peers for each type
+    BuildTypeMapVisitor v1 = new BuildTypeMapVisitor(typeMap);
+    for (int i = 0; i < unitDecls.length; ++i) {
+      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 1f4e4ad..2102401 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JArrayRef;
@@ -43,92 +57,18 @@
  */
 public class CastNormalizer {
 
-  private Map/* <JReferenceType, Integer> */queryIds = new IdentityHashMap();
-
-  /**
-   * 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 JMethod fStringValueOfChar = null;
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
-      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);
-      }
-    }
-
-    private void convertCharString(Mutator m) {
-      JPrimitiveType charType = program.getTypePrimitiveChar();
-      JExpression expr = m.get();
-      if (expr.getType() == charType) {
-        if (fStringValueOfChar == null) {
-          fStringValueOfChar = program.getSpecialMethod("Cast.charToString");
-          assert (fStringValueOfChar != null);
-        }
-        JMethodCall call = new JMethodCall(program, null, fStringValueOfChar);
-        ChangeList myChangeList = new ChangeList("Replace '" + expr
-          + "' with a call to Cast.charToString()");
-        myChangeList.addExpression(m, call.args);
-        myChangeList.replaceExpression(m, call);
-        changeList.add(myChangeList);
-      }
-    }
-  }
-
-  /**
-   * 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.");
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
-      JType type = x.getType();
-      if (x.op == 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);
-      }
-    }
-  }
-
   private class AssignTypeIdsVisitor extends JVisitor {
 
+    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> */();
+
     {
       JTypeOracle typeOracle = program.typeOracle;
       for (Iterator it = program.getAllArrayTypes().iterator(); it.hasNext();) {
@@ -139,6 +79,34 @@
       }
     }
 
+    public void computeTypeIds() {
+
+      // the 0th entry is the "always false" entry
+      classes.add(null);
+      jsonObjects.add(new JsonObject(program));
+
+      /*
+       * Compute the list of classes than can successfully satisfy cast
+       * requests, along with the set of types they can be successfully cast to.
+       * Do it in super type order.
+       */
+      for (Iterator it = program.getDeclaredTypes().iterator(); it.hasNext();) {
+        JReferenceType type = (JReferenceType) it.next();
+        if (type instanceof JClassType) {
+          computeSourceClass((JClassType) type);
+        }
+      }
+
+      for (Iterator it = program.getAllArrayTypes().iterator(); it.hasNext();) {
+        JArrayType type = (JArrayType) it.next();
+        computeSourceClass(type);
+      }
+
+      // pass our info to JProgram
+      program.initTypeInfo(classes, jsonObjects);
+      program.recordQueryIds(queryIds);
+    }
+
     /*
      * If this expression could possibly generate an ArrayStoreException, we
      * must record a query on the element type being assigned to.
@@ -198,66 +166,6 @@
       recordCast(x.testType, x.getExpression());
     }
 
-    private void recordCast(JType targetType, JExpression rhs) {
-      if (targetType instanceof JReferenceType) {
-        // unconditional cast b/c it would've been a semantic error earlier
-        JReferenceType rhsType = (JReferenceType) rhs.getType();
-        // don't record a type for trivial casts that won't generate code
-        if (rhsType instanceof JClassType) {
-          if (program.typeOracle.canTriviallyCast(rhsType,
-            (JReferenceType) targetType)) {
-            return;
-          }
-        }
-
-        recordCastInternal((JReferenceType) targetType, rhsType);
-      }
-    }
-
-    private void recordCastInternal(JReferenceType targetType,
-        JReferenceType rhsType) {
-      JReferenceType toType = targetType;
-      Set/* <JReferenceType> */querySet = (Set) queriedTypes.get(toType);
-      if (querySet == null) {
-        queryIds.put(toType, new Integer(nextQueryId++));
-        querySet = new HashSet/* <JReferenceType> */();
-        queriedTypes.put(toType, querySet);
-      }
-      querySet.add(rhsType);
-    }
-
-    Set/* <JClassType> */alreadyRan = new HashSet/* <JClassType> */();
-    private List/* <JClassType> */classes = new ArrayList/* <JClassType> */();
-    private List/* <JsonObject> */jsonObjects = new ArrayList/* <JsonObject> */();
-
-    public void computeTypeIds() {
-
-      // the 0th entry is the "always false" entry
-      classes.add(null);
-      jsonObjects.add(new JsonObject(program));
-
-      /*
-       * Compute the list of classes than can successfully satisfy cast
-       * requests, along with the set of types they can be successfully cast to.
-       * Do it in super type order.
-       */
-      for (Iterator it = program.getDeclaredTypes().iterator(); it.hasNext();) {
-        JReferenceType type = (JReferenceType) it.next();
-        if (type instanceof JClassType) {
-          computeSourceClass((JClassType) type);
-        }
-      }
-
-      for (Iterator it = program.getAllArrayTypes().iterator(); it.hasNext();) {
-        JArrayType type = (JArrayType) it.next();
-        computeSourceClass(type);
-      }
-
-      // pass our info to JProgram
-      program.initTypeInfo(classes, jsonObjects);
-      program.recordQueryIds(queryIds);
-    }
-
     /**
      * Create the data for JSON table to capture the mapping from a class to its
      * query types.
@@ -325,7 +233,7 @@
           JIntLiteral labelExpr = program.getLiteralInt(i);
           JIntLiteral valueExpr = program.getLiteralInt(1);
           jsonObject.propInits.add(new JsonPropInit(program, labelExpr,
-            valueExpr));
+              valueExpr));
         }
       }
 
@@ -333,16 +241,118 @@
       classes.add(type);
       jsonObjects.add(jsonObject);
     }
+
+    private void recordCast(JType targetType, JExpression rhs) {
+      if (targetType instanceof JReferenceType) {
+        // unconditional cast b/c it would've been a semantic error earlier
+        JReferenceType rhsType = (JReferenceType) rhs.getType();
+        // don't record a type for trivial casts that won't generate code
+        if (rhsType instanceof JClassType) {
+          if (program.typeOracle.canTriviallyCast(rhsType,
+              (JReferenceType) targetType)) {
+            return;
+          }
+        }
+
+        recordCastInternal((JReferenceType) targetType, rhsType);
+      }
+    }
+
+    private void recordCastInternal(JReferenceType targetType,
+        JReferenceType rhsType) {
+      JReferenceType toType = targetType;
+      Set/* <JReferenceType> */querySet = (Set) queriedTypes.get(toType);
+      if (querySet == null) {
+        queryIds.put(toType, new Integer(nextQueryId++));
+        querySet = new HashSet/* <JReferenceType> */();
+        queriedTypes.put(toType, querySet);
+      }
+      querySet.add(rhsType);
+    }
+  }
+
+  /**
+   * 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 JMethod stringValueOfChar = null;
+
+    // @Override
+    public void endVisit(JBinaryOperation x, Mutator m) {
+      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);
+      }
+    }
+
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+
+    private void convertCharString(Mutator m) {
+      JPrimitiveType charType = program.getTypePrimitiveChar();
+      JExpression expr = m.get();
+      if (expr.getType() == charType) {
+        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);
+      }
+    }
+  }
+
+  /**
+   * 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.");
+
+    // @Override
+    public void endVisit(JBinaryOperation x, Mutator m) {
+      JType type = x.getType();
+      if (x.op == 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);
+      }
+    }
+
+    public ChangeList getChangeList() {
+      return changeList;
+    }
   }
 
   private class ReplaceTypeChecksVisitor extends JVisitor {
 
     private final ChangeList changeList = new ChangeList(
-      "Replace all casts and instanceof operations.");
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
+        "Replace all casts and instanceof operations.");
 
     // @Override
     public void endVisit(JCastOperation x, Mutator m) {
@@ -369,7 +379,8 @@
           changeList.add(myChangeList);
         }
         if (argType instanceof JClassType
-          && program.typeOracle.canTriviallyCast((JClassType) argType, refType)) {
+            && program.typeOracle.canTriviallyCast((JClassType) argType,
+                refType)) {
           // just remove the cast
           changeList.replaceExpression(m, x.expr);
         } else {
@@ -411,12 +422,12 @@
           }
         } else if (tLong == fromType) {
           if (tByte == toType || tShort == toType || tChar == toType
-            || tInt == toType) {
+              || tInt == toType) {
             narrow = true;
           }
         } else if (tFloat == fromType || tDouble == fromType) {
           if (tByte == toType || tShort == toType || tChar == toType
-            || tInt == toType || tLong == toType) {
+              || tInt == toType || tLong == toType) {
             round = true;
           }
         }
@@ -424,9 +435,9 @@
         ChangeList myChangeList;
         if (narrow || round) {
           String methodName = "Cast." + (narrow ? "narrow_" : "round_")
-            + toType.getName();
+              + toType.getName();
           myChangeList = new ChangeList("Replace '" + x + "' with a call to "
-            + methodName);
+              + methodName);
           JMethod castMethod = program.getSpecialMethod(methodName);
           JMethodCall call = new JMethodCall(program, null, castMethod);
           myChangeList.addExpression(x.expr, call.args);
@@ -443,14 +454,15 @@
     public void endVisit(JInstanceOf x, Mutator m) {
       JType argType = x.getExpression().getType();
       if (argType instanceof JClassType
-        && program.typeOracle.canTriviallyCast((JClassType) argType, x.testType)) {
+          && program.typeOracle.canTriviallyCast((JClassType) argType,
+              x.testType)) {
         // trivially true if non-null
         JNullLiteral nullLit = program.getLiteralNull();
         JBinaryOperation eq = new JBinaryOperation(program,
-          program.getTypePrimitiveBoolean(), JBinaryOperator.NEQ, nullLit,
-          nullLit);
+            program.getTypePrimitiveBoolean(), JBinaryOperator.NEQ, nullLit,
+            nullLit);
         ChangeList myChangeList = new ChangeList("Replace '" + x
-          + "' with a simple null test.");
+            + "' with a simple null test.");
         myChangeList.replaceExpression(eq.lhs, x.expr);
         myChangeList.replaceExpression(m, eq);
         changeList.add(myChangeList);
@@ -461,12 +473,16 @@
       }
     }
 
+    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());
+          + " with a call to " + call.getTarget().getName());
       myChangeList.addExpression(arg, call.args);
       myChangeList.addExpression(qId, call.args);
       myChangeList.replaceExpression(dest, call);
@@ -474,6 +490,18 @@
     }
   }
 
+  public static void exec(JProgram program) {
+    new CastNormalizer(program).execImpl();
+  }
+
+  private Map/* <JReferenceType, Integer> */queryIds = new IdentityHashMap();
+
+  private final JProgram program;
+
+  private CastNormalizer(JProgram program) {
+    this.program = program;
+  }
+
   private void execImpl() {
     {
       ConcatVisitor visitor = new ConcatVisitor();
@@ -506,14 +534,4 @@
     }
   }
 
-  private final JProgram program;
-
-  private CastNormalizer(JProgram program) {
-    this.program = program;
-  }
-
-  public static void exec(JProgram program) {
-    new CastNormalizer(program).execImpl();
-  }
-
 }
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 90eaf97..36ae6e1 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JBinaryOperation;
@@ -17,7 +31,7 @@
 import com.google.gwt.dev.jjs.ast.change.ChangeList;
 
 /**
- * Optimizer that will remove all trivially computable casts and instanceof 
+ * Optimizer that will remove all trivially computable casts and instanceof
  * operations.
  */
 public class CastOptimizer {
@@ -25,17 +39,13 @@
   private class ReplaceTrivialCastsVisitor extends JVisitor {
 
     private final ChangeList changeList = new ChangeList(
-      "Replace all trivially computable casts and instanceof operations.");
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
+        "Replace all trivially computable casts and instanceof operations.");
 
     // @Override
     public void endVisit(JCastOperation x, Mutator m) {
       JType argType = x.getExpression().getType();
       if (!(x.castType instanceof JReferenceType)
-        || !(argType instanceof JReferenceType)) {
+          || !(argType instanceof JReferenceType)) {
         return;
       }
 
@@ -66,9 +76,9 @@
          * call completes normally it will return null.
          */
         JMethodCall call = new JMethodCall(program, null, method,
-          program.getTypeNull());
+            program.getTypeNull());
         ChangeList myChangeList = new ChangeList("Replace '" + x
-          + "' with a call to throwClassCastExceptionUnlessNull().");
+            + "' with a call to throwClassCastExceptionUnlessNull().");
         myChangeList.addExpression(x.expr, call.args);
         myChangeList.replaceExpression(m, call);
         changeList.add(myChangeList);
@@ -105,10 +115,10 @@
           // replace with a simple null test
           JNullLiteral nullLit = program.getLiteralNull();
           JBinaryOperation eq = new JBinaryOperation(program,
-            program.getTypePrimitiveBoolean(), JBinaryOperator.NEQ, nullLit,
-            nullLit);
+              program.getTypePrimitiveBoolean(), JBinaryOperator.NEQ, nullLit,
+              nullLit);
           ChangeList myChangeList = new ChangeList("Replace '" + x
-            + "' with a simple null test.");
+              + "' with a simple null test.");
           myChangeList.replaceExpression(eq.lhs, x.expr);
           myChangeList.replaceExpression(m, eq);
           changeList.add(myChangeList);
@@ -118,6 +128,20 @@
         changeList.replaceExpression(m, program.getLiteralBoolean(false));
       }
     }
+
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+  }
+
+  public static boolean exec(JProgram program) {
+    return new CastOptimizer(program).execImpl();
+  }
+
+  private final JProgram program;
+
+  private CastOptimizer(JProgram program) {
+    this.program = program;
   }
 
   private boolean execImpl() {
@@ -130,14 +154,4 @@
     changes.apply();
     return true;
   }
-
-  private final JProgram program;
-
-  private CastOptimizer(JProgram program) {
-    this.program = program;
-  }
-
-  public static boolean exec(JProgram program) {
-    return new CastOptimizer(program).execImpl();
-  }
 }
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 6f3ddc8..aadfdbd 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JBlock;
@@ -27,35 +41,15 @@
  */
 public class CatchBlockNormalizer {
 
-  private JMethod fCurrentMethod;
-  private final List/*<JLocal>*/ fTempLocals = new ArrayList/*<JLocal>*/();
-  private int fLocalIndex;
-
-  private void clearLocals() {
-    fTempLocals.clear();
-    fLocalIndex = 0;
-  }
-
-  private void pushTempLocal() {
-    if (fLocalIndex == fTempLocals.size()) {
-      JLocal newTemp = program.createLocal(("$e" + fLocalIndex).toCharArray(),
-        program.getTypeJavaLangObject(), false, fCurrentMethod);
-      fTempLocals.add(newTemp);
-    }
-    ++fLocalIndex;
-  }
-
-  private JLocal popTempLocal() {
-    return (JLocal) fTempLocals.get(--fLocalIndex);
-  }
-
   private class CollapseCatchBlocks extends JVisitor {
 
     private final ChangeList changeList = new ChangeList(
-      "Collapse all multi-catch blocks into a single catch block.");
+        "Collapse all multi-catch blocks into a single catch block.");
 
-    public ChangeList getChangeList() {
-      return changeList;
+    // @Override
+    public void endVisit(JMethod x) {
+      clearLocals();
+      currentMethod = null;
     }
 
     // @Override
@@ -65,7 +59,7 @@
       }
 
       ChangeList myChangeList = new ChangeList("Merge " + x.catchBlocks.size()
-        + " catch blocks.");
+          + " catch blocks.");
       JLocal exObj = popTempLocal();
       JLocalRef exRef = new JLocalRef(program, exObj);
       JBlock newCatchBlock = new JBlock(program);
@@ -104,6 +98,17 @@
       changeList.add(myChangeList);
     }
 
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+
+    // @Override
+    public boolean visit(JMethod x) {
+      currentMethod = x;
+      clearLocals();
+      return true;
+    }
+
     // @Override
     public boolean visit(JTryStatement x) {
       if (!x.catchBlocks.isEmpty()) {
@@ -111,19 +116,27 @@
       }
       return true;
     }
+  }
 
-    // @Override
-    public void endVisit(JMethod x) {
-      clearLocals();
-      fCurrentMethod = null;
-    }
+  public static void exec(JProgram program) {
+    new CatchBlockNormalizer(program).execImpl();
+  }
 
-    // @Override
-    public boolean visit(JMethod x) {
-      fCurrentMethod = x;
-      clearLocals();
-      return true;
-    }
+  private JMethod currentMethod;
+
+  private final List/* <JLocal> */tempLocals = new ArrayList/* <JLocal> */();
+
+  private int localIndex;
+
+  private final JProgram program;
+
+  private CatchBlockNormalizer(JProgram program) {
+    this.program = program;
+  }
+
+  private void clearLocals() {
+    tempLocals.clear();
+    localIndex = 0;
   }
 
   private void execImpl() {
@@ -137,14 +150,17 @@
     }
   }
 
-  private final JProgram program;
-
-  private CatchBlockNormalizer(JProgram program) {
-    this.program = program;
+  private JLocal popTempLocal() {
+    return (JLocal) tempLocals.get(--localIndex);
   }
 
-  public static void exec(JProgram program) {
-    new CatchBlockNormalizer(program).execImpl();
+  private void pushTempLocal() {
+    if (localIndex == tempLocals.size()) {
+      JLocal newTemp = program.createLocal(("$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 8bd50d1..c2fcc16 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.Holder;
@@ -28,39 +42,91 @@
  */
 public class CompoundAssignmentNormalizer {
 
-  private JMethod fCurrentMethod;
-  private final List/*<JLocal>*/ fTempLocals = new ArrayList/*<JLocal>*/();
-  private int fLocalIndex;
-
-  private void clearLocals() {
-    fTempLocals.clear();
-    fLocalIndex = 0;
-  }
-
-  private JLocal getTempLocal() {
-    if (fLocalIndex < fTempLocals.size()) {
-      return (JLocal) fTempLocals.get(fLocalIndex++);
-    }
-    JLocal newTemp = program.createLocal(("$t" + fLocalIndex++).toCharArray(),
-      program.getTypeVoid(), false, fCurrentMethod);
-    fTempLocals.add(newTemp);
-    return newTemp;
-  }
-
-  private class ReplaceSideEffectsInLvalue extends JVisitor {
+  private class BreakupAssignOpsVisitor extends JVisitor {
     private final ChangeList changeList = new ChangeList(
-      "Replace side effects in lvalue.");
+        "Break apart certain complex assignments.");
+
+    // @Override
+    public void endVisit(JBinaryOperation x, Mutator m) {
+      /*
+       * 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
+          && x.getType() != program.getTypePrimitiveFloat()
+          && x.getType() != program.getTypePrimitiveDouble()) {
+
+        /*
+         * 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.
+         */
+        final int pushUsedLocals = localIndex;
+        JMultiExpression multi = new JMultiExpression(program);
+        ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
+            multi);
+        x.lhs.traverse(replacer);
+        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);
+
+        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);
+        } else {
+          myChangeList.add(replacer.getChangeList());
+          myChangeList.addExpression(asg, multi.exprs);
+          myChangeList.replaceExpression(m, multi);
+        }
+
+        changeList.add(myChangeList);
+      }
+    }
+
+    // @Override
+    public void endVisit(JMethod x) {
+      clearLocals();
+      currentMethod = null;
+    }
 
     public ChangeList getChangeList() {
       return changeList;
     }
 
+    // @Override
+    public boolean visit(JMethod x) {
+      currentMethod = x;
+      clearLocals();
+      return true;
+    }
+  }
+  private class ReplaceSideEffectsInLvalue extends JVisitor {
+    private final ChangeList changeList = new ChangeList(
+        "Replace side effects in lvalue.");
+
     private final JMultiExpression multi;
 
     ReplaceSideEffectsInLvalue(JMultiExpression multi) {
       this.multi = multi;
     }
 
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+
     // @Override
     public boolean visit(JArrayRef x, Mutator m) {
       possiblyReplace(x.instance);
@@ -102,7 +168,7 @@
       // 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());
+          JBinaryOperator.ASG, tempRef, program.getLiteralNull());
       changeList.replaceExpression(asg.rhs, x);
       changeList.addExpression(asg, multi.exprs);
       // Update me with the temp
@@ -110,76 +176,25 @@
     }
   }
 
-  private class BreakupAssignOpsVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-      "Break apart certain complex assignments.");
+  public static void exec(JProgram program) {
+    new CompoundAssignmentNormalizer(program).execImpl();
+  }
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
+  private JMethod currentMethod;
 
-    // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
-      /*
-       * 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
-        && x.getType() != program.getTypePrimitiveFloat()
-        && x.getType() != program.getTypePrimitiveDouble()) {
+  private final List/* <JLocal> */tempLocals = new ArrayList/* <JLocal> */();
 
-        /*
-         * 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.
-         */
-        final int pushUsedLocals = fLocalIndex;
-        JMultiExpression multi = new JMultiExpression(program);
-        ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
-          multi);
-        x.lhs.traverse(replacer);
-        fLocalIndex = pushUsedLocals;
+  private int localIndex;
 
-        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);
+  private final JProgram program;
 
-        ChangeList myChangeList = new ChangeList("Break '" + x
-          + "' into two operations.");
+  private CompoundAssignmentNormalizer(JProgram program) {
+    this.program = program;
+  }
 
-        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);
-        } else {
-          myChangeList.add(replacer.getChangeList());
-          myChangeList.addExpression(asg, multi.exprs);
-          myChangeList.replaceExpression(m, multi);
-        }
-
-        changeList.add(myChangeList);
-      }
-    }
-
-    // @Override
-    public void endVisit(JMethod x) {
-      clearLocals();
-      fCurrentMethod = null;
-    }
-
-    // @Override
-    public boolean visit(JMethod x) {
-      fCurrentMethod = x;
-      clearLocals();
-      return true;
-    }
+  private void clearLocals() {
+    tempLocals.clear();
+    localIndex = 0;
   }
 
   private void execImpl() {
@@ -191,14 +206,14 @@
     }
   }
 
-  private final JProgram program;
-
-  private CompoundAssignmentNormalizer(JProgram program) {
-    this.program = program;
-  }
-
-  public static void exec(JProgram program) {
-    new CompoundAssignmentNormalizer(program).execImpl();
+  private JLocal getTempLocal() {
+    if (localIndex < tempLocals.size()) {
+      return (JLocal) tempLocals.get(localIndex++);
+    }
+    JLocal newTemp = program.createLocal(("$t" + localIndex++).toCharArray(),
+        program.getTypeVoid(), false, currentMethod);
+    tempLocals.add(newTemp);
+    return newTemp;
   }
 
 }
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 c94b399..b12495a 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.CanBeStatic;
@@ -167,7 +181,7 @@
 public class GenerateJavaAST {
 
   /**
-   * Comparator for <code>HasName</code> instances. 
+   * Comparator for <code>HasName</code> instances.
    */
   public static class HasNameSort implements Comparator/* <HasName> */ {
     public int compare(Object o1, Object o2) {
@@ -204,9 +218,27 @@
       return sb.toString();
     }
 
+    private Object[] args = new Object[1];
+
+    private JReferenceType currentClass;
+
+    private ClassScope currentClassScope;
+
+    private JMethod currentMethod;
+
+    private MethodScope currentMethodScope;
+
+    private final Map/* <JMethod, Map<String, JLabel>> */labelMap = new IdentityHashMap();
+
+    private Class[] params = new Class[1];
+
+    private final TypeMap typeMap;
+
+    private final JProgram program;
+
     public JavaASTGenerationVisitor(TypeMap typeMap) {
-      fTypeMap = typeMap;
-      program = fTypeMap.getProgram();
+      this.typeMap = typeMap;
+      program = this.typeMap.getProgram();
     }
 
     /**
@@ -215,8 +247,8 @@
      * output JavaScript.
      */
     public void processType(TypeDeclaration x) {
-      fCurrentClass = (JReferenceType) fTypeMap.get(x.binding);
-      fCurrentClassScope = x.scope;
+      currentClass = (JReferenceType) typeMap.get(x.binding);
+      currentClassScope = x.scope;
 
       if (x.fields != null) {
         // Process fields
@@ -224,16 +256,16 @@
           FieldDeclaration fieldDeclaration = x.fields[i];
           if (fieldDeclaration.isStatic()) {
             // clinit
-            fCurrentMethod = (JMethod) fCurrentClass.methods.get(0);
-            fCurrentMethodScope = x.staticInitializerScope;
+            currentMethod = (JMethod) currentClass.methods.get(0);
+            currentMethodScope = x.staticInitializerScope;
           } else {
             // init
-            fCurrentMethod = (JMethod) fCurrentClass.methods.get(1);
-            fCurrentMethodScope = x.initializerScope;
+            currentMethod = (JMethod) currentClass.methods.get(1);
+            currentMethodScope = x.initializerScope;
           }
 
           if (fieldDeclaration instanceof Initializer) {
-            assert (fCurrentClass instanceof JClassType);
+            assert (currentClass instanceof JClassType);
             processInitializer((Initializer) fieldDeclaration);
           } else {
             processField(fieldDeclaration);
@@ -241,14 +273,14 @@
         }
       }
 
-      fCurrentMethodScope = null;
-      fCurrentMethod = null;
+      currentMethodScope = null;
+      currentMethod = null;
 
       if (x.methods != null) {
         // Process methods
         for (int i = 0, n = x.methods.length; i < n; ++i) {
           if (x.methods[i].isConstructor()) {
-            assert (fCurrentClass instanceof JClassType);
+            assert (currentClass instanceof JClassType);
             processConstructor((ConstructorDeclaration) x.methods[i]);
           } else if (x.methods[i].isClinit()) {
             // nothing to do
@@ -258,8 +290,8 @@
         }
       }
 
-      fCurrentClassScope = null;
-      fCurrentClass = null;
+      currentClassScope = null;
+      currentClass = null;
     }
 
     /**
@@ -270,16 +302,16 @@
       if (child == null) {
         return null;
       }
-      
+
       try {
-        fParams[0] = child.getClass();
-        Method method = getClass().getDeclaredMethod(name, fParams);
-        fArgs[0] = child;
-        return (JNode) method.invoke(this, fArgs);
+        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 = fParams[0].getName();
+        String s = params[0].getName();
         throw new InternalCompilerException("Expecting: private void " + name
             + "(" + s.substring(s.lastIndexOf('.') + 1) + " x) { }", e);
       } catch (IllegalArgumentException e) {
@@ -389,10 +421,10 @@
      * initializer emulation method - run user code - return this
      */
     void processConstructor(ConstructorDeclaration x) {
-      JMethod ctor = (JMethod) fTypeMap.get(x.binding);
+      JMethod ctor = (JMethod) typeMap.get(x.binding);
 
-      fCurrentMethod = ctor;
-      fCurrentMethodScope = x.scope;
+      currentMethod = ctor;
+      currentMethodScope = x.scope;
 
       JMethodCall call = null;
       ExplicitConstructorCall ctorCall = x.constructorCall;
@@ -429,18 +461,18 @@
               SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
               JParameter param = (JParameter) paramIt.next();
               if (arg.matchingField != null) {
-                JField field = (JField) fTypeMap.get(arg);
+                JField field = (JField) typeMap.get(arg);
                 ctor.body.statements.add(program.createAssignmentStmt(
                     createVariableRef(field), createVariableRef(param)));
               }
             }
           }
-          
+
           if (nestedBinding.outerLocalVariables != null) {
             for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
               SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
               JParameter param = (JParameter) paramIt.next();
-              JField field = (JField) fTypeMap.get(arg);
+              JField field = (JField) typeMap.get(arg);
               ctor.body.statements.add(program.createAssignmentStmt(
                   createVariableRef(field), createVariableRef(param)));
             }
@@ -477,8 +509,8 @@
         }
       }
 
-      fCurrentMethodScope = null;
-      fCurrentMethod = null;
+      currentMethodScope = null;
+      currentMethod = null;
 
       // synthesize a return statement to emulate returning the new object
       ctor.body.statements.add(new JReturnStatement(program, thisRef));
@@ -493,9 +525,9 @@
          */
         return program.getLiteralNull();
       }
-      JClassType newType = (JClassType) fTypeMap.get(typeBinding);
+      JClassType newType = (JClassType) typeMap.get(typeBinding);
       MethodBinding b = x.binding;
-      JMethod ctor = (JMethod) fTypeMap.get(b);
+      JMethod ctor = (JMethod) typeMap.get(b);
       JMethodCall call;
       JClassType javaLangString = program.getTypeJavaLangString();
       if (newType == javaLangString) {
@@ -540,7 +572,7 @@
         if (nestedBinding.enclosingInstances != null) {
           for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
-            JClassType syntheticThisType = (JClassType) fTypeMap.get(arg.type);
+            JClassType syntheticThisType = (JClassType) typeMap.get(arg.type);
             call.args.add(createThisRef(syntheticThisType));
           }
         }
@@ -548,7 +580,7 @@
         if (nestedBinding.outerLocalVariables != null) {
           for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
-            JVariable variable = (JVariable) fTypeMap.get(arg.actualOuterLocalVariable);
+            JVariable variable = (JVariable) typeMap.get(arg.actualOuterLocalVariable);
             call.args.add(createVariableRef(variable,
                 arg.actualOuterLocalVariable));
           }
@@ -566,12 +598,12 @@
     }
 
     JExpression processExpression(AND_AND_Expression x) {
-      JType type = (JType) fTypeMap.get(x.resolvedType);
+      JType type = (JType) typeMap.get(x.resolvedType);
       return processBinaryOperation(JBinaryOperator.AND, type, x.left, x.right);
     }
 
     JExpression processExpression(ArrayAllocationExpression x) {
-      JArrayType type = (JArrayType) fTypeMap.get(x.resolvedType);
+      JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
       JNewArray newArray = new JNewArray(program, type);
 
       if (x.initializer != null) {
@@ -599,7 +631,7 @@
     }
 
     JExpression processExpression(ArrayInitializer x) {
-      JArrayType type = (JArrayType) fTypeMap.get(x.resolvedType);
+      JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
       JNewArray newArray = new JNewArray(program, type);
 
       newArray.initializers = new HolderList();
@@ -620,7 +652,7 @@
     }
 
     JExpression processExpression(Assignment x) {
-      JType type = (JType) fTypeMap.get(x.resolvedType);
+      JType type = (JType) typeMap.get(x.resolvedType);
       return processBinaryOperation(JBinaryOperator.ASG, type, x.lhs,
           x.expression);
     }
@@ -680,19 +712,19 @@
               "Unexpected operator for BinaryExpression");
       }
 
-      JType type = (JType) fTypeMap.get(x.resolvedType);
+      JType type = (JType) typeMap.get(x.resolvedType);
       return processBinaryOperation(op, type, x.left, x.right);
     }
 
     JExpression processExpression(CastExpression x) {
-      JType type = (JType) fTypeMap.get(x.resolvedType);
+      JType type = (JType) typeMap.get(x.resolvedType);
       JCastOperation cast = new JCastOperation(program, type,
           dispProcessExpression(x.expression));
       return cast;
     }
 
     JExpression processExpression(ClassLiteralAccess x) {
-      JType type = (JType) fTypeMap.get(x.targetType);
+      JType type = (JType) typeMap.get(x.targetType);
       return program.getLiteralClass(type);
     }
 
@@ -738,12 +770,12 @@
               "Unexpected operator for CompoundAssignment");
       }
 
-      JType type = (JType) fTypeMap.get(x.resolvedType);
+      JType type = (JType) typeMap.get(x.resolvedType);
       return processBinaryOperation(op, type, x.lhs, x.expression);
     }
 
     JExpression processExpression(ConditionalExpression x) {
-      JType type = (JType) fTypeMap.get(x.resolvedType);
+      JType type = (JType) typeMap.get(x.resolvedType);
       JExpression ifTest = dispProcessExpression(x.condition);
       JExpression thenExpr = dispProcessExpression(x.valueIfTrue);
       JExpression elseExpr = dispProcessExpression(x.valueIfFalse);
@@ -766,7 +798,7 @@
               "Unexpected operator for EqualExpression");
       }
 
-      JType type = (JType) fTypeMap.get(x.resolvedType);
+      JType type = (JType) typeMap.get(x.resolvedType);
       return processBinaryOperation(op, type, x.left, x.right);
     }
 
@@ -792,23 +824,23 @@
           throw new InternalCompilerException("Error matching fieldBinding.");
         }
       } else {
-        field = (JField) fTypeMap.get(fieldBinding);
+        field = (JField) typeMap.get(fieldBinding);
       }
       JExpression instance = dispProcessExpression(x.receiver);
       JExpression fieldRef = new JFieldRef(program, instance, field,
-          fCurrentClass);
+          currentClass);
       return fieldRef;
     }
 
     JExpression processExpression(InstanceOfExpression x) {
       JExpression expr = dispProcessExpression(x.expression);
-      JReferenceType testType = (JReferenceType) fTypeMap.get(x.type.resolvedType);
+      JReferenceType testType = (JReferenceType) typeMap.get(x.type.resolvedType);
       return new JInstanceOf(program, testType, expr);
     }
 
     JExpression processExpression(MessageSend x) {
-      JType type = (JType) fTypeMap.get(x.resolvedType);
-      JMethod method = (JMethod) fTypeMap.get(x.binding);
+      JType type = (JType) typeMap.get(x.resolvedType);
+      JMethod method = (JMethod) typeMap.get(x.binding);
       assert (type == method.getType());
 
       JExpression qualifier = dispProcessExpression(x.receiver);
@@ -848,7 +880,7 @@
     }
 
     JExpression processExpression(OR_OR_Expression x) {
-      JType type = (JType) fTypeMap.get(x.resolvedType);
+      JType type = (JType) typeMap.get(x.resolvedType);
       return processBinaryOperation(JBinaryOperator.OR, type, x.left, x.right);
     }
 
@@ -907,9 +939,9 @@
       if (x.enclosingInstance() == null) {
         return processExpression((AllocationExpression) x);
       }
-      
+
       MethodBinding b = x.binding;
-      JMethod ctor = (JMethod) fTypeMap.get(b);
+      JMethod ctor = (JMethod) typeMap.get(b);
       JClassType enclosingType = (JClassType) ctor.getEnclosingType();
       JNewInstance newInstance = new JNewInstance(program, enclosingType);
       JMethodCall call = new JMethodCall(program, newInstance, ctor);
@@ -923,7 +955,7 @@
         if (nestedBinding.enclosingInstances != null) {
           for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
-            JClassType syntheticThisType = (JClassType) fTypeMap.get(arg.type);
+            JClassType syntheticThisType = (JClassType) typeMap.get(arg.type);
             call.args.add(createThisRef(syntheticThisType, qualifier));
           }
         }
@@ -931,7 +963,7 @@
         if (nestedBinding.outerLocalVariables != null) {
           for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
-            JVariable variable = (JVariable) fTypeMap.get(arg.actualOuterLocalVariable);
+            JVariable variable = (JVariable) typeMap.get(arg.actualOuterLocalVariable);
             call.args.add(createVariableRef(variable,
                 arg.actualOuterLocalVariable));
           }
@@ -950,7 +982,7 @@
 
     JExpression processExpression(QualifiedNameReference x) {
       Binding binding = x.binding;
-      JNode node = fTypeMap.get(binding);
+      JNode node = typeMap.get(binding);
       if (!(node instanceof JVariable)) {
         return null;
       }
@@ -975,9 +1007,9 @@
                   "Error matching fieldBinding.");
             }
           } else {
-            field = (JField) fTypeMap.get(fieldBinding);
+            field = (JField) typeMap.get(fieldBinding);
           }
-          curRef = new JFieldRef(program, curRef, field, fCurrentClass);
+          curRef = new JFieldRef(program, curRef, field, currentClass);
         }
       }
 
@@ -985,7 +1017,7 @@
     }
 
     JExpression processExpression(QualifiedSuperReference x) {
-      JClassType type = (JClassType) fTypeMap.get(x.resolvedType);
+      JClassType type = (JClassType) typeMap.get(x.resolvedType);
       /*
        * 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
@@ -996,7 +1028,7 @@
        * the innermost type (even if the innermost type could be cast to a
        * compatible type), so we must create a reference to some outer type.
        */
-      if (type == fCurrentClass) {
+      if (type == currentClass) {
         return createSuperRef(type);
       } else {
         return createQualifiedSuperRef(type);
@@ -1004,7 +1036,7 @@
     }
 
     JExpression processExpression(QualifiedThisReference x) {
-      JClassType type = (JClassType) fTypeMap.get(x.resolvedType);
+      JClassType type = (JClassType) typeMap.get(x.resolvedType);
       /*
        * 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
@@ -1015,7 +1047,7 @@
        * innermost type (even if the innermost type could be cast to a
        * compatible type), so we must create a reference to some outer type.
        */
-      if (type == fCurrentClass) {
+      if (type == currentClass) {
         return createThisRef(type);
       } else {
         return createQualifiedThisRef(type);
@@ -1024,7 +1056,7 @@
 
     JExpression processExpression(SingleNameReference x) {
       Binding binding = x.binding;
-      Object target = fTypeMap.get(binding);
+      Object target = typeMap.get(binding);
       if (!(target instanceof JVariable)) {
         return null;
       }
@@ -1040,7 +1072,7 @@
         JField field = (JField) variable;
         if (!field.isStatic()) {
           JExpression instance = createThisRef(field.getEnclosingType());
-          return new JFieldRef(program, instance, field, fCurrentClass);
+          return new JFieldRef(program, instance, field, currentClass);
         }
       }
 
@@ -1048,15 +1080,15 @@
     }
 
     JExpression processExpression(SuperReference x) {
-      JClassType type = (JClassType) fTypeMap.get(x.resolvedType);
-      assert (type == fCurrentClass.extnds);
+      JClassType type = (JClassType) typeMap.get(x.resolvedType);
+      assert (type == currentClass.extnds);
       JExpression superRef = createSuperRef(type);
       return superRef;
     }
 
     JExpression processExpression(ThisReference x) {
-      JClassType type = (JClassType) fTypeMap.get(x.resolvedType);
-      assert (type == fCurrentClass);
+      JClassType type = (JClassType) typeMap.get(x.resolvedType);
+      assert (type == currentClass);
       JExpression thisRef = createThisRef(type);
       return thisRef;
     }
@@ -1093,7 +1125,7 @@
     }
 
     void processField(FieldDeclaration declaration) {
-      JField field = (JField) fTypeMap.tryGet(declaration.binding);
+      JField field = (JField) typeMap.tryGet(declaration.binding);
       if (field == null) {
         /*
          * When anonymous classes declare constant fields, the field declaration
@@ -1114,19 +1146,24 @@
             createVariableRef(field), initializer);
 
         // will either be init or clinit
-        fCurrentMethod.body.statements.add(assignStmt);
+        currentMethod.body.statements.add(assignStmt);
       }
     }
 
     void processInitializer(Initializer initializer) {
       JBlock block = processStatement(initializer.block);
       // will either be init or clinit
-      fCurrentMethod.body.statements.add(block);
+      currentMethod.body.statements.add(block);
     }
 
+    // // 5.0
+    // JStatement processStatement(ForeachStatement x) {
+    // return null;
+    // }
+
     void processMethod(AbstractMethodDeclaration x) {
       MethodBinding b = x.binding;
-      JMethod method = (JMethod) fTypeMap.get(b);
+      JMethod method = (JMethod) typeMap.get(b);
 
       if (b.isImplementing() || b.isOverriding()) {
         tryFindUpRefs(method, b);
@@ -1137,8 +1174,8 @@
         return;
       }
 
-      fCurrentMethod = method;
-      fCurrentMethodScope = x.scope;
+      currentMethod = method;
+      currentMethodScope = x.scope;
 
       if (x.statements != null) {
         for (int i = 0, n = x.statements.length; i < n; ++i) {
@@ -1149,8 +1186,8 @@
           }
         }
       }
-      fCurrentMethodScope = null;
-      fCurrentMethod = null;
+      currentMethodScope = null;
+      currentMethod = null;
     }
 
     void processNativeMethod(AbstractMethodDeclaration x,
@@ -1160,7 +1197,7 @@
       if (func == null) {
         return;
       }
-      
+
       // resolve jsni refs
       final List/* <JsNameRef> */nameRefs = new ArrayList/* <JsNameRef> */();
       func.traverse(new JsAbstractVisitorWithAllVisits() {
@@ -1204,8 +1241,7 @@
            * from JSNI with the literal value of the field.
            */
           JField field = (JField) node;
-          JsniFieldRef fieldRef = new JsniFieldRef(program, field,
-              fCurrentClass);
+          JsniFieldRef fieldRef = new JsniFieldRef(program, field, currentClass);
           nativeMethod.jsniFieldRefs.add(fieldRef);
         } else {
           JMethod method = (JMethod) node;
@@ -1239,7 +1275,7 @@
     }
 
     JStatement processStatement(BreakStatement x) {
-      return new JBreakStatement(program, getOrCreateLabel(fCurrentMethod,
+      return new JBreakStatement(program, getOrCreateLabel(currentMethod,
           x.label));
     }
 
@@ -1249,7 +1285,7 @@
     }
 
     JStatement processStatement(ContinueStatement x) {
-      return new JContinueStatement(program, getOrCreateLabel(fCurrentMethod,
+      return new JContinueStatement(program, getOrCreateLabel(currentMethod,
           x.label));
     }
 
@@ -1272,11 +1308,6 @@
       return null;
     }
 
-    // // 5.0
-    // JStatement processStatement(ForeachStatement x) {
-    // return null;
-    // }
-
     JStatement processStatement(ForStatement x) {
       // If false, just return the initializers
       // for (init; false; inc) { x } => { init }
@@ -1325,25 +1356,25 @@
       if (body == null) {
         return null;
       }
-      return new JLabeledStatement(program, getOrCreateLabel(fCurrentMethod,
+      return new JLabeledStatement(program, getOrCreateLabel(currentMethod,
           x.label), body);
     }
 
     JStatement processStatement(LocalDeclaration x) {
-      JLocal local = (JLocal) fTypeMap.get(x.binding);
+      JLocal local = (JLocal) typeMap.get(x.binding);
       JLocalRef localRef = new JLocalRef(program, local);
       JExpression initializer = dispProcessExpression(x.initialization);
       return new JLocalDeclarationStatement(program, localRef, initializer);
     }
 
     JStatement processStatement(ReturnStatement x) {
-      if (fCurrentMethodScope.referenceContext instanceof ConstructorDeclaration) {
+      if (currentMethodScope.referenceContext instanceof ConstructorDeclaration) {
         /*
          * Special: constructors are implemented as instance methods that return
          * their this object, so any embedded return statements have to be fixed
          * up.
          */
-        JClassType enclosingType = (JClassType) fCurrentMethod.getEnclosingType();
+        JClassType enclosingType = (JClassType) currentMethod.getEnclosingType();
         assert (x.expression == null);
         return new JReturnStatement(program, createThisRef(enclosingType));
       } else {
@@ -1377,7 +1408,7 @@
       List/* <JBlock> */catchBlocks = new ArrayList/* <JBlock> */();
       if (x.catchBlocks != null) {
         for (int i = 0, c = x.catchArguments.length; i < c; ++i) {
-          JLocal local = (JLocal) fTypeMap.get(x.catchArguments[i].binding);
+          JLocal local = (JLocal) typeMap.get(x.catchArguments[i].binding);
           catchArgs.add(createVariableRef(local));
         }
         for (int i = 0, c = x.catchBlocks.length; i < c; ++i) {
@@ -1423,14 +1454,14 @@
     }
 
     JMethodCall processSuperConstructorCall(ExplicitConstructorCall x) {
-      JMethod ctor = (JMethod) fTypeMap.get(x.binding);
-      JExpression trueQualifier = createThisRef(fCurrentClass);
+      JMethod ctor = (JMethod) typeMap.get(x.binding);
+      JExpression trueQualifier = createThisRef(currentClass);
       JMethodCall call = new JMethodCall(program, trueQualifier, ctor);
 
       // We have to find and pass through any synthetics our supertype needs
       ReferenceBinding superClass = x.binding.declaringClass;
       if (superClass instanceof NestedTypeBinding && !superClass.isStatic()) {
-        NestedTypeBinding myBinding = (NestedTypeBinding) fCurrentClassScope.referenceType().binding;
+        NestedTypeBinding myBinding = (NestedTypeBinding) currentClassScope.referenceType().binding;
         NestedTypeBinding superBinding = (NestedTypeBinding) superClass;
 
         // enclosing types
@@ -1438,7 +1469,7 @@
           JExpression qualifier = dispProcessExpression(x.qualification);
           for (int j = 0; j < superBinding.enclosingInstances.length; ++j) {
             SyntheticArgumentBinding arg = superBinding.enclosingInstances[j];
-            JClassType classType = (JClassType) fTypeMap.get(arg.type);
+            JClassType classType = (JClassType) typeMap.get(arg.type);
             if (qualifier == null) {
               /*
                * Got to be one of my params; it would be illegal to use a this
@@ -1449,7 +1480,7 @@
                * check each one to see if any will make a suitable this ref.
                */
               List/* <JExpression> */workList = new ArrayList/* <JExpression> */();
-              Iterator/* <JParameter> */paramIt = fCurrentMethod.params.iterator();
+              Iterator/* <JParameter> */paramIt = currentMethod.params.iterator();
               for (int i = 0; i < myBinding.enclosingInstances.length; ++i) {
                 workList.add(createVariableRef((JParameter) paramIt.next()));
               }
@@ -1465,11 +1496,11 @@
           for (int j = 0; j < superBinding.outerLocalVariables.length; ++j) {
             SyntheticArgumentBinding arg = superBinding.outerLocalVariables[j];
             // Got to be one of my params
-            JType varType = (JType) fTypeMap.get(arg.type);
+            JType varType = (JType) typeMap.get(arg.type);
             String varName = String.valueOf(arg.name);
             JParameter param = null;
-            for (int i = 0; i < fCurrentMethod.params.size(); ++i) {
-              JParameter paramIt = (JParameter) fCurrentMethod.params.get(i);
+            for (int i = 0; i < currentMethod.params.size(); ++i) {
+              JParameter paramIt = (JParameter) currentMethod.params.get(i);
               if (varType == paramIt.getType()
                   && varName.equals(paramIt.getName())) {
                 param = paramIt;
@@ -1494,14 +1525,14 @@
     }
 
     JMethodCall processThisConstructorCall(ExplicitConstructorCall x) {
-      JMethod ctor = (JMethod) fTypeMap.get(x.binding);
-      JExpression trueQualifier = createThisRef(fCurrentClass);
+      JMethod ctor = (JMethod) typeMap.get(x.binding);
+      JExpression trueQualifier = createThisRef(currentClass);
       JMethodCall call = new JMethodCall(program, trueQualifier, ctor);
 
       // All synthetics must be passed through to the target ctor
       ReferenceBinding declaringClass = x.binding.declaringClass;
       if (declaringClass instanceof NestedTypeBinding) {
-        Iterator/* <JParameter> */paramIt = fCurrentMethod.params.iterator();
+        Iterator/* <JParameter> */paramIt = currentMethod.params.iterator();
         NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
         if (nestedBinding.enclosingInstances != null) {
           for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
@@ -1530,7 +1561,7 @@
       if (classType.fields.size() > 0) {
         JField field = (JField) classType.fields.get(0);
         if (field.getName().startsWith("this$")) {
-          list.add(new JFieldRef(program, expr, field, fCurrentClass));
+          list.add(new JFieldRef(program, expr, field, currentClass));
         }
       }
     }
@@ -1564,10 +1595,10 @@
      * creating a naked JThisRef or you won't get the synthetic accesses right.
      */
     private JExpression createQualifiedSuperRef(JClassType targetType) {
-      assert (fCurrentClass instanceof JClassType);
-      JExpression expr = program.getExpressionThisRef((JClassType) fCurrentClass);
+      assert (currentClass instanceof JClassType);
+      JExpression expr = program.getExpressionThisRef((JClassType) currentClass);
       List/* <JExpression> */list = new ArrayList();
-      addAllOuterThisRefsPlusSuperChain(list, expr, (JClassType) fCurrentClass);
+      addAllOuterThisRefsPlusSuperChain(list, expr, (JClassType) currentClass);
       return createSuperRef(targetType, list);
     }
 
@@ -1577,10 +1608,10 @@
      * creating a naked JThisRef or you won't get the synthetic accesses right.
      */
     private JExpression createQualifiedThisRef(JClassType targetType) {
-      assert (fCurrentClass instanceof JClassType);
-      JExpression expr = program.getExpressionThisRef((JClassType) fCurrentClass);
+      assert (currentClass instanceof JClassType);
+      JExpression expr = program.getExpressionThisRef((JClassType) currentClass);
       List/* <JExpression> */list = new ArrayList();
-      addAllOuterThisRefsPlusSuperChain(list, expr, (JClassType) fCurrentClass);
+      addAllOuterThisRefsPlusSuperChain(list, expr, (JClassType) currentClass);
       return createThisRef(targetType, list);
     }
 
@@ -1590,8 +1621,8 @@
      * creating a naked JThisRef or you won't get the synthetic accesses right.
      */
     private JExpression createSuperRef(JClassType targetType) {
-      assert (fCurrentClass instanceof JClassType);
-      JExpression expr = program.getExpressionThisRef((JClassType) fCurrentClass);
+      assert (currentClass instanceof JClassType);
+      JExpression expr = program.getExpressionThisRef((JClassType) currentClass);
       List/* <JExpression> */list = new ArrayList();
       list.add(expr);
       return createSuperRef(targetType, list);
@@ -1604,7 +1635,7 @@
      */
     private JExpression createSuperRef(JClassType targetType,
         List/* <JExpression> */list) {
-      assert (fCurrentClass instanceof JClassType);
+      assert (currentClass instanceof JClassType);
       LinkedList/* <JExpression> */workList = new LinkedList/* <JExpression> */();
       workList.addAll(list);
 
@@ -1627,9 +1658,9 @@
      * JThisRef or you won't get the synthetic accesses right.
      */
     private JExpression createThisRef(JReferenceType targetType) {
-      assert (fCurrentClass instanceof JClassType);
+      assert (currentClass instanceof JClassType);
       return createThisRef(targetType,
-          program.getExpressionThisRef((JClassType) fCurrentClass));
+          program.getExpressionThisRef((JClassType) currentClass));
     }
 
     /**
@@ -1692,14 +1723,14 @@
     private JVariableRef createVariableRef(JVariable variable) {
       if (variable instanceof JLocal) {
         JLocal local = (JLocal) variable;
-        if (local.getEnclosingMethod() != fCurrentMethod) {
+        if (local.getEnclosingMethod() != currentMethod) {
           throw new InternalCompilerException(
               "LocalRef referencing local in a different method.");
         }
         return new JLocalRef(program, local);
       } else if (variable instanceof JParameter) {
         JParameter parameter = (JParameter) variable;
-        if (parameter.getEnclosingMethod() != fCurrentMethod) {
+        if (parameter.getEnclosingMethod() != currentMethod) {
           throw new InternalCompilerException(
               "ParameterRef referencing param in a different method.");
         }
@@ -1716,7 +1747,7 @@
                 "FieldRef referencing field in a different type.");
           }
         }
-        return new JFieldRef(program, instance, field, fCurrentClass);
+        return new JFieldRef(program, instance, field, currentClass);
       }
       throw new InternalCompilerException("Unknown JVariable subclass.");
     }
@@ -1749,10 +1780,10 @@
         return null;
       }
       String sname = String.valueOf(name);
-      Map/* <String, JLabel> */labelMap = (Map) fLabelMap.get(enclosingMethod);
+      Map/* <String, JLabel> */labelMap = (Map) this.labelMap.get(enclosingMethod);
       if (labelMap == null) {
         labelMap = new HashMap();
-        fLabelMap.put(enclosingMethod, labelMap);
+        this.labelMap.put(enclosingMethod, labelMap);
       }
       JLabel jlabel = (JLabel) labelMap.get(sname);
       if (jlabel == null) {
@@ -1831,9 +1862,9 @@
 
       if (variable instanceof JLocal || variable instanceof JParameter) {
         LocalVariableBinding localBinding = (LocalVariableBinding) binding;
-        if (localBinding.declaringScope.methodScope() != fCurrentMethodScope) {
+        if (localBinding.declaringScope.methodScope() != currentMethodScope) {
           variable = null;
-          VariableBinding[] vars = fCurrentMethodScope.getEmulationPath(localBinding);
+          VariableBinding[] vars = currentMethodScope.getEmulationPath(localBinding);
           if (vars == null) {
             return null;
           }
@@ -1842,10 +1873,10 @@
 
           // See if there's an available parameter
           if (varBinding instanceof SyntheticArgumentBinding) {
-            JType type = (JType) fTypeMap.get(varBinding.type);
+            JType type = (JType) typeMap.get(varBinding.type);
             String name = String.valueOf(varBinding.name);
-            for (int i = 0; i < fCurrentMethod.params.size(); ++i) {
-              JParameter param = (JParameter) fCurrentMethod.params.get(i);
+            for (int i = 0; i < currentMethod.params.size(); ++i) {
+              JParameter param = (JParameter) currentMethod.params.get(i);
               if (type == param.getType() && name.equals(param.getName())) {
                 variable = param;
                 break;
@@ -1855,7 +1886,7 @@
 
           // just use the field
           if (variable == null) {
-            variable = (JField) fTypeMap.get(varBinding);
+            variable = (JField) typeMap.get(varBinding);
           }
 
           // now we have an updated variable that we can create our ref from
@@ -1904,7 +1935,7 @@
 
         if (result != null) {
           if (areParametersIdentical(binding, result)) {
-            JMethod upRef = (JMethod) fTypeMap.get(result);
+            JMethod upRef = (JMethod) typeMap.get(result);
             if (!method.overrides.contains(upRef)) {
               method.overrides.add(upRef);
             }
@@ -1925,16 +1956,6 @@
         }
       }
     }
-
-    private Object[] fArgs = new Object[1];
-    private JReferenceType fCurrentClass;
-    private ClassScope fCurrentClassScope;
-    private JMethod fCurrentMethod;
-    private MethodScope fCurrentMethodScope;
-    private final Map/* <JMethod, Map<String, JLabel>> */fLabelMap = new IdentityHashMap();
-    private Class[] fParams = new Class[1];
-    private final TypeMap fTypeMap;
-    private final JProgram program;
   }
 
   /**
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 b2baf00..88ee143 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.HasEnclosingType;
@@ -126,12 +140,14 @@
 import java.util.Stack;
 
 /**
- * Creates a JavaScript AST  from a <code>JProgram</code> node.
+ * Creates a JavaScript AST from a <code>JProgram</code> node.
  */
 public class GenerateJavaScriptAST {
 
   private class CreateNamesAndScopesVisitor extends JVisitor {
 
+    private final Stack/* <JsScope> */scopeStack = new Stack();
+
     // @Override
     public void endVisit(JClassType x) {
       pop();
@@ -142,7 +158,7 @@
       String name = x.getName();
       String mangleName = mangleName(x);
       if (x.isStatic()) {
-        names.put(x, fRootScope.createUniqueObfuscatableName(mangleName, name));
+        names.put(x, rootScope.createUniqueObfuscatableName(mangleName, name));
       } else {
         names.put(x, peek().createUniqueObfuscatableName(mangleName, name));
       }
@@ -183,16 +199,16 @@
     public void endVisit(JProgram x) {
       // visit special things that may have been culled
       JField field = x.getSpecialField("Object.typeId");
-      names.put(field, fObjectScope.getOrCreateObfuscatableName(
-        mangleName(field), field.getName()));
+      names.put(field, objectScope.getOrCreateObfuscatableName(
+          mangleName(field), field.getName()));
 
       field = x.getSpecialField("Object.typeName");
       names.put(field,
-        fObjectScope.getOrCreateObfuscatableName(mangleName(field)));
+          objectScope.getOrCreateObfuscatableName(mangleName(field)));
 
       field = x.getSpecialField("Cast.typeIdArray");
-      names.put(field, fRootScope.getOrCreateObfuscatableName(
-        mangleName(field), field.getName()));
+      names.put(field, rootScope.getOrCreateObfuscatableName(mangleName(field),
+          field.getName()));
 
       /*
        * put the null method and field into fObjectScope since they can be
@@ -200,17 +216,17 @@
        */
       JMethod nullMethod = x.getNullMethod();
       polymorphicNames.put(nullMethod,
-        fObjectScope.createUniqueObfuscatableName(nullMethod.getName()));
+          objectScope.createUniqueObfuscatableName(nullMethod.getName()));
       JField nullField = x.getNullField();
-      JsName nullFieldName = fObjectScope.createUniqueObfuscatableName(nullField.getName());
+      JsName nullFieldName = objectScope.createUniqueObfuscatableName(nullField.getName());
       polymorphicNames.put(nullField, nullFieldName);
       names.put(nullField, nullFieldName);
 
       /*
        * put nullMethod in the global scope, too; it's the replacer for clinits
        */
-      fNullMethodName = fRootScope.createUniqueObfuscatableName(nullMethod.getName());
-      names.put(nullMethod, fNullMethodName);
+      nullMethodName = rootScope.createUniqueObfuscatableName(nullMethod.getName());
+      names.put(nullMethod, nullMethodName);
     }
 
     // @Override
@@ -228,12 +244,12 @@
       }
 
       // My seed function name
-      names.put(x, fRootScope.createUniqueObfuscatableName(getNameString(x),
-        x.getShortName()));
+      names.put(x, rootScope.createUniqueObfuscatableName(getNameString(x),
+          x.getShortName()));
 
       // My class scope
       if (x.extnds == null) {
-        myScope = fObjectScope;
+        myScope = objectScope;
       } else {
         JsScope parentScope = (JsScope) classScopes.get(x.extnds);
         // Run my superclass first!
@@ -247,8 +263,8 @@
          * of its subclasses; this ensures that interface method names trump all
          * (except Object methods names)
          */
-        if (parentScope == fObjectScope) {
-          parentScope = fInterfaceScope;
+        if (parentScope == objectScope) {
+          parentScope = interfaceScope;
         }
         myScope = new JsScope(parentScope);
         myScope.setDescription("class " + x.getShortName());
@@ -262,7 +278,7 @@
     // @Override
     public boolean visit(JInterfaceType x) {
       // interfaces have no name at run time
-      push(fInterfaceScope);
+      push(interfaceScope);
       return true;
     }
 
@@ -274,8 +290,8 @@
       if (!x.isStatic()) {
         if (getPolyName(x) == null) {
           String mangleName = mangleNameForPoly(x);
-          JsName polyName = fInterfaceScope.getOrCreateObfuscatableName(
-            mangleName, name);
+          JsName polyName = interfaceScope.getOrCreateObfuscatableName(
+              mangleName, name);
           polymorphicNames.put(x, polyName);
         }
       }
@@ -289,15 +305,15 @@
       // my global name
       JsName globalName;
       if (x.getEnclosingType() == null) {
-        globalName = fRootScope.createUniqueObfuscatableName(name);
+        globalName = rootScope.createUniqueObfuscatableName(name);
       } else {
         String mangleName = mangleNameForGlobal(x);
-        globalName = fRootScope.createUniqueObfuscatableName(mangleName, name);
+        globalName = rootScope.createUniqueObfuscatableName(mangleName, name);
       }
       names.put(x, globalName);
 
       // create my peer JsFunction
-      JsFunction jsFunction = new JsFunction(fRootScope, globalName);
+      JsFunction jsFunction = new JsFunction(rootScope, globalName);
       methodMap.put(x, jsFunction);
 
       push(jsFunction.getScope());
@@ -311,15 +327,15 @@
       if (!x.isStatic()) {
         if (getPolyName(x) == null) {
           String mangleName = mangleNameForPoly(x);
-          JsName polyName = fInterfaceScope.getOrCreateObfuscatableName(
-            mangleName, name);
+          JsName polyName = interfaceScope.getOrCreateObfuscatableName(
+              mangleName, name);
           polymorphicNames.put(x, polyName);
         }
       }
 
       // set my global name now that we have a name allocator
       String fnName = mangleNameForGlobal(x);
-      JsName globalName = fRootScope.createUniqueObfuscatableName(fnName, name);
+      JsName globalName = rootScope.createUniqueObfuscatableName(fnName, name);
       x.getFunc().setName(globalName);
       names.put(x, globalName);
 
@@ -337,12 +353,22 @@
     private void push(JsScope scope) {
       scopeStack.push(scope);
     }
-
-    private final Stack/* <JsScope> */scopeStack = new Stack();
   }
-  
+
   private class GenerateJavaScriptVisitor extends JVisitor {
 
+    private final Set/* <JClassType> */alreadyRan = new HashSet/* <JClassType> */();
+
+    private JMethod currentMethod = null;
+
+    private final JsName globalTemp = rootScope.getOrCreateUnobfuscatableName("_");
+
+    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) {
       throw new InternalCompilerException("Should not get here.");
@@ -377,12 +403,12 @@
        * when Object.toString() == 'some string'.
        */
       if (myOp == JsBinaryOperator.EQ
-        && x.getLhs().getType() instanceof JReferenceType
-        && x.getRhs().getType() instanceof JReferenceType) {
+          && x.getLhs().getType() instanceof JReferenceType
+          && x.getRhs().getType() instanceof JReferenceType) {
         myOp = JsBinaryOperator.REF_EQ;
       } else if (myOp == JsBinaryOperator.NEQ
-        && x.getLhs().getType() instanceof JReferenceType
-        && x.getRhs().getType() instanceof JReferenceType) {
+          && x.getLhs().getType() instanceof JReferenceType
+          && x.getRhs().getType() instanceof JReferenceType) {
         myOp = JsBinaryOperator.REF_NEQ;
       }
       JsBinaryOperation binOp = new JsBinaryOperation(myOp, lhs, rhs);
@@ -444,7 +470,7 @@
     public void endVisit(JClassLiteral x, Mutator m) {
       // My seed function name
       String nameString = x.refType.getJavahSignatureName() + "_classlit";
-      JsName classLit = fRootScope.getOrCreateObfuscatableName(nameString);
+      JsName classLit = rootScope.getOrCreateObfuscatableName(nameString);
       classLits.put(x.refType, classLit);
       push(classLit.makeRef());
     }
@@ -456,11 +482,11 @@
 
     // @Override
     public void endVisit(JClassType x) {
-      if (fAlreadyRan.contains(x)) {
+      if (alreadyRan.contains(x)) {
         return;
       }
 
-      fAlreadyRan.add(x);
+      alreadyRan.add(x);
 
       List/* <JsFunction> */jsFuncs = popList(x.methods.size()); // methods
       List/* <JsStatement> */jsFields = popList(x.fields.size()); // fields
@@ -486,13 +512,13 @@
           JsName seedFuncName = getName(x);
 
           // seed function
-          JsFunction seedFunc = new JsFunction(fRootScope, seedFuncName);
+          JsFunction seedFunc = new JsFunction(rootScope, seedFuncName);
           JsBlock body = new JsBlock();
           seedFunc.setBody(body);
           globalStmts.add(seedFunc.makeStmt());
 
           // prototype
-          JsNameRef lhs = fPrototype.makeRef();
+          JsNameRef lhs = prototype.makeRef();
           lhs.setQualifier(seedFuncName.makeRef());
           JsExpression rhs;
           if (x.extnds != null) {
@@ -503,17 +529,16 @@
             rhs = new JsObjectLiteral();
           }
           JsExpression protoAsg = createAssignment(lhs, rhs);
-          JsExpression tmpAsg = createAssignment(fGlobalTemp.makeRef(),
-            protoAsg);
+          JsExpression tmpAsg = createAssignment(globalTemp.makeRef(), protoAsg);
           globalStmts.add(tmpAsg.makeStmt());
         } else {
           /*
            * MAGIC: java.lang.String is implemented as a JavaScript String
            * primitive with a modified prototype.
            */
-          JsNameRef rhs = fPrototype.makeRef();
-          rhs.setQualifier(fRootScope.getOrCreateUnobfuscatableName("String").makeRef());
-          JsExpression tmpAsg = createAssignment(fGlobalTemp.makeRef(), rhs);
+          JsNameRef rhs = prototype.makeRef();
+          rhs.setQualifier(rootScope.getOrCreateUnobfuscatableName("String").makeRef());
+          JsExpression tmpAsg = createAssignment(globalTemp.makeRef(), rhs);
           globalStmts.add(tmpAsg.makeStmt());
         }
 
@@ -522,7 +547,7 @@
           JMethod method = (JMethod) x.methods.get(i);
           if (!method.isStatic() && !method.isAbstract()) {
             JsNameRef lhs = getPolyName(method).makeRef();
-            lhs.setQualifier(fGlobalTemp.makeRef());
+            lhs.setQualifier(globalTemp.makeRef());
             JsNameRef rhs = getName(method).makeRef();
             JsExpression asg = createAssignment(lhs, rhs);
             globalStmts.add(new JsExprStmt(asg));
@@ -536,9 +561,9 @@
             // _.toString = function(){return this.java_lang_Object_toString();}
 
             // lhs
-            JsName lhsName = fRootScope.getOrCreateUnobfuscatableName("toString");
+            JsName lhsName = rootScope.getOrCreateUnobfuscatableName("toString");
             JsNameRef lhs = lhsName.makeRef();
-            lhs.setQualifier(fGlobalTemp.makeRef());
+            lhs.setQualifier(globalTemp.makeRef());
 
             // rhs
             JsInvocation call = new JsInvocation();
@@ -546,7 +571,7 @@
             toStringRef.setQualifier(new JsThisRef());
             call.setQualifier(toStringRef);
             JsReturn jsReturn = new JsReturn(call);
-            JsFunction rhs = new JsFunction(fRootScope);
+            JsFunction rhs = new JsFunction(rootScope);
             JsBlock body = new JsBlock();
             body.getStatements().add(jsReturn);
             rhs.setBody(body);
@@ -561,7 +586,7 @@
         {
           JField typeIdField = program.getSpecialField("Object.typeName");
           JsNameRef lhs = getName(typeIdField).makeRef();
-          lhs.setQualifier(fGlobalTemp.makeRef());
+          lhs.setQualifier(globalTemp.makeRef());
           JsStringLiteral rhs = jsProgram.getStringLiteral(x.getName());
           JsExpression asg = createAssignment(lhs, rhs);
           globalStmts.add(new JsExprStmt(asg));
@@ -572,7 +597,7 @@
         if (typeId >= 0) {
           JField typeIdField = program.getSpecialField("Object.typeId");
           JsNameRef fieldRef = getName(typeIdField).makeRef();
-          fieldRef.setQualifier(fGlobalTemp.makeRef());
+          fieldRef.setQualifier(globalTemp.makeRef());
           JsIntegralLiteral typeIdLit = jsProgram.getIntegralLiteral(BigInteger.valueOf(typeId));
           JsExpression asg = createAssignment(fieldRef, typeIdLit);
           globalStmts.add(new JsExprStmt(asg));
@@ -645,10 +670,10 @@
       } else {
         x.getType().getDefaultValue().traverse(this);
       }
-      
+
       JsNameRef fieldRef = getName(x).makeRef();
       if (!x.isStatic()) {
-        fieldRef.setQualifier(fGlobalTemp.makeRef());
+        fieldRef.setQualifier(globalTemp.makeRef());
       }
       JsExpression asg = createAssignment(fieldRef, (JsExpression) pop());
       push(new JsExprStmt(asg));
@@ -663,13 +688,13 @@
       if (x.getInstance() != null) {
         qualifier = (JsExpression) pop();
       }
-      
+
       JField field = x.getField();
       JsInvocation jsInvocation = maybeCreateClinitCall(field);
       if (jsInvocation != null) {
         qualifier = createCommaExpression(qualifier, jsInvocation);
       }
-      
+
       nameRef.setQualifier(qualifier); // instance
       push(nameRef);
     }
@@ -705,7 +730,7 @@
       if (x.getTestExpr() != null) {
         jsFor.setCondition((JsExpression) pop());
       }
-      
+
       // initializers
       JsExpression initExpr = null;
       List/* <JsExprStmt> */initStmts = popList(x.getInitializers().size());
@@ -803,7 +828,7 @@
       JsNameRef localRef = (JsNameRef) pop(); // localRef
 
       JsBinaryOperation binOp = new JsBinaryOperation(JsBinaryOperator.ASG,
-        localRef, initializer);
+          localRef, initializer);
 
       push(binOp.makeStmt());
     }
@@ -855,7 +880,7 @@
       }
 
       push(jsFunc);
-      fCurrentMethod = null;
+      currentMethod = null;
     }
 
     // @Override
@@ -922,13 +947,13 @@
     // @Override
     public void endVisit(JPostfixOperation x, Mutator m) {
       push(new JsPostfixOperation(JavaToJsOperatorMap.get(x.op),
-        (JsExpression) pop())); // arg
+          (JsExpression) pop())); // arg
     }
 
     // @Override
     public void endVisit(JPrefixOperation x, Mutator m) {
       push(new JsPrefixOperation(JavaToJsOperatorMap.get(x.op),
-        (JsExpression) pop())); // arg
+          (JsExpression) pop())); // arg
     }
 
     // @Override
@@ -960,9 +985,9 @@
        * }
        * </pre>
        */
-      JsFunction gwtOnLoad = new JsFunction(fRootScope);
+      JsFunction gwtOnLoad = new JsFunction(rootScope);
       globalStmts.add(gwtOnLoad.makeStmt());
-      gwtOnLoad.setName(fRootScope.getOrCreateUnobfuscatableName("gwtOnLoad"));
+      gwtOnLoad.setName(rootScope.getOrCreateUnobfuscatableName("gwtOnLoad"));
       JsBlock body = new JsBlock();
       gwtOnLoad.setBody(body);
       JsScope fnScope = gwtOnLoad.getScope();
@@ -1071,7 +1096,7 @@
                 if (jsName == null) {
                   // this can occur when JSNI references an instance method on a
                   // type that was never actually instantiated.
-                  jsName = fNullMethodName;
+                  jsName = nullMethodName;
                 }
                 x.setName(jsName);
               }
@@ -1086,7 +1111,7 @@
       }
 
       push(jsFunc);
-      fCurrentMethod = null;
+      currentMethod = null;
     }
 
     // @Override
@@ -1162,13 +1187,13 @@
 
     // @Override
     public boolean visit(JClassType x) {
-      if (fAlreadyRan.contains(x)) {
+      if (alreadyRan.contains(x)) {
         return false;
       }
 
       // force supertype to generate code first, this is required for prototype
       // chaining to work properly
-      if (x.extnds != null && !fAlreadyRan.contains(x)) {
+      if (x.extnds != null && !alreadyRan.contains(x)) {
         x.extnds.traverse(this);
       }
 
@@ -1177,7 +1202,7 @@
 
     // @Override
     public boolean visit(JMethod x) {
-      fCurrentMethod = x;
+      currentMethod = x;
       return true;
     }
 
@@ -1185,10 +1210,10 @@
     public boolean visit(JProgram x) {
       // handle null method
       // return 'window' so that fields can be referenced
-      JsReturn jsReturn = new JsReturn(fWindow.makeRef());
+      JsReturn jsReturn = new JsReturn(window.makeRef());
       JsBlock body = new JsBlock();
       body.getStatements().add(jsReturn);
-      JsFunction nullFunc = new JsFunction(fRootScope, fNullMethodName);
+      JsFunction nullFunc = new JsFunction(rootScope, nullMethodName);
       nullFunc.setBody(body);
       jsProgram.getGlobalBlock().getStatements().add(nullFunc.makeStmt());
       return true;
@@ -1196,7 +1221,7 @@
 
     // @Override
     public boolean visit(JsniMethod x) {
-      fCurrentMethod = x;
+      currentMethod = x;
       return false;
     }
 
@@ -1253,11 +1278,11 @@
     private void handleClinit(JsFunction clinitFunc) {
       // self-assign to the null func immediately (to prevent reentrancy)
       JsExpression asg = createAssignment(clinitFunc.getName().makeRef(),
-        fNullMethodName.makeRef());
+          nullMethodName.makeRef());
       clinitFunc.getBody().getStatements().add(0, asg.makeStmt());
 
       // return 'window' so that fields can be referenced
-      JsReturn jsReturn = new JsReturn(fWindow.makeRef());
+      JsReturn jsReturn = new JsReturn(window.makeRef());
       clinitFunc.getBody().getStatements().add(jsReturn);
     }
 
@@ -1265,13 +1290,13 @@
       if (!x.isStatic()) {
         return null;
       }
-      
+
       JReferenceType enclosingType = x.getEnclosingType();
       if (!typeOracle.hasClinit(enclosingType)) {
         return null;
       }
       // don't need to clinit on my own static fields
-      if (enclosingType == fCurrentMethod.getEnclosingType()) {
+      if (enclosingType == currentMethod.getEnclosingType()) {
         return null;
       }
       JMethod clinitMethod = (JMethod) enclosingType.methods.get(0);
@@ -1284,17 +1309,17 @@
       if (!x.isStatic()) {
         return null;
       }
-      
+
       JReferenceType enclosingType = x.getEnclosingType();
       if (!typeOracle.hasClinit(enclosingType)) {
         return null;
       }
-      
+
       // avoid recursion sickness
       if (x == enclosingType.methods.get(0)) {
         return null;
       }
-      
+
       if (program.isStaticImpl(x)) {
         return null;
       }
@@ -1314,7 +1339,7 @@
       while (count > 0) {
         array[--count] = pop();
       }
-      
+
       List/* <T> */list = new ArrayList/* <T> */();
       for (int i = 0; i < array.length; i++) {
         JsNode item = array[i];
@@ -1330,7 +1355,7 @@
       while (count > 0) {
         array[--count] = pop();
       }
-      
+
       for (int i = 0; i < array.length; i++) {
         JsNode item = array[i];
         if (item != null) {
@@ -1342,32 +1367,12 @@
     private void push(JsNode node) {
       nodeStack.push(node);
     }
-
-    private final Set/* <JClassType> */fAlreadyRan = new HashSet/* <JClassType> */();
-
-    private JMethod fCurrentMethod = null;
-
-    private final JsName fGlobalTemp = fRootScope.getOrCreateUnobfuscatableName("_");
-
-    private final JsName fPrototype = fObjectScope.getOrCreateUnobfuscatableName("prototype");
-
-    private final JsName fWindow = fRootScope.getOrCreateUnobfuscatableName("window");
-
-    private final Stack/* <JsNode> */nodeStack = new Stack/* <JsNode> */();
   }
-  
+
   private static class JavaToJsOperatorMap {
     private static final Map/* <JBinaryOperator, JsBinaryOperator> */bOpMap = new IdentityHashMap();
     private static final Map/* <JUnaryOperator, JsUnaryOperator> */uOpMap = new IdentityHashMap();
 
-    public static JsBinaryOperator get(JBinaryOperator op) {
-      return (JsBinaryOperator) bOpMap.get(op);
-    }
-
-    public static JsUnaryOperator get(JUnaryOperator op) {
-      return (JsUnaryOperator) uOpMap.get(op);
-    }
-
     static {
       bOpMap.put(JBinaryOperator.MUL, JsBinaryOperator.MUL);
       bOpMap.put(JBinaryOperator.DIV, JsBinaryOperator.DIV);
@@ -1407,23 +1412,52 @@
       uOpMap.put(JUnaryOperator.NOT, JsUnaryOperator.NOT);
       uOpMap.put(JUnaryOperator.BIT_NOT, JsUnaryOperator.BIT_NOT);
     }
+
+    public static JsBinaryOperator get(JBinaryOperator op) {
+      return (JsBinaryOperator) bOpMap.get(op);
+    }
+
+    public static JsUnaryOperator get(JUnaryOperator op) {
+      return (JsUnaryOperator) uOpMap.get(op);
+    }
   }
 
   public static void exec(JProgram program, JsProgram jsProgram) {
     GenerateJavaScriptAST generateJavaScriptAST = new GenerateJavaScriptAST(
-      program, jsProgram);
+        program, jsProgram);
     generateJavaScriptAST.execImpl();
   }
 
+  private final Map/* <JType, JsName> */classLits = new IdentityHashMap();
+
+  private final Map/* <JClassType, JsScope> */classScopes = new IdentityHashMap();
+
+  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 final Map/* <JMethod, JsName> */polymorphicNames = new IdentityHashMap();
+  private final JProgram program;
+  private final JTypeOracle typeOracle;
+
   private GenerateJavaScriptAST(JProgram program, JsProgram jsProgram) {
     this.program = program;
     typeOracle = program.typeOracle;
     this.jsProgram = jsProgram;
-    fRootScope = jsProgram.getScope();
-    fObjectScope = new JsScope(fRootScope);
-    fObjectScope.setDescription("Object scope");
-    fInterfaceScope = new JsScope(fObjectScope);
-    fInterfaceScope.setDescription("Interfaces");
+    rootScope = jsProgram.getScope();
+    objectScope = new JsScope(rootScope);
+    objectScope.setDescription("Object scope");
+    interfaceScope = new JsScope(objectScope);
+    interfaceScope.setDescription("Interfaces");
   }
 
   String getNameString(HasName hasName) {
@@ -1438,7 +1472,7 @@
 
   String mangleNameForGlobal(JMethod x) {
     String s = getNameString(x.getEnclosingType()) + '_' + getNameString(x)
-      + "__";
+        + "__";
     for (int i = 0; i < x.getOriginalParamTypes().size(); ++i) {
       JType type = (JType) x.getOriginalParamTypes().get(i);
       s += type.getJavahSignatureName();
@@ -1470,17 +1504,4 @@
     return (JsName) polymorphicNames.get(x);
   }
 
-  private final Map/* <JType, JsName> */classLits = new IdentityHashMap();
-  private final Map/* <JClassType, JsScope> */classScopes = new IdentityHashMap();
-  private final JsScope fInterfaceScope;
-  private JsName fNullMethodName;
-  private final JsScope fObjectScope;
-  private final JsScope fRootScope;
-  private final JsProgram jsProgram;
-  private final Map/* <JMethod, JsFunction> */methodMap = new IdentityHashMap();
-  private final Map/* <HasName, JsName> */names = new IdentityHashMap();
-  private final Map/* <JMethod, JsName> */polymorphicNames = new IdentityHashMap();
-  private final JProgram program;
-  private final JTypeOracle typeOracle;
-
 }
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 90fc366..d7bd231 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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;
 
 public class InternalCompilerException extends RuntimeException {
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 ea6ba8e..ad86e58 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JAbsentArrayDimension;
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 0ba542b..f34e52c 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JBinaryOperation;
@@ -25,35 +39,9 @@
   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.");
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    private void checkAndReplaceJso(Mutator arg, JType targetType) {
-      JType argType = arg.get().getType();
-      if (argType == targetType) {
-        return;
-      }
-      
-      if (!(targetType instanceof JReferenceType)) {
-        return;
-      }
-      
-      if (!program.isJavaScriptObject(argType)) {
-        return;
-      }
-      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);
-    }
-
-    private JMethod fCurrentMethod;
+    private JMethod currentMethod;
 
     // @Override
     public void endVisit(JBinaryOperation x, Mutator m) {
@@ -78,7 +66,7 @@
 
     // @Override
     public void endVisit(JMethod x) {
-      fCurrentMethod = null;
+      currentMethod = null;
     }
 
     // @Override
@@ -96,15 +84,51 @@
     // @Override
     public void endVisit(JReturnStatement x) {
       if (x.getExpression() != null) {
-        checkAndReplaceJso(x.expr, fCurrentMethod.getType());
+        checkAndReplaceJso(x.expr, currentMethod.getType());
       }
     }
 
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+
     // @Override
     public boolean visit(JMethod x) {
-      fCurrentMethod = x;
+      currentMethod = x;
       return true;
     }
+
+    private void checkAndReplaceJso(Mutator arg, JType targetType) {
+      JType argType = arg.get().getType();
+      if (argType == targetType) {
+        return;
+      }
+
+      if (!(targetType instanceof JReferenceType)) {
+        return;
+      }
+
+      if (!program.isJavaScriptObject(argType)) {
+        return;
+      }
+      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);
+    }
+  }
+
+  public static void exec(JProgram program) {
+    new JavaScriptObjectCaster(program).execImpl();
+  }
+
+  private final JProgram program;
+
+  private JavaScriptObjectCaster(JProgram program) {
+    this.program = program;
   }
 
   private void execImpl() {
@@ -118,14 +142,4 @@
     }
   }
 
-  private final JProgram program;
-
-  private JavaScriptObjectCaster(JProgram program) {
-    this.program = program;
-  }
-
-  public static void exec(JProgram program) {
-    new JavaScriptObjectCaster(program).execImpl();
-  }
-
 }
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 83aa78d..40c8071 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JClassType;
@@ -37,8 +51,6 @@
  */
 public class MakeCallsStatic {
 
-  private final JProgram program;
-
   /**
    * 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
@@ -48,11 +60,7 @@
   private class CreateStaticMethodVisitor extends JVisitor {
 
     private final ChangeList changeList = new ChangeList(
-      "Create static impls for instance methods");
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
+        "Create static impls for instance methods");
 
     // @Override
     public void endVisit(JMethodCall x, Mutator m) {
@@ -92,36 +100,37 @@
        * its enclosing class, which will break iteration.
        */
       JMethod newMethod = new JMethod(program, newName, enclosingType,
-        oldReturnType, false, true, true, oldMethod.isPrivate());
+          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);
+      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);
+        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);
+            oldVar.getType(), oldVar.isFinal(), newMethod);
         varMap.put(oldVar, newVar);
       }
       ChangeList myChangeList = new ChangeList("Create a new static method '"
-        + newMethod + "' for instance method '" + oldMethod + "'");
+          + 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.");
+          "Update thisrefs as paramrefs; update paramrefs and localrefs to target this method.");
       RewriteMethodBody rewriter = new RewriteMethodBody(thisParam, varMap,
-        subChangeList);
+          subChangeList);
       oldMethod.traverse(rewriter);
       myChangeList.add(subChangeList);
 
@@ -145,6 +154,56 @@
       myChangeList.addStatement(statement, oldMethod.body);
       changeList.add(myChangeList);
     }
+
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+  }
+
+  /**
+   * For any method calls to methods we updated during
+   * 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.");
+
+    /*
+     * In cases where callers are directly referencing (effectively) final
+     * instance methods, rewrite the call site to reference the newly-generated
+     * static method instead.
+     */
+    // @Override
+    public void endVisit(JMethodCall x, Mutator m) {
+      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);
+
+      // The qualifier becomes the first arg
+      changes.addExpression(x.instance, newCall.args);
+      // 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);
+      }
+
+      changeList.add(changes);
+    }
+
+    public ChangeList getChangeList() {
+      return changeList;
+    }
   }
 
   /**
@@ -185,50 +244,14 @@
     }
   }
 
-  /**
-   * For any method calls to methods we updated during
-   * CreateStaticMethodVisitor, go and rewrite the call sites to call the static
-   * method instead.
-   */
-  private class RewriteCallSites extends JVisitor {
+  public static boolean exec(JProgram program) {
+    return new MakeCallsStatic(program).execImpl();
+  }
 
-    private final ChangeList changeList = new ChangeList(
-      "Rewrite calls to final instance methods as calls to static impl methods.");
+  private final JProgram program;
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    /*
-     * In cases where callers are directly referencing (effectively) final
-     * instance methods, rewrite the call site to reference the newly-generated
-     * static method instead.
-     */
-    // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
-      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);
-
-      // The qualifier becomes the first arg
-      changes.addExpression(x.instance, newCall.args);
-      // 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);
-      }
-
-      changeList.add(changes);
-    }
+  private MakeCallsStatic(JProgram program) {
+    this.program = program;
   }
 
   private boolean execImpl() {
@@ -253,12 +276,4 @@
     return true;
   }
 
-  private MakeCallsStatic(JProgram program) {
-    this.program = program;
-  }
-
-  public static boolean exec(JProgram program) {
-    return new MakeCallsStatic(program).execImpl();
-  }
-
 }
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 5820330..a2ded14 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JClassType;
@@ -20,60 +34,6 @@
  */
 public class MethodAndClassFinalizer {
 
-  private final Set/* <JClassType> */isSubclassed = new HashSet/* <JClassType> */();
-  private final Set/* <JMethod> */isOverriden = new HashSet/* <JMethod> */();
-
-  /**
-   * Find all methods and classes that ARE overriden/subclassed.
-   */
-  private class MarkVisitor extends JVisitor {
-
-    // @Override
-    public boolean visit(JClassType x) {
-      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);
-      }
-      return false;
-    }
-
-    // @Override
-    public boolean visit(JInterfaceType x) {
-      for (int i = 0; i < x.methods.size(); ++i) {
-        JMethod method = (JMethod) x.methods.get(i);
-        method.traverse(this);
-      }
-      return false;
-    }
-
-    // @Override
-    public boolean visit(JMethod x) {
-      for (int i = 0; i < x.overrides.size(); ++i) {
-        JMethod it = (JMethod) x.overrides.get(i);
-        isOverriden.add(it);
-      }
-      return false;
-    }
-
-    // @Override
-    public boolean visit(JsniMethod x) {
-      return visit((JMethod) x);
-    }
-
-    // @Override
-    public boolean visit(JProgram x) {
-      for (int i = 0; i < x.getDeclaredTypes().size(); ++i) {
-        JReferenceType type = (JReferenceType) x.getDeclaredTypes().get(i);
-        type.traverse(this);
-      }
-      return false;
-    }
-  }
-
   /**
    * Any method and classes that weren't marked during MarkVisitor can be set
    * final.
@@ -87,7 +47,7 @@
   private class FinalizeVisitor extends JVisitor {
 
     private final ChangeList changeList = new ChangeList(
-      "Finalize effectively final methods and types.");
+        "Finalize effectively final methods and types.");
 
     public ChangeList getChangeList() {
       return changeList;
@@ -127,6 +87,67 @@
       return visit((JMethod) x);
     }
   }
+  /**
+   * Find all methods and classes that ARE overriden/subclassed.
+   */
+  private class MarkVisitor extends JVisitor {
+
+    // @Override
+    public boolean visit(JClassType x) {
+      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);
+      }
+      return false;
+    }
+
+    // @Override
+    public boolean visit(JInterfaceType x) {
+      for (int i = 0; i < x.methods.size(); ++i) {
+        JMethod method = (JMethod) x.methods.get(i);
+        method.traverse(this);
+      }
+      return false;
+    }
+
+    // @Override
+    public boolean visit(JMethod x) {
+      for (int i = 0; i < x.overrides.size(); ++i) {
+        JMethod it = (JMethod) x.overrides.get(i);
+        isOverriden.add(it);
+      }
+      return false;
+    }
+
+    // @Override
+    public boolean visit(JProgram x) {
+      for (int i = 0; i < x.getDeclaredTypes().size(); ++i) {
+        JReferenceType type = (JReferenceType) x.getDeclaredTypes().get(i);
+        type.traverse(this);
+      }
+      return false;
+    }
+
+    // @Override
+    public boolean visit(JsniMethod x) {
+      return visit((JMethod) x);
+    }
+  }
+
+  public static boolean exec(JProgram program) {
+    return new MethodAndClassFinalizer().execImpl(program);
+  }
+
+  private final Set/* <JClassType> */isSubclassed = new HashSet/* <JClassType> */();
+
+  private final Set/* <JMethod> */isOverriden = new HashSet/* <JMethod> */();
+
+  private MethodAndClassFinalizer() {
+  }
 
   private boolean execImpl(JProgram program) {
     MarkVisitor marker = new MarkVisitor();
@@ -141,11 +162,4 @@
     return true;
   }
 
-  private MethodAndClassFinalizer() {
-  }
-
-  public static boolean exec(JProgram program) {
-    return new MethodAndClassFinalizer().execImpl(program);
-  }
-
 }
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 721c41c..9269611 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JArrayType;
@@ -25,19 +39,13 @@
  */
 public class MethodCallTightener {
 
-  private final JProgram program;
-
   /**
-   * Updates polymorphic method calls to tighter bindings based on the type of 
+   * 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 ChangeList getChangeList() {
-      return changeList;
-    }
+        "Update polymorphic method calls to tighter bindings based on the type of the qualifier.");
 
     // @Override
     public void endVisit(JMethodCall x, Mutator m) {
@@ -53,7 +61,7 @@
       JReferenceType enclosingType = method.getEnclosingType();
 
       if (instanceType == enclosingType
-        || instanceType instanceof JInterfaceType) {
+          || instanceType instanceof JInterfaceType) {
         // This method call is as tight as it can be for the type of the
         // qualifier
         return;
@@ -78,7 +86,7 @@
       JMethod foundMethod = null;
       JClassType type;
       outer : for (type = (JClassType) instanceType; type != null
-        && type != enclosingType; type = type.extnds) {
+          && type != enclosingType; type = type.extnds) {
         for (int i = 0; i < type.methods.size(); ++i) {
           JMethod methodIt = (JMethod) type.methods.get(i);
           if (JProgram.methodsDoMatch(method, methodIt)) {
@@ -93,8 +101,8 @@
       }
 
       ChangeList changes = new ChangeList("Replace call '" + x + "' to type '"
-        + enclosingType + "' with a call to type '"
-        + foundMethod.getEnclosingType() + "'");
+          + enclosingType + "' with a call to type '"
+          + foundMethod.getEnclosingType() + "'");
 
       // Update the call site
       JMethodCall call = new JMethodCall(program, null, foundMethod);
@@ -114,8 +122,18 @@
 
       return;
     }
+
+    public ChangeList getChangeList() {
+      return changeList;
+    }
   }
 
+  public static boolean exec(JProgram program) {
+    return new MethodCallTightener(program).execImpl();
+  }
+
+  private final JProgram program;
+
   private MethodCallTightener(JProgram program) {
     this.program = program;
   }
@@ -127,13 +145,9 @@
     if (changes.empty()) {
       return false;
     }
-    
+
     changes.apply();
     return true;
   }
 
-  public static boolean exec(JProgram program) {
-    return new MethodCallTightener(program).execImpl();
-  }
-
 }
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 a33ddef..78d45ec 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.Holder;
@@ -31,32 +45,53 @@
  */
 public class MethodInliner {
 
-  private final JProgram program;
-  private JMethod fCurrentMethod;
-
   /**
-   * Method inlining visitor.
+   * Flattens <code>JMultiExpressions</code> where possible.
    */
-  public class InliningVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList("Inline methods.");
+  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;
+
+      // 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 ChangeList getChangeList() {
       return changeList;
     }
-
+  }
+  /**
+   * Method inlining visitor.
+   */
+  public class InliningVisitor extends JVisitor {
     /**
      * 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> */();
 
-    public boolean visit(JMethod x) {
-      fCurrentMethod = x;
-      return true;
-    }
+    private final ChangeList changeList = new ChangeList("Inline methods.");
 
     public void endVisit(JMethod x) {
-      fCurrentMethod = null;
+      currentMethod = null;
     }
 
     // @Override
@@ -80,7 +115,7 @@
         JStatement stmt = (JStatement) stmts.get(0);
         if (stmt instanceof JReturnStatement) {
           didInline = tryInlineSimpleMethodCall(x, m, method,
-            (JReturnStatement) stmt);
+              (JReturnStatement) stmt);
         }
       }
 
@@ -89,13 +124,100 @@
       }
     }
 
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+
+    public boolean visit(JMethod x) {
+      currentMethod = x;
+      return true;
+    }
+
+    /**
+     * This complicated method has a simple purpose: see if the expression part
+     * of a return statement is inlinable. The trickiness comes from the fact
+     * that we'd like to be able to do this recursively in certain cases. For
+     * example, the accessor method
+     * 
+     * <pre>
+     * $getFoo(this$static) {
+     *  return this$static.foo
+     * }
+     * </pre>
+     * 
+     * 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) {
+        // just reference the same JLiteral
+        /*
+         * hackish: pretend there is an arg that is returned which comes after
+         * all the real args; this allows the evaluation order check below to
+         * succeed
+         */
+        magicArg[0] = args.size();
+        return new Holder(targetReturnExpr);
+      } else if (targetReturnExpr instanceof JParameterRef) {
+        // translate the param ref into the appropriate arg
+        int i = params.indexOf(((JParameterRef) targetReturnExpr).getTarget());
+        assert (i >= 0);
+        magicArg[0] = i;
+        return args.getMutator(i);
+      } else if (targetReturnExpr instanceof JFieldRef) {
+        JFieldRef oldFieldRef = (JFieldRef) targetReturnExpr;
+        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) {
+            return null;
+          }
+          changes.replaceExpression(newFieldRef.instance, instanceMutator);
+        }
+        return new Holder(newFieldRef);
+      } else {
+        /*
+         * For now, only inline REALLY trivial stuff since we have no way of
+         * cloning arbitrary expressions.
+         */
+        return null;
+      }
+    }
+
+    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);
+      JExpression instance = x.getInstance();
+      if (instance != null && instance.hasSideEffects()) {
+        changes.addExpression(x.instance, multi.exprs);
+      }
+      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);
+        }
+      }
+
+      changes.replaceExpression(m, multi);
+      changeList.add(changes);
+      return true;
+    }
+
     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 + "'");
+          + method + "'");
 
       // the expression returned by the inlined method, if any
       Mutator resultExpression;
@@ -104,7 +226,7 @@
 
       JExpression targetReturnExpr = returnStmt.getExpression();
       resultExpression = canInlineResultExpression(targetReturnExpr, params,
-        args, magicArg, changes);
+          args, magicArg, changes);
 
       if (resultExpression == null) {
         return false; // cannot inline
@@ -148,118 +270,6 @@
       changeList.add(changes);
       return true;
     }
-
-    /**
-     * This complicated method has a simple purpose: see if the expression part
-     * of a return statement is inlinable. The trickiness comes from the fact
-     * that we'd like to be able to do this recursively in certain cases. For
-     * example, the accessor method
-     * 
-     * <pre>
-     * $getFoo(this$static) {
-     *  return this$static.foo
-     * }
-     * </pre>
-     * 
-     * 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) {
-        // just reference the same JLiteral
-        /*
-         * hackish: pretend there is an arg that is returned which comes after
-         * all the real args; this allows the evaluation order check below to
-         * succeed
-         */
-        magicArg[0] = args.size();
-        return new Holder(targetReturnExpr);
-      } else if (targetReturnExpr instanceof JParameterRef) {
-        // translate the param ref into the appropriate arg
-        int i = params.indexOf(((JParameterRef) targetReturnExpr).getTarget());
-        assert (i >= 0);
-        magicArg[0] = i;
-        return args.getMutator(i);
-      } else if (targetReturnExpr instanceof JFieldRef) {
-        JFieldRef oldFieldRef = (JFieldRef) targetReturnExpr;
-        JField field = oldFieldRef.getField();
-        JExpression instance = oldFieldRef.getInstance();
-        JFieldRef newFieldRef = new JFieldRef(program, null, field,
-          fCurrentMethod.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) {
-            return null;
-          }
-          changes.replaceExpression(newFieldRef.instance, instanceMutator);
-        }
-        return new Holder(newFieldRef);
-      } else {
-        /*
-         * For now, only inline REALLY trivial stuff since we have no way of
-         * cloning arbitrary expressions.
-         */
-        return null;
-      }
-    }
-
-    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);
-      JExpression instance = x.getInstance();
-      if (instance != null && instance.hasSideEffects()) {
-        changes.addExpression(x.instance, multi.exprs);
-      }
-      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);
-        }
-      }
-
-      changes.replaceExpression(m, multi);
-      changeList.add(changes);
-      return true;
-    }
-  }
-
-  /**
-   * Flattens <code>JMultiExpressions</code> where possible.
-   */
-  public class FlattenMultiVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-      "Flatten multis where possible.");
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    // @Override
-    public void endVisit(JMultiExpression x, Mutator m) {
-      HolderList exprs = x.exprs;
-
-      // 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);
-        }
-      }
-    }
   }
 
   /**
@@ -267,11 +277,7 @@
    */
   public class ReduceMultiVisitor extends JVisitor {
     private final ChangeList changeList = new ChangeList(
-      "Reduce multis where possible.");
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
+        "Reduce multis where possible.");
 
     // @Override
     public void endVisit(JMultiExpression x, Mutator m) {
@@ -281,7 +287,7 @@
       if (c == 0) {
         return;
       }
-      
+
       int countSideEffectsBeforeLast = 0;
       for (int i = 0; i < c - 1; ++i) {
         JExpression expr = exprs.getExpr(i);
@@ -302,6 +308,10 @@
       }
     }
 
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+
     // @Override
     public boolean visit(JBlock x) {
       for (int i = 0; i < x.statements.size(); ++i) {
@@ -319,6 +329,14 @@
     }
   }
 
+  public static boolean exec(JProgram program) {
+    return new MethodInliner(program).execImpl();
+  }
+
+  private final JProgram program;
+
+  private JMethod currentMethod;
+
   private MethodInliner(JProgram program) {
     this.program = program;
   }
@@ -358,8 +376,4 @@
     return madeChanges;
   }
 
-  public static boolean exec(JProgram program) {
-    return new MethodInliner(program).execImpl();
-  }
-
 }
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 d436652..288fe33 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JAbsentArrayDimension;
@@ -52,14 +66,90 @@
  */
 public class Pruner {
 
-  private final JProgram program;
-  private final boolean noSpecialTypes;
+  /**
+   * Remove any unreferenced classes and interfaces from JProgram. Remove any
+   * unreferenced methods and fields from their containing classes.
+   */
+  private class PruneVisitor extends JVisitor {
 
-  private final Set/* <JReferenceType> */referencedTypes = new HashSet/* <JReferenceType> */();
-  private final Set/* <JNode> */referencedNonTypes = new HashSet/* <JNode> */();
+    private final ChangeList changeList = new ChangeList(
+        "Prune unreferenced methods, fields, and types.");
 
-  private JMethod fStringValueOfChar = null;
+    public ChangeList getChangeList() {
+      return changeList;
+    }
 
+    // @Override
+    public boolean visit(JClassType type) {
+
+      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 (int i = 0; i < type.methods.size(); ++i) {
+        JMethod it = (JMethod) type.methods.get(i);
+        if (!referencedNonTypes.contains(it)
+            || pruneViaNoninstantiability(isInstantiated, it)) {
+          changeList.removeMethod(it);
+        }
+      }
+
+      return false;
+    }
+
+    // @Override
+    public boolean visit(JInterfaceType type) {
+      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);
+        // all interface fields are static and final
+        if (!isReferenced || !referencedNonTypes.contains(it)) {
+          changeList.removeField(it);
+        }
+      }
+      // 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);
+        // all other interface methods are instance and abstract
+        if (!isInstantiated || !referencedNonTypes.contains(it)) {
+          changeList.removeMethod(it);
+        }
+      }
+
+      return false;
+    }
+
+    // @Override
+    public boolean visit(JProgram program) {
+      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);
+        } else {
+          changeList.removeType(type);
+        }
+      }
+      return false;
+    }
+
+    private boolean pruneViaNoninstantiability(boolean isInstantiated, JField it) {
+      return (!isInstantiated && !it.isStatic());
+    }
+
+    private boolean pruneViaNoninstantiability(boolean isInstantiated,
+        JMethod it) {
+      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
@@ -73,118 +163,6 @@
       program.typeOracle.setInstantiatedTypes(instantiatedTypes);
     }
 
-    /**
-     * Subclasses of JavaScriptObject are never instantiated directly. They are
-     * created "magically" when a JSNI method passes a reference to an existing
-     * JS object into Java code. The point at which a subclass of JSO is passed
-     * into Java code constitutes "instantiation". We must identify these points
-     * and trigger a rescue and instantiation of that particular JSO subclass.
-     * 
-     * @param type The type of the value passing from Java to JavaScript.
-     * @see com.google.gwt.core.client.JavaScriptObject
-     */
-    private void maybeRescueJavaScriptObjectPassingIntoJava(JType type) {
-      if (type instanceof JReferenceType) {
-        JReferenceType refType = (JReferenceType) type;
-        if (program.typeOracle.canTriviallyCast(refType,
-            program.getSpecialJavaScriptObject())) {
-          rescue(refType, true, true);
-        }
-      }
-    }
-
-    private boolean rescue(JReferenceType type, boolean isReferenced,
-        boolean isInstantiated) {
-      if (type == null) {
-        return false;
-      }
-
-      boolean doVisit = false;
-      if (isInstantiated && !instantiatedTypes.contains(type)) {
-        instantiatedTypes.add(type);
-        doVisit = true;
-      }
-      
-      if (isReferenced && !referencedTypes.contains(type)) {
-        referencedTypes.add(type);
-        doVisit = true;
-      }
-      
-      if (doVisit) {
-        type.traverse(this);
-      }
-
-      return doVisit;
-    }
-
-    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());
-        }
-        return true;
-      }
-      return false;
-    }
-
-    private boolean rescue(JField field) {
-      if (field == null) {
-        return false;
-      }
-      
-      if (!referencedNonTypes.contains(field)) {
-        referencedNonTypes.add(field);
-        return true;
-      }
-      return true;
-    }
-
-    /**
-     * Handle special rescues needed implicitly to support concat.
-     */
-    private void rescueByConcat(JType type) {
-      JClassType stringType = program.getTypeJavaLangString();
-      JPrimitiveType charType = program.getTypePrimitiveChar();
-      if (type instanceof JReferenceType && type != stringType) {
-        /*
-         * Any reference types (except String, which works by default) that take
-         * part in a concat must rescue java.lang.Object.toString().
-         */
-        JMethod toStringMethod = program.getSpecialMethod("Object.toString");
-        rescue(toStringMethod);
-      } else if (type == charType) {
-        /*
-         * Characters must rescue String.valueOf(char)
-         */
-        if (fStringValueOfChar == null) {
-          for (int i = 0; i < stringType.methods.size(); ++i) {
-            JMethod meth = (JMethod) stringType.methods.get(i);
-            if (meth.getName().equals("valueOf")) {
-              List params = meth.getOriginalParamTypes();
-              if (params.size() == 1) {
-                if (params.get(0) == charType) {
-                  fStringValueOfChar = meth;
-                  break;
-                }
-              }
-            }
-          }
-          assert (fStringValueOfChar != null);
-        }
-        rescue(fStringValueOfChar);
-      }
-    }
-
     // @Override
     public void endVisit(JBinaryOperation x, Mutator m) {
       // special string concat handling
@@ -348,13 +326,6 @@
     }
 
     // @Override
-    public boolean visit(JStringLiteral literal, Mutator mutator) {
-      // rescue and instantiate java.lang.String
-      rescue(program.getTypeJavaLangString(), true, true);
-      return true;
-    }
-
-    // @Override
     public boolean visit(JsniFieldRef x) {
       /*
        * SPECIAL: this could be an assignment that passes a value from
@@ -382,6 +353,125 @@
       // JsniMethodRef rescues as JMethodCall
       return visit(/* (JMethodCall) */x, null);
     }
+
+    // @Override
+    public boolean visit(JStringLiteral literal, Mutator mutator) {
+      // rescue and instantiate java.lang.String
+      rescue(program.getTypeJavaLangString(), true, true);
+      return true;
+    }
+
+    /**
+     * Subclasses of JavaScriptObject are never instantiated directly. They are
+     * created "magically" when a JSNI method passes a reference to an existing
+     * JS object into Java code. The point at which a subclass of JSO is passed
+     * into Java code constitutes "instantiation". We must identify these points
+     * and trigger a rescue and instantiation of that particular JSO subclass.
+     * 
+     * @param type The type of the value passing from Java to JavaScript.
+     * @see com.google.gwt.core.client.JavaScriptObject
+     */
+    private void maybeRescueJavaScriptObjectPassingIntoJava(JType type) {
+      if (type instanceof JReferenceType) {
+        JReferenceType refType = (JReferenceType) type;
+        if (program.typeOracle.canTriviallyCast(refType,
+            program.getSpecialJavaScriptObject())) {
+          rescue(refType, true, true);
+        }
+      }
+    }
+
+    private boolean rescue(JField field) {
+      if (field == null) {
+        return false;
+      }
+
+      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());
+        }
+        return true;
+      }
+      return false;
+    }
+
+    private boolean rescue(JReferenceType type, boolean isReferenced,
+        boolean isInstantiated) {
+      if (type == null) {
+        return false;
+      }
+
+      boolean doVisit = false;
+      if (isInstantiated && !instantiatedTypes.contains(type)) {
+        instantiatedTypes.add(type);
+        doVisit = true;
+      }
+
+      if (isReferenced && !referencedTypes.contains(type)) {
+        referencedTypes.add(type);
+        doVisit = true;
+      }
+
+      if (doVisit) {
+        type.traverse(this);
+      }
+
+      return doVisit;
+    }
+
+    /**
+     * Handle special rescues needed implicitly to support concat.
+     */
+    private void rescueByConcat(JType type) {
+      JClassType stringType = program.getTypeJavaLangString();
+      JPrimitiveType charType = program.getTypePrimitiveChar();
+      if (type instanceof JReferenceType && type != stringType) {
+        /*
+         * Any reference types (except String, which works by default) that take
+         * part in a concat must rescue java.lang.Object.toString().
+         */
+        JMethod toStringMethod = program.getSpecialMethod("Object.toString");
+        rescue(toStringMethod);
+      } else if (type == charType) {
+        /*
+         * Characters must rescue String.valueOf(char)
+         */
+        if (stringValueOfChar == null) {
+          for (int i = 0; i < stringType.methods.size(); ++i) {
+            JMethod meth = (JMethod) stringType.methods.get(i);
+            if (meth.getName().equals("valueOf")) {
+              List params = meth.getOriginalParamTypes();
+              if (params.size() == 1) {
+                if (params.get(0) == charType) {
+                  stringValueOfChar = meth;
+                  break;
+                }
+              }
+            }
+          }
+          assert (stringValueOfChar != null);
+        }
+        rescue(stringValueOfChar);
+      }
+    }
   }
 
   /**
@@ -406,7 +496,7 @@
       if (referencedNonTypes.contains(x)) {
         return false;
       }
-      
+
       for (int i = 0; i < x.overrides.size(); ++i) {
         JMethod ref = (JMethod) x.overrides.get(i);
         if (referencedNonTypes.contains(ref)) {
@@ -424,100 +514,34 @@
     }
 
     // @Override
-    public boolean visit(JsniMethod x) {
-      return visit((JMethod) x);
-    }
-
-    // @Override
     public boolean visit(JProgram x) {
       didRescue = false;
       return true;
     }
+
+    // @Override
+    public boolean visit(JsniMethod x) {
+      return visit((JMethod) x);
+    }
   }
 
-  /**
-   * Remove any unreferenced classes and interfaces from JProgram. Remove any
-   * unreferenced methods and fields from their containing classes.
-   */
-  private class PruneVisitor extends JVisitor {
+  public static boolean exec(JProgram program, boolean noSpecialTypes) {
+    return new Pruner(program, noSpecialTypes).execImpl();
+  }
 
-    private final ChangeList changeList = new ChangeList(
-        "Prune unreferenced methods, fields, and types.");
+  private final JProgram program;
 
-    public ChangeList getChangeList() {
-      return changeList;
-    }
+  private final boolean noSpecialTypes;
 
-    // @Override
-    public boolean visit(JClassType type) {
+  private final Set/* <JReferenceType> */referencedTypes = new HashSet/* <JReferenceType> */();
 
-      assert (referencedTypes.contains(type));
-      boolean isInstantiated = program.typeOracle.isInstantiatedType(type);
+  private final Set/* <JNode> */referencedNonTypes = new HashSet/* <JNode> */();
 
-      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 (int i = 0; i < type.methods.size(); ++i) {
-        JMethod it = (JMethod) type.methods.get(i);
-        if (!referencedNonTypes.contains(it)
-            || pruneViaNoninstantiability(isInstantiated, it)) {
-          changeList.removeMethod(it);
-        }
-      }
+  private JMethod stringValueOfChar = null;
 
-      return false;
-    }
-
-    private boolean pruneViaNoninstantiability(boolean isInstantiated,
-        JMethod it) {
-      return (!isInstantiated && (!it.isStatic() || program.isStaticImpl(it)));
-    }
-
-    private boolean pruneViaNoninstantiability(boolean isInstantiated, JField it) {
-      return (!isInstantiated && !it.isStatic());
-    }
-
-    // @Override
-    public boolean visit(JInterfaceType type) {
-      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);
-        // all interface fields are static and final
-        if (!isReferenced || !referencedNonTypes.contains(it)) {
-          changeList.removeField(it);
-        }
-      }
-      // 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);
-        // all other interface methods are instance and abstract
-        if (!isInstantiated || !referencedNonTypes.contains(it)) {
-          changeList.removeMethod(it);
-        }
-      }
-
-      return false;
-    }
-
-    // @Override
-    public boolean visit(JProgram program) {
-      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);
-        } else {
-          changeList.removeType(type);
-        }
-      }
-      return false;
-    }
+  private Pruner(JProgram program, boolean noSpecialTypes) {
+    this.program = program;
+    this.noSpecialTypes = noSpecialTypes;
   }
 
   private boolean execImpl() {
@@ -557,13 +581,4 @@
     return madeChanges;
   }
 
-  private Pruner(JProgram program, boolean noSpecialTypes) {
-    this.program = program;
-    this.noSpecialTypes = noSpecialTypes;
-  }
-
-  public static boolean exec(JProgram program, boolean noSpecialTypes) {
-    return new Pruner(program, noSpecialTypes).execImpl();
-  }
-
 }
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 ba7a5e3..e54a836 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.Holder;
@@ -14,17 +28,15 @@
 import com.google.gwt.dev.jjs.ast.change.ChangeList;
 
 /**
- * Replaces any "GWT.create()" calls with a new expression for the actual
- * result of the deferred binding decision.
+ * Replaces any "GWT.create()" calls with a new expression for the actual result
+ * of the deferred binding decision.
  */
 public class ReplaceRebinds {
 
-  private final JProgram program;
-
   private class RebindVisitor extends JVisitor {
 
     private final ChangeList changeList = new ChangeList(
-      "Replace GWT.create() with new expressions.");
+        "Replace GWT.create() with new expressions.");
 
     // @Override
     public void endVisit(JMethodCall x, Mutator mutator) {
@@ -63,6 +75,16 @@
     }
   }
 
+  public static boolean exec(JProgram program) {
+    return new ReplaceRebinds(program).execImpl();
+  }
+
+  private final JProgram program;
+
+  private ReplaceRebinds(JProgram program) {
+    this.program = program;
+  }
+
   private boolean execImpl() {
     RebindVisitor rebinder = new RebindVisitor();
     program.traverse(rebinder);
@@ -74,12 +96,4 @@
     return true;
   }
 
-  private ReplaceRebinds(JProgram program) {
-    this.program = program;
-  }
-
-  public static boolean exec(JProgram program) {
-    return new ReplaceRebinds(program).execImpl();
-  }
-
 }
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 55f4ccb..4d07ac4 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.CanBeFinal;
@@ -121,6 +135,23 @@
   }
 
   // @Override
+  public boolean visit(JProgram x) {
+    for (int i = 0; i < x.entryMethods.size(); ++i) {
+      JMethod method = (JMethod) x.entryMethods.get(i);
+      method.traverse(this);
+      newline();
+      newline();
+    }
+    for (int i = 0; i < x.getDeclaredTypes().size(); ++i) {
+      JReferenceType type = (JReferenceType) x.getDeclaredTypes().get(i);
+      type.traverse(this);
+      newline();
+      newline();
+    }
+    return false;
+  }
+
+  // @Override
   public boolean visit(JsniMethod x) {
     super.visit(x);
     space();
@@ -140,20 +171,8 @@
   }
 
   // @Override
-  public boolean visit(JProgram x) {
-    for (int i = 0; i < x.entryMethods.size(); ++i) {
-      JMethod method = (JMethod) x.entryMethods.get(i);
-      method.traverse(this);
-      newline();
-      newline();
-    }
-    for (int i = 0; i < x.getDeclaredTypes().size(); ++i) {
-      JReferenceType type = (JReferenceType) x.getDeclaredTypes().get(i);
-      type.traverse(this);
-      newline();
-      newline();
-    }
-    return false;
+  protected void printMemberFinalFlag(CanBeFinal x) {
+    // suppress final flags
   }
 
   // @Override
@@ -165,17 +184,12 @@
     }
   }
 
-  // @Override
-  protected void printMemberFinalFlag(CanBeFinal x) {
-    // suppress final flags
+  private boolean isEmptyInitializer(JMethod x) {
+    return isInitializer(x) && (x.body.statements.size() == 0);
   }
 
   private boolean isInitializer(JMethod x) {
     return x.getName().equals("$clinit") || x.getName().equals("$init");
   }
 
-  private boolean isEmptyInitializer(JMethod x) {
-    return isInitializer(x) && (x.body.statements.size() == 0);
-  }
-
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TextOutputVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TextOutputVisitor.java
index 34b393c..68a5a56 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TextOutputVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TextOutputVisitor.java
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JVisitor;
@@ -6,49 +20,49 @@
 
 public class TextOutputVisitor extends JVisitor implements TextOutput {
 
-    private final TextOutput fTextOutput;
+  private final TextOutput textOutput;
 
-    public TextOutputVisitor(TextOutput textOutput) {
-        fTextOutput = textOutput;
-    }
+  public TextOutputVisitor(TextOutput textOutput) {
+    this.textOutput = textOutput;
+  }
 
-    public void indentIn() {
-        fTextOutput.indentIn();
-    }
+  public void indentIn() {
+    textOutput.indentIn();
+  }
 
-    public void indentOut() {
-        fTextOutput.indentOut();
-    }
+  public void indentOut() {
+    textOutput.indentOut();
+  }
 
-    public void newline() {
-        fTextOutput.newline();
-    }
+  public void newline() {
+    textOutput.newline();
+  }
 
-    public void newlineOpt() {
-        fTextOutput.newlineOpt();
-    }
+  public void newlineOpt() {
+    textOutput.newlineOpt();
+  }
 
-    public void print(char c) {
-        fTextOutput.print(c);
-    }
+  public void print(char c) {
+    textOutput.print(c);
+  }
 
-    public void print(char[] s) {
-        fTextOutput.print(s);
-    }
+  public void print(char[] s) {
+    textOutput.print(s);
+  }
 
-    public void print(String s) {
-        fTextOutput.print(s);
-    }
+  public void print(String s) {
+    textOutput.print(s);
+  }
 
-    public void printOpt(char c) {
-        fTextOutput.printOpt(c);
-    }
+  public void printOpt(char c) {
+    textOutput.printOpt(c);
+  }
 
-    public void printOpt(char[] s) {
-        fTextOutput.printOpt(s);
-    }
+  public void printOpt(char[] s) {
+    textOutput.printOpt(s);
+  }
 
-    public void printOpt(String s) {
-        fTextOutput.printOpt(s);
-    }
+  public void printOpt(String s) {
+    textOutput.printOpt(s);
+  }
 }
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 70c2265..dd3028d 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.CanBeAbstract;
@@ -124,6 +138,10 @@
   protected static final char[] CHARS_TRY = "try ".toCharArray();
   protected static final char[] CHARS_WHILE = "while ".toCharArray();
 
+  private boolean needSemi = true;
+
+  private boolean suppressType = false;
+
   public ToStringGenerationVisitor(TextOutput textOutput) {
     super(textOutput);
   }
@@ -191,15 +209,15 @@
     openBlock();
     for (int i = 0; i < x.statements.size(); ++i) {
       JStatement statement = (JStatement) x.statements.get(i);
-      fNeedSemi = true;
+      needSemi = true;
       statement.traverse(this);
-      if (fNeedSemi) {
+      if (needSemi) {
         semi();
       }
       newline();
     }
     closeBlock();
-    fNeedSemi = false;
+    needSemi = false;
     return false;
   }
 
@@ -229,7 +247,7 @@
     }
     print(':');
     space();
-    fNeedSemi = false;
+    needSemi = false;
     return false;
   }
 
@@ -330,12 +348,12 @@
       x.body.traverse(this);
       nestedStatementPop(x.body);
     }
-    if (fNeedSemi) {
+    if (needSemi) {
       semi();
       newline();
     } else {
       space();
-      fNeedSemi = true;
+      needSemi = true;
     }
     print(CHARS_WHILE);
     lparen();
@@ -357,11 +375,6 @@
   }
 
   // @Override
-  public boolean visit(JsniFieldRef x) {
-    return visit(x.getField());
-  }
-
-  // @Override
   public boolean visit(JField x) {
     // Due to our wacky construction model, only constant fields may be final
     // when generating source
@@ -370,7 +383,7 @@
     } else {
       printMemberFinalFlag(x);
     }
-    
+
     printStaticFlag(x);
     printType(x);
     space();
@@ -409,20 +422,20 @@
       JStatement stmt = (JStatement) iter.next();
       stmt.traverse(this);
     }
-    fSuppressType = true;
+    suppressType = true;
     while (iter.hasNext()) {
       print(CHARS_COMMA);
       JStatement stmt = (JStatement) iter.next();
       stmt.traverse(this);
     }
-    fSuppressType = false;
+    suppressType = false;
 
     semi();
     space();
     if (x.getTestExpr() != null) {
       x.getTestExpr().traverse(this);
     }
-    
+
     semi();
     space();
     visitCollectionWithCommas(x.getIncrements().iterator());
@@ -450,12 +463,12 @@
     }
 
     if (x.elseStmt != null) {
-      if (fNeedSemi) {
+      if (needSemi) {
         semi();
         newline();
       } else {
         space();
-        fNeedSemi = true;
+        needSemi = true;
       }
       print(CHARS_ELSE);
       boolean elseIf = x.elseStmt instanceof JIfStatement;
@@ -535,7 +548,7 @@
 
   // @Override
   public boolean visit(JLocalDeclarationStatement x) {
-    if (!fSuppressType) {
+    if (!suppressType) {
       x.getLocalRef().getTarget().traverse(this);
     } else {
       x.getLocalRef().traverse(this);
@@ -597,16 +610,6 @@
   }
 
   // @Override
-  public boolean visit(JsniMethod x) {
-    return printMethodHeader(x);
-  }
-
-  // @Override
-  public boolean visit(JsniMethodRef x) {
-    return printMethodHeader(x.getTarget());
-  }
-
-  // @Override
   public boolean visit(JNewArray x, Mutator h) {
     print(CHARS_NEW);
     printTypeName(x.getArrayType().leafType);
@@ -705,6 +708,21 @@
   }
 
   // @Override
+  public boolean visit(JsniFieldRef x) {
+    return visit(x.getField());
+  }
+
+  // @Override
+  public boolean visit(JsniMethod x) {
+    return printMethodHeader(x);
+  }
+
+  // @Override
+  public boolean visit(JsniMethodRef x) {
+    return printMethodHeader(x.getTarget());
+  }
+
+  // @Override
   public boolean visit(JsonArray x, Mutator m) {
     print('[');
     visitCollectionWithCommas(x.exprs.iterator());
@@ -902,11 +920,11 @@
           if (c < 0x1000) {
             print('0');
           }
-          
+
           if (c < 0x100) {
             print('0');
           }
-          
+
           if (c < 0x10) {
             print('0');
           }
@@ -933,6 +951,16 @@
     }
   }
 
+  protected void printFloatLiteral(float value) {
+    print(Float.toString(value));
+    print('f');
+  }
+
+  protected void printLongLiteral(long value) {
+    print(Long.toString(value));
+    print('L');
+  }
+
   protected void printMemberFinalFlag(CanBeFinal x) {
     if (x.isFinal()) {
       print(CHARS_FINAL);
@@ -957,7 +985,7 @@
     } else {
       printName(x);
     }
-    
+
     // Parameters
     printParameterList(x);
 
@@ -975,16 +1003,6 @@
     return false;
   }
 
-  protected void printFloatLiteral(float value) {
-    print(Float.toString(value));
-    print('f');
-  }
-
-  protected void printLongLiteral(long value) {
-    print(Long.toString(value));
-    print('L');
-  }
-
   protected void printName(HasName x) {
     print(x.getName());
   }
@@ -1020,6 +1038,14 @@
     printTypeName(hasType.getType());
   }
 
+  protected void printTypeName(JType type) {
+    if (type instanceof JReferenceType) {
+      print(((JReferenceType) type).getShortName());
+    } else {
+      print(type.getName());
+    }
+  }
+
   protected void rparen() {
     print(')');
   }
@@ -1044,20 +1070,8 @@
     }
   }
 
-  protected void printTypeName(JType type) {
-    if (type instanceof JReferenceType) {
-      print(((JReferenceType) type).getShortName());
-    } else {
-      print(type.getName());
-    }
-  }
-
   private void printUniqueName(HasName x) {
     print(x.getName());
   }
 
-  private boolean fNeedSemi = true;
-
-  private boolean fSuppressType = false;
-
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeMap.java
index 3057730..f5d2607 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeMap.java
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JArrayType;
@@ -18,8 +32,18 @@
  */
 public class TypeMap {
 
+  /**
+   * Maps Eclipse AST nodes to our JNodes.
+   */
+  private final Map/* <Binding, JNode> */crossRefMap = new IdentityHashMap();
+
+  /**
+   * Centralizes creation and singleton management.
+   */
+  private final JProgram program;
+
   public TypeMap(JProgram program) {
-    fProgram = program;
+    this.program = program;
   }
 
   public JNode get(Binding binding) {
@@ -31,15 +55,15 @@
   }
 
   public JProgram getProgram() {
-    return fProgram;
+    return program;
   }
 
   public void put(Binding binding, JNode to) {
     if (binding == null) {
       throw new InternalCompilerException("Trying to put null into typeMap.");
     }
-    
-    Object old = fCrossRefMap.put(binding, to);
+
+    Object old = crossRefMap.put(binding, to);
     assert (old == null);
   }
 
@@ -48,7 +72,7 @@
   }
 
   private JNode internalGet(Binding binding) {
-    JNode cached = (JNode) fCrossRefMap.get(binding);
+    JNode cached = (JNode) crossRefMap.get(binding);
     if (cached != null) {
       // Already seen this one.
       return cached;
@@ -56,23 +80,23 @@
       BaseTypeBinding baseTypeBinding = (BaseTypeBinding) binding;
       switch (baseTypeBinding.id) {
         case BaseTypeBinding.T_void:
-          return fProgram.getTypeVoid();
+          return program.getTypeVoid();
         case BaseTypeBinding.T_boolean:
-          return fProgram.getTypePrimitiveBoolean();
+          return program.getTypePrimitiveBoolean();
         case BaseTypeBinding.T_char:
-          return fProgram.getTypePrimitiveChar();
+          return program.getTypePrimitiveChar();
         case BaseTypeBinding.T_byte:
-          return fProgram.getTypePrimitiveByte();
+          return program.getTypePrimitiveByte();
         case BaseTypeBinding.T_short:
-          return fProgram.getTypePrimitiveShort();
+          return program.getTypePrimitiveShort();
         case BaseTypeBinding.T_int:
-          return fProgram.getTypePrimitiveInt();
+          return program.getTypePrimitiveInt();
         case BaseTypeBinding.T_long:
-          return fProgram.getTypePrimitiveLong();
+          return program.getTypePrimitiveLong();
         case BaseTypeBinding.T_float:
-          return fProgram.getTypePrimitiveFloat();
+          return program.getTypePrimitiveFloat();
         case BaseTypeBinding.T_double:
-          return fProgram.getTypePrimitiveDouble();
+          return program.getTypePrimitiveDouble();
       }
     } else if (binding instanceof ArrayBinding) {
       ArrayBinding arrayBinding = (ArrayBinding) binding;
@@ -82,22 +106,12 @@
 
       // Don't create a new JArrayType; use TypeMap to get the singleton
       // instance
-      JArrayType arrayType = fProgram.getTypeArray(leafType,
-        arrayBinding.dimensions);
+      JArrayType arrayType = program.getTypeArray(leafType,
+          arrayBinding.dimensions);
 
       return arrayType;
     }
     return null;
   }
 
-  /**
-   * Maps Eclipse AST nodes to our JNodes.
-   */
-  private final Map/* <Binding, JNode> */fCrossRefMap = new IdentityHashMap();
-
-  /**
-   * Centralizes creation and singleton management.
-   */
-  private final JProgram fProgram;
-
 }
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 dc0e9a1..633c3c9 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
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * 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.JArrayRef;
@@ -72,391 +86,12 @@
  */
 public class TypeTightener {
 
-  private final JProgram program;
-  private final JNullType typeNull;
-
-  private final Map/* <JReferenceType, Set<JClassType>> */implementors = new IdentityHashMap();
-  private final Map/* <JMethod, Set<JExpression>> */returns = new IdentityHashMap();
-  private final Map/* <JMethod, Set<JMethod>> */overriders = new IdentityHashMap();
-  private final Map/* <JVariable, Set<JExpression>> */assignments = new IdentityHashMap();
-  private final Map/* <JParameter, Set<JParameter>> */paramUpRefs = new IdentityHashMap();
-
-  private static/* <T, V> */void add(Object target, Object value,
-      Map/* <T, Set<V>> */map) {
-    Set/* <V> */set = (Set) map.get(target);
-    if (set == null) {
-      set = new HashSet/* <V> */();
-      map.put(target, set);
-    }
-    set.add(value);
-  }
-
-  /**
-   * Record "type flow" information. Variables receive type flow via assignment.
-   * As a special case, Parameters also receive type flow based on the types of
-   * arguments used when calling the containing method (think of this as a kind
-   * of assignment). Method return types receive type flow from their contained
-   * return statements, plus the return type of any methods that
-   * override/implement them.
-   * 
-   * Note that we only have to run this pass ONCE to record the relationships,
-   * because type tightening never changes any relationships, only the types of
-   * the things related. In my original implementation, I had naively mapped
-   * nodes onto sets of JReferenceType directly, which meant I had to rerun this
-   * visitor each time.
-   */
-  public class RecordVisitor extends JVisitor {
-
-    private JMethod fCurrentMethod;
-
-    private void addAssignment(JVariable target, JExpression rhs) {
-      add(target, rhs, assignments);
-    }
-
-    private void addImplementor(JReferenceType target, JClassType implementor) {
-      add(target, implementor, implementors);
-    }
-
-    private void addOverrider(JMethod target, JMethod overrider) {
-      add(target, overrider, overriders);
-    }
-
-    private void addReturn(JMethod target, JExpression expr) {
-      add(target, expr, returns);
-    }
-
-    // @Override
-    public void endVisit(JBinaryOperation x, Mutator m) {
-      if (x.isAssignment() && (x.getType() instanceof JReferenceType)) {
-        JExpression lhs = x.getLhs();
-        if (lhs instanceof JVariableRef) {
-          addAssignment(((JVariableRef) lhs).getTarget(), x.getRhs());
-        }
-      }
-    }
-
-    // @Override
-    public void endVisit(JClassType x) {
-      for (JClassType cur = x; cur != null; cur = cur.extnds) {
-        addImplementor(cur, x);
-        for (Iterator it = cur.implments.iterator(); it.hasNext();) {
-          JInterfaceType implment = (JInterfaceType) it.next();
-          addImplementor(implment, x);
-        }
-      }
-    }
-
-    // @Override
-    public void endVisit(JField x) {
-      if (x.constInitializer != null) {
-        addAssignment(x, x.constInitializer);
-      }
-      fCurrentMethod = null;
-    }
-
-    // @Override
-    public void endVisit(JLocalDeclarationStatement x) {
-      JExpression initializer = x.getInitializer();
-      if (initializer != null) {
-        addAssignment(x.getLocalRef().getTarget(), initializer);
-      }
-    }
-
-    // @Override
-    public void endVisit(JMethod x) {
-      for (int i = 0; i < x.overrides.size(); ++i) {
-        JMethod method = (JMethod) x.overrides.get(i);
-        addOverrider(method, x);
-      }
-      JMethod[] allVirtualOverrides = program.typeOracle.getAllVirtualOverrides(x);
-      for (int i = 0; i < allVirtualOverrides.length; ++i) {
-        JMethod method = allVirtualOverrides[i];
-        addOverrider(method, x);
-      }
-      fCurrentMethod = null;
-    }
-
-    // @Override
-    public void endVisit(JMethodCall x, Mutator m) {
-      // 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();
-      ArrayList params = x.getTarget().params;
-      for (int i = 0; i < params.size(); ++i) {
-        JParameter param = (JParameter) params.get(i);
-        JExpression arg = (JExpression) argIt.next();
-        if (param.getType() instanceof JReferenceType) {
-          addAssignment(param, arg);
-        }
-      }
-    }
-
-    // @Override
-    public void endVisit(JsniMethod x) {
-      endVisit((JMethod) x);
-    }
-
-    // @Override
-    public void endVisit(JReturnStatement x) {
-      if (fCurrentMethod.getType() instanceof JReferenceType) {
-        addReturn(fCurrentMethod, x.getExpression());
-      }
-    }
-
-    // @Override
-    public void endVisit(JsniFieldRef x) {
-      // 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(JsniMethodRef x) {
-      // If this happens in JSNI, we can't make any type-tightening assumptions
-      // Fake an assignment-to-self on all args to prevent tightening
-
-      JMethod method = x.getTarget();
-
-      for (int i = 0; i < method.params.size(); ++i) {
-        JParameter param = (JParameter) method.params.get(i);
-        addAssignment(param, new JParameterRef(program, param));
-      }
-    }
-
-    // @Override
-    public void endVisit(JTryStatement x) {
-      // 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);
-        addAssignment(arg.getTarget(), arg);
-      }
-    }
-
-    /**
-     * Merge param call args across overriders/implementors. We can't tighten a
-     * param type in an overriding method if the declaring method is looser.
-     */
-    // @Override
-    public boolean visit(JMethod x) {
-      fCurrentMethod = x;
-
-      List/* <JMethod> */overrides = x.overrides;
-      JMethod[] virtualOverrides = program.typeOracle.getAllVirtualOverrides(x);
-
-      if (overrides.isEmpty() && virtualOverrides.length == 0) {
-        return true;
-      }
-
-      for (int j = 0, c = x.params.size(); j < c; ++j) {
-        JParameter param = (JParameter) x.params.get(j);
-        Set/* <JParameter> */set = (Set) paramUpRefs.get(param);
-        if (set == null) {
-          set = new HashSet/* <JParameter> */();
-          paramUpRefs.put(param, set);
-        }
-        for (int i = 0; i < overrides.size(); ++i) {
-          JMethod baseMethod = (JMethod) overrides.get(i);
-          JParameter baseParam = (JParameter) baseMethod.params.get(j);
-          set.add(baseParam);
-        }
-        for (int i = 0; i < virtualOverrides.length; ++i) {
-          JMethod baseMethod = virtualOverrides[i];
-          JParameter baseParam = (JParameter) baseMethod.params.get(j);
-          set.add(baseParam);
-        }
-      }
-
-      return true;
-    }
-  }
-
-  /**
-   * Wherever possible, use the type flow information recorded by RecordVisitor
-   * to change the declared type of a field, local, parameter, or method to a
-   * more specific type.
-   * 
-   * We must iterate mutiple times because each way point we tighten creates
-   * more opportunities to do additional tightening for the things that depend
-   * on it.
-   */
-  public class TightenTypesVisitor extends JVisitor {
-    private final ChangeList changeList = new ChangeList(
-      "Tighten types on fields, locals, params, methods.");
-
-    public ChangeList getChangeList() {
-      return changeList;
-    }
-
-    /**
-     * Tighten based on assignment, and for parameters, callArgs as well.
-     */
-    private void tighten(JVariable x) {
-      if (!(x.getType() instanceof JReferenceType)) {
-        return;
-      }
-      JReferenceType refType = (JReferenceType) x.getType();
-
-      if (refType == typeNull) {
-        return;
-      }
-
-      // tighten based on non-instantiability
-      if (!program.typeOracle.isInstantiatedType(refType)) {
-        changeList.changeType(x, typeNull);
-        return;
-      }
-
-      // tighten based on assignment
-      List/* <JReferenceType> */typeList = new ArrayList/* <JReferenceType> */();
-
-      /*
-       * Always assume at least one null assignment; if there really aren't any
-       * other assignments, then this variable will get the null type. If there
-       * are, it won't hurt anything because null type will always lose.
-       */
-      typeList.add(typeNull);
-
-      Set/* <JExpression> */myAssignments = (Set) assignments.get(x);
-      if (myAssignments != null) {
-        for (Iterator iter = myAssignments.iterator(); iter.hasNext();) {
-          JExpression expr = (JExpression) iter.next();
-          JType type = expr.getType();
-          if (!(type instanceof JReferenceType)) {
-            return; // something fishy is going on, just abort
-          }
-          typeList.add(type);
-        }
-      }
-
-      if (x instanceof JParameter) {
-        Set/* <JParameter> */myParams = (Set) paramUpRefs.get(x);
-        if (myParams != null) {
-          for (Iterator iter = myParams.iterator(); iter.hasNext();) {
-            JParameter param = (JParameter) iter.next();
-            typeList.add(param.getType());
-          }
-        }
-      }
-
-      JReferenceType resultType = program.generalizeTypes(typeList);
-      resultType = program.strongerType(refType, resultType);
-      if (refType != resultType) {
-        changeList.changeType(x, resultType);
-      }
-    }
-
-    // @Override
-    public void endVisit(JField x) {
-      tighten(x);
-    }
-
-    // @Override
-    public void endVisit(JLocal x) {
-      tighten(x);
-    }
-
-    // @Override
-    public void endVisit(JNewArray x, Mutator m) {
-      // 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);
-        }
-      }
-    }
-
-    // @Override
-    public void endVisit(JParameter x) {
-      tighten(x);
-    }
-
-    // @Override
-    public boolean visit(JClassType x) {
-      // don't mess with classes used in code gen
-      if (program.specialTypes.contains(x)) {
-        return false;
-      }
-      return true;
-    }
-
-    /**
-     * Tighten based on return types and overrides.
-     */
-    // @Override
-    public void endVisit(JMethod x) {
-
-      if (!(x.getType() instanceof JReferenceType)) {
-        return;
-      }
-      JReferenceType refType = (JReferenceType) x.getType();
-
-      if (refType == typeNull) {
-        return;
-      }
-
-      // tighten based on non-instantiability
-      if (!program.typeOracle.isInstantiatedType(refType)) {
-        changeList.changeType(x, typeNull);
-        return;
-      }
-
-      // tighten based on both returned types and possible overrides
-      List/* <JReferenceType> */typeList = new ArrayList/* <JReferenceType> */();
-
-      /*
-       * Always assume at least one null assignment; if there really aren't any
-       * other assignments, then this variable will get the null type. If there
-       * are, it won't hurt anything because null type will always lose.
-       */
-      typeList.add(typeNull);
-
-      Set/* <JExpression> */myReturns = (Set) returns.get(x);
-      if (myReturns != null) {
-        for (Iterator iter = myReturns.iterator(); iter.hasNext();) {
-          JExpression expr = (JExpression) iter.next();
-          typeList.add(expr.getType());
-        }
-      }
-      Set/* <JMethod> */myOverriders = (Set) overriders.get(x);
-      if (myOverriders != null) {
-        for (Iterator iter = myOverriders.iterator(); iter.hasNext();) {
-          JMethod method = (JMethod) iter.next();
-          typeList.add(method.getType());
-        }
-      }
-
-      JReferenceType resultType = program.generalizeTypes(typeList);
-      resultType = program.strongerType(refType, resultType);
-      if (refType != resultType) {
-        changeList.changeType(x, resultType);
-      }
-    }
-
-    public boolean visit(JsniMethod x) {
-      /*
-       * Explicitly NOT visiting native methods since we can't infer type
-       * information.
-       * 
-       * TODO(later): can we figure out simple pass-through info?
-       */
-      return false;
-    }
-  }
-
   /**
    * 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 ChangeList getChangeList() {
-      return changeList;
-    }
+        "Replace dangling null references with dummy calls.");
 
     // @Override
     public void endVisit(JArrayRef x, Mutator m) {
@@ -521,7 +156,393 @@
         changeList.replaceExpression(m, call);
       }
     }
+
+    public ChangeList getChangeList() {
+      return changeList;
+    }
   }
+  /**
+   * Record "type flow" information. Variables receive type flow via assignment.
+   * As a special case, Parameters also receive type flow based on the types of
+   * arguments used when calling the containing method (think of this as a kind
+   * of assignment). Method return types receive type flow from their contained
+   * return statements, plus the return type of any methods that
+   * override/implement them.
+   * 
+   * Note that we only have to run this pass ONCE to record the relationships,
+   * because type tightening never changes any relationships, only the types of
+   * the things related. In my original implementation, I had naively mapped
+   * nodes onto sets of JReferenceType directly, which meant I had to rerun this
+   * visitor each time.
+   */
+  public class RecordVisitor extends JVisitor {
+
+    private JMethod currentMethod;
+
+    // @Override
+    public void endVisit(JBinaryOperation x, Mutator m) {
+      if (x.isAssignment() && (x.getType() instanceof JReferenceType)) {
+        JExpression lhs = x.getLhs();
+        if (lhs instanceof JVariableRef) {
+          addAssignment(((JVariableRef) lhs).getTarget(), x.getRhs());
+        }
+      }
+    }
+
+    // @Override
+    public void endVisit(JClassType x) {
+      for (JClassType cur = x; cur != null; cur = cur.extnds) {
+        addImplementor(cur, x);
+        for (Iterator it = cur.implments.iterator(); it.hasNext();) {
+          JInterfaceType implment = (JInterfaceType) it.next();
+          addImplementor(implment, x);
+        }
+      }
+    }
+
+    // @Override
+    public void endVisit(JField x) {
+      if (x.constInitializer != null) {
+        addAssignment(x, x.constInitializer);
+      }
+      currentMethod = null;
+    }
+
+    // @Override
+    public void endVisit(JLocalDeclarationStatement x) {
+      JExpression initializer = x.getInitializer();
+      if (initializer != null) {
+        addAssignment(x.getLocalRef().getTarget(), initializer);
+      }
+    }
+
+    // @Override
+    public void endVisit(JMethod x) {
+      for (int i = 0; i < x.overrides.size(); ++i) {
+        JMethod method = (JMethod) x.overrides.get(i);
+        addOverrider(method, x);
+      }
+      JMethod[] allVirtualOverrides = program.typeOracle.getAllVirtualOverrides(x);
+      for (int i = 0; i < allVirtualOverrides.length; ++i) {
+        JMethod method = allVirtualOverrides[i];
+        addOverrider(method, x);
+      }
+      currentMethod = null;
+    }
+
+    // @Override
+    public void endVisit(JMethodCall x, Mutator m) {
+      // 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();
+      ArrayList params = x.getTarget().params;
+      for (int i = 0; i < params.size(); ++i) {
+        JParameter param = (JParameter) params.get(i);
+        JExpression arg = (JExpression) argIt.next();
+        if (param.getType() instanceof JReferenceType) {
+          addAssignment(param, arg);
+        }
+      }
+    }
+
+    // @Override
+    public void endVisit(JReturnStatement x) {
+      if (currentMethod.getType() instanceof JReferenceType) {
+        addReturn(currentMethod, x.getExpression());
+      }
+    }
+
+    // @Override
+    public void endVisit(JsniFieldRef x) {
+      // 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);
+    }
+
+    // @Override
+    public void endVisit(JsniMethodRef x) {
+      // If this happens in JSNI, we can't make any type-tightening assumptions
+      // Fake an assignment-to-self on all args to prevent tightening
+
+      JMethod method = x.getTarget();
+
+      for (int i = 0; i < method.params.size(); ++i) {
+        JParameter param = (JParameter) method.params.get(i);
+        addAssignment(param, new JParameterRef(program, param));
+      }
+    }
+
+    // @Override
+    public void endVisit(JTryStatement x) {
+      // 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);
+        addAssignment(arg.getTarget(), arg);
+      }
+    }
+
+    /**
+     * Merge param call args across overriders/implementors. We can't tighten a
+     * param type in an overriding method if the declaring method is looser.
+     */
+    // @Override
+    public boolean visit(JMethod x) {
+      currentMethod = x;
+
+      List/* <JMethod> */overrides = x.overrides;
+      JMethod[] virtualOverrides = program.typeOracle.getAllVirtualOverrides(x);
+
+      if (overrides.isEmpty() && virtualOverrides.length == 0) {
+        return true;
+      }
+
+      for (int j = 0, c = x.params.size(); j < c; ++j) {
+        JParameter param = (JParameter) x.params.get(j);
+        Set/* <JParameter> */set = (Set) paramUpRefs.get(param);
+        if (set == null) {
+          set = new HashSet/* <JParameter> */();
+          paramUpRefs.put(param, set);
+        }
+        for (int i = 0; i < overrides.size(); ++i) {
+          JMethod baseMethod = (JMethod) overrides.get(i);
+          JParameter baseParam = (JParameter) baseMethod.params.get(j);
+          set.add(baseParam);
+        }
+        for (int i = 0; i < virtualOverrides.length; ++i) {
+          JMethod baseMethod = virtualOverrides[i];
+          JParameter baseParam = (JParameter) baseMethod.params.get(j);
+          set.add(baseParam);
+        }
+      }
+
+      return true;
+    }
+
+    private void addAssignment(JVariable target, JExpression rhs) {
+      add(target, rhs, assignments);
+    }
+
+    private void addImplementor(JReferenceType target, JClassType implementor) {
+      add(target, implementor, implementors);
+    }
+
+    private void addOverrider(JMethod target, JMethod overrider) {
+      add(target, overrider, overriders);
+    }
+
+    private void addReturn(JMethod target, JExpression expr) {
+      add(target, expr, returns);
+    }
+  }
+
+  /**
+   * Wherever possible, use the type flow information recorded by RecordVisitor
+   * to change the declared type of a field, local, parameter, or method to a
+   * more specific type.
+   * 
+   * We must iterate mutiple times because each way point we tighten creates
+   * more opportunities to do additional tightening for the things that depend
+   * on it.
+   */
+  public class TightenTypesVisitor extends JVisitor {
+    private final ChangeList changeList = new ChangeList(
+        "Tighten types on fields, locals, params, methods.");
+
+    // @Override
+    public void endVisit(JField x) {
+      tighten(x);
+    }
+
+    // @Override
+    public void endVisit(JLocal x) {
+      tighten(x);
+    }
+
+    /**
+     * Tighten based on return types and overrides.
+     */
+    // @Override
+    public void endVisit(JMethod x) {
+
+      if (!(x.getType() instanceof JReferenceType)) {
+        return;
+      }
+      JReferenceType refType = (JReferenceType) x.getType();
+
+      if (refType == typeNull) {
+        return;
+      }
+
+      // tighten based on non-instantiability
+      if (!program.typeOracle.isInstantiatedType(refType)) {
+        changeList.changeType(x, typeNull);
+        return;
+      }
+
+      // tighten based on both returned types and possible overrides
+      List/* <JReferenceType> */typeList = new ArrayList/* <JReferenceType> */();
+
+      /*
+       * Always assume at least one null assignment; if there really aren't any
+       * other assignments, then this variable will get the null type. If there
+       * are, it won't hurt anything because null type will always lose.
+       */
+      typeList.add(typeNull);
+
+      Set/* <JExpression> */myReturns = (Set) returns.get(x);
+      if (myReturns != null) {
+        for (Iterator iter = myReturns.iterator(); iter.hasNext();) {
+          JExpression expr = (JExpression) iter.next();
+          typeList.add(expr.getType());
+        }
+      }
+      Set/* <JMethod> */myOverriders = (Set) overriders.get(x);
+      if (myOverriders != null) {
+        for (Iterator iter = myOverriders.iterator(); iter.hasNext();) {
+          JMethod method = (JMethod) iter.next();
+          typeList.add(method.getType());
+        }
+      }
+
+      JReferenceType resultType = program.generalizeTypes(typeList);
+      resultType = program.strongerType(refType, resultType);
+      if (refType != resultType) {
+        changeList.changeType(x, resultType);
+      }
+    }
+
+    // @Override
+    public void endVisit(JNewArray x, Mutator m) {
+      // 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);
+        }
+      }
+    }
+
+    // @Override
+    public void endVisit(JParameter x) {
+      tighten(x);
+    }
+
+    public ChangeList getChangeList() {
+      return changeList;
+    }
+
+    // @Override
+    public boolean visit(JClassType x) {
+      // don't mess with classes used in code gen
+      if (program.specialTypes.contains(x)) {
+        return false;
+      }
+      return true;
+    }
+
+    public boolean visit(JsniMethod x) {
+      /*
+       * Explicitly NOT visiting native methods since we can't infer type
+       * information.
+       * 
+       * TODO(later): can we figure out simple pass-through info?
+       */
+      return false;
+    }
+
+    /**
+     * Tighten based on assignment, and for parameters, callArgs as well.
+     */
+    private void tighten(JVariable x) {
+      if (!(x.getType() instanceof JReferenceType)) {
+        return;
+      }
+      JReferenceType refType = (JReferenceType) x.getType();
+
+      if (refType == typeNull) {
+        return;
+      }
+
+      // tighten based on non-instantiability
+      if (!program.typeOracle.isInstantiatedType(refType)) {
+        changeList.changeType(x, typeNull);
+        return;
+      }
+
+      // tighten based on assignment
+      List/* <JReferenceType> */typeList = new ArrayList/* <JReferenceType> */();
+
+      /*
+       * Always assume at least one null assignment; if there really aren't any
+       * other assignments, then this variable will get the null type. If there
+       * are, it won't hurt anything because null type will always lose.
+       */
+      typeList.add(typeNull);
+
+      Set/* <JExpression> */myAssignments = (Set) assignments.get(x);
+      if (myAssignments != null) {
+        for (Iterator iter = myAssignments.iterator(); iter.hasNext();) {
+          JExpression expr = (JExpression) iter.next();
+          JType type = expr.getType();
+          if (!(type instanceof JReferenceType)) {
+            return; // something fishy is going on, just abort
+          }
+          typeList.add(type);
+        }
+      }
+
+      if (x instanceof JParameter) {
+        Set/* <JParameter> */myParams = (Set) paramUpRefs.get(x);
+        if (myParams != null) {
+          for (Iterator iter = myParams.iterator(); iter.hasNext();) {
+            JParameter param = (JParameter) iter.next();
+            typeList.add(param.getType());
+          }
+        }
+      }
+
+      JReferenceType resultType = program.generalizeTypes(typeList);
+      resultType = program.strongerType(refType, resultType);
+      if (refType != resultType) {
+        changeList.changeType(x, resultType);
+      }
+    }
+  }
+
+  public static boolean exec(JProgram program) {
+    return new TypeTightener(program).execImpl();
+  }
+
+  private static/* <T, V> */void add(Object target, Object value,
+      Map/* <T, Set<V>> */map) {
+    Set/* <V> */set = (Set) map.get(target);
+    if (set == null) {
+      set = new HashSet/* <V> */();
+      map.put(target, set);
+    }
+    set.add(value);
+  }
+
+  private final JProgram program;
+  private final JNullType typeNull;
+
+  private final Map/* <JReferenceType, Set<JClassType>> */implementors = new IdentityHashMap();
+
+  private final Map/* <JMethod, Set<JExpression>> */returns = new IdentityHashMap();
+
+  private final Map/* <JMethod, Set<JMethod>> */overriders = new IdentityHashMap();
+
+  private final Map/* <JVariable, Set<JExpression>> */assignments = new IdentityHashMap();
+
+  private final Map/* <JParameter, Set<JParameter>> */paramUpRefs = new IdentityHashMap();
 
   private TypeTightener(JProgram program) {
     this.program = program;
@@ -551,8 +572,4 @@
     }
   }
 
-  public static boolean exec(JProgram program) {
-    return new TypeTightener(program).execImpl();
-  }
-
 }