Now that the file/line information and new visitor pattern is in, it's
time to leverage the infrastructure!  This change adds try/catch blocks
into the major compiler visitors (both JDT and our own AST) which record a
stack of nodes being visited, much like a stack trace.  When the ICE is
caught by the main compile method, a log message points users to the
portion of their code that triggered the fault.

Review by: mmendez



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@392 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 262ca4d..51988d1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -30,6 +30,7 @@
 import com.google.gwt.dev.jjs.ast.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 import com.google.gwt.dev.jjs.impl.ArrayNormalizer;
 import com.google.gwt.dev.jjs.impl.BuildTypeMap;
 import com.google.gwt.dev.jjs.impl.CastNormalizer;
@@ -39,6 +40,7 @@
 import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
 import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
 import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
+import com.google.gwt.dev.jjs.impl.InternalCompilerException;
 import com.google.gwt.dev.jjs.impl.JavaScriptObjectCaster;
 import com.google.gwt.dev.jjs.impl.MakeCallsStatic;
 import com.google.gwt.dev.jjs.impl.MethodAndClassFinalizer;
@@ -48,6 +50,7 @@
 import com.google.gwt.dev.jjs.impl.ReplaceRebinds;
 import com.google.gwt.dev.jjs.impl.TypeMap;
 import com.google.gwt.dev.jjs.impl.TypeTightener;
+import com.google.gwt.dev.jjs.impl.InternalCompilerException.NodeInfo;
 import com.google.gwt.dev.js.FullNamingStrategy;
 import com.google.gwt.dev.js.JsSourceGenerationVisitor;
 import com.google.gwt.dev.js.NamingStrategy;
@@ -65,6 +68,8 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -383,6 +388,36 @@
     } catch (UnableToCompleteException e) {
       // just rethrow
       throw e;
+    } catch (InternalCompilerException e) {
+      TreeLogger topBranch = logger.branch(TreeLogger.ERROR,
+          "An internal compiler exception occurred", e);
+      List nodeTrace = e.getNodeTrace();
+      for (Iterator it = nodeTrace.iterator(); it.hasNext();) {
+        NodeInfo nodeInfo = (NodeInfo) it.next();
+        JSourceInfo info = nodeInfo.getSourceInfo();
+        String msg;
+        if (info != null) {
+          String fileName = info.getFileName();
+          fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
+          fileName = fileName.substring(fileName.lastIndexOf('\\') + 1);
+          msg = "at " + fileName + "(" + info.getStartLine() + "): ";
+        } else {
+          msg = "<no source info>: ";
+        }
+
+        String description = nodeInfo.getDescription();
+        if (description != null) {
+          msg += description;
+        } else {
+          msg += "<no description available>";
+        }
+        TreeLogger nodeBranch = topBranch.branch(TreeLogger.ERROR, msg, null);
+        String className = nodeInfo.getClassName();
+        if (className != null) {
+          nodeBranch.log(TreeLogger.INFO, className, null);
+        }
+      }
+      throw new UnableToCompleteException();
     } catch (Throwable e) {
       logger.log(TreeLogger.ERROR, "Unexpected internal compiler error", e);
       throw new UnableToCompleteException();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
index fc9c259..89f9782 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
@@ -516,40 +516,21 @@
   }
 
   protected final void doTraverse(JNode node, Context ctx) {
-    // boolean trace = false;
-    // String before = null;
     try {
-      // trace = !(this instanceof ToStringGenerationVisitor)
-      // && (node instanceof JTryStatement);
-      // if (trace) {
-      // before = node.toSource();
-      // }
       node.traverse(this, ctx);
-      // if (trace) {
-      // String after = node.toSource();
-      // if (!before.equals(after)) {
-      // System.out.println(this.getClass().getName() + ":");
-      // System.out.println("--");
-      // System.out.println(before);
-      // System.out.println("VV");
-      // System.out.println(after);
-      // System.out.println("---------------------------------------------------------");
-      // }
-      // }
-    } catch (InternalCompilerException ice) {
-      ice.addNode(node);
-      throw ice;
     } catch (Throwable e) {
-      // if (trace) {
-      // System.out.println(this.getClass().getName() + ":");
-      // System.out.println("--");
-      // System.out.println(before);
-      // System.out.println(e);
-      // }
-      InternalCompilerException ice = new InternalCompilerException(
-          "Unexpected error during visit.", e);
-      ice.addNode(node);
-      throw ice;
+      throw translateException(node, e);
     }
   }
+
+  private InternalCompilerException translateException(JNode node, Throwable e) {
+    InternalCompilerException ice;
+    if (e instanceof InternalCompilerException) {
+      ice = (InternalCompilerException) e;
+    } else {
+      ice = new InternalCompilerException("Error constructing Java AST", e);
+    }
+    ice.addNode(node);
+    return ice;
+  }
 }
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 7f5dd22..9f43513 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
@@ -104,6 +104,18 @@
           startLine, fileName);
     }
 
+    private static InternalCompilerException translateException(
+        AbstractMethodDeclaration amd, Throwable e) {
+      InternalCompilerException ice;
+      if (e instanceof InternalCompilerException) {
+        ice = (InternalCompilerException) e;
+      } else {
+        ice = new InternalCompilerException("Error building type map", e);
+      }
+      ice.addNode(amd.getClass().getName(), amd.toString(), makeSourceInfo(amd));
+      return ice;
+    }
+
     private String currentFileName;
     private int[] currentSeparatorPositions;
     private final JsParser jsParser = new JsParser();
@@ -124,18 +136,22 @@
     }
 
     public boolean visit(Argument argument, BlockScope scope) {
-      if (scope == scope.methodScope()) {
-        return true;
-      }
+      try {
+        if (scope == scope.methodScope()) {
+          return true;
+        }
 
-      JSourceInfo info = makeInfo(argument);
-      LocalVariableBinding b = argument.binding;
-      JType localType = (JType) typeMap.get(b.type);
-      JMethod enclosingMethod = findEnclosingMethod(scope);
-      JLocal newLocal = program.createLocal(info, argument.name, localType,
-          b.isFinal(), enclosingMethod);
-      typeMap.put(b, newLocal);
-      return true;
+        JSourceInfo info = makeSourceInfo(argument);
+        LocalVariableBinding b = argument.binding;
+        JType localType = (JType) typeMap.get(b.type);
+        JMethod enclosingMethod = findEnclosingMethod(scope);
+        JLocal newLocal = program.createLocal(info, argument.name, localType,
+            b.isFinal(), enclosingMethod);
+        typeMap.put(b, newLocal);
+        return true;
+      } catch (Throwable e) {
+        throw translateException(argument, e);
+      }
     }
 
     /**
@@ -146,191 +162,208 @@
      * for details.
      */
     public boolean visit(ConstructorDeclaration ctorDecl, ClassScope scope) {
-      MethodBinding b = ctorDecl.binding;
-      JClassType enclosingType = (JClassType) typeMap.get(scope.enclosingSourceType());
-      String name = enclosingType.getShortName();
-      JSourceInfo info = makeSourceInfo(ctorDecl);
-      JMethod newMethod = program.createMethod(info, name.toCharArray(),
-          enclosingType, enclosingType, false, false, true, b.isPrivate(),
-          false);
-      mapThrownExceptions(newMethod, ctorDecl);
+      try {
+        MethodBinding b = ctorDecl.binding;
+        JClassType enclosingType = (JClassType) typeMap.get(scope.enclosingSourceType());
+        String name = enclosingType.getShortName();
+        JSourceInfo info = makeSourceInfo(ctorDecl);
+        JMethod newMethod = program.createMethod(info, name.toCharArray(),
+            enclosingType, enclosingType, false, false, true, b.isPrivate(),
+            false);
+        mapThrownExceptions(newMethod, ctorDecl);
 
-      int syntheticParamCount = 0;
-      ReferenceBinding declaringClass = b.declaringClass;
-      if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
-        // add synthentic args for outer this and locals
-        NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
-        Set alreadyNamedVariables = new HashSet();
-        if (nestedBinding.enclosingInstances != null) {
-          for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
-            SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
-            String argName = String.valueOf(arg.name);
-            if (alreadyNamedVariables.contains(argName)) {
-              argName += "_" + i;
+        int syntheticParamCount = 0;
+        ReferenceBinding declaringClass = b.declaringClass;
+        if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
+          // add synthentic args for outer this and locals
+          NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
+          Set alreadyNamedVariables = new HashSet();
+          if (nestedBinding.enclosingInstances != null) {
+            for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
+              SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
+              String argName = String.valueOf(arg.name);
+              if (alreadyNamedVariables.contains(argName)) {
+                argName += "_" + i;
+              }
+              createParameter(arg, argName, newMethod);
+              ++syntheticParamCount;
+              alreadyNamedVariables.add(argName);
             }
-            createParameter(arg, argName, newMethod);
-            ++syntheticParamCount;
-            alreadyNamedVariables.add(argName);
+          }
+
+          if (nestedBinding.outerLocalVariables != null) {
+            for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
+              SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
+              String argName = String.valueOf(arg.name);
+              if (alreadyNamedVariables.contains(argName)) {
+                argName += "_" + i;
+              }
+              createParameter(arg, argName, newMethod);
+              ++syntheticParamCount;
+              alreadyNamedVariables.add(argName);
+            }
           }
         }
 
-        if (nestedBinding.outerLocalVariables != null) {
-          for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
-            SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
-            String argName = String.valueOf(arg.name);
-            if (alreadyNamedVariables.contains(argName)) {
-              argName += "_" + i;
-            }
-            createParameter(arg, argName, newMethod);
-            ++syntheticParamCount;
-            alreadyNamedVariables.add(argName);
-          }
+        // user args
+        mapParameters(newMethod, ctorDecl);
+
+        // remove synthetic args from the original param types list
+        for (; syntheticParamCount > 0; --syntheticParamCount) {
+          newMethod.getOriginalParamTypes().remove(0);
         }
+
+        typeMap.put(b, newMethod);
+        return true;
+      } catch (Throwable e) {
+        throw translateException(ctorDecl, e);
       }
-
-      // user args
-      mapParameters(newMethod, ctorDecl);
-
-      // remove synthetic args from the original param types list
-      for (; syntheticParamCount > 0; --syntheticParamCount) {
-        newMethod.getOriginalParamTypes().remove(0);
-      }
-
-      typeMap.put(b, newMethod);
-      return true;
     }
 
     public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
-      FieldBinding b = fieldDeclaration.binding;
-      JSourceInfo info = makeInfo(fieldDeclaration);
-      JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.enclosingSourceType());
-      createField(info, b, enclosingType,
-          fieldDeclaration.initialization != null);
-      return true;
+      try {
+        FieldBinding b = fieldDeclaration.binding;
+        JSourceInfo info = makeSourceInfo(fieldDeclaration);
+        JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.enclosingSourceType());
+        createField(info, b, enclosingType,
+            fieldDeclaration.initialization != null);
+        return true;
+      } catch (Throwable e) {
+        throw translateException(fieldDeclaration, e);
+      }
     }
 
     public boolean visit(LocalDeclaration localDeclaration, BlockScope scope) {
-      LocalVariableBinding b = localDeclaration.binding;
-      JType localType = (JType) typeMap.get(localDeclaration.type.resolvedType);
-      JMethod enclosingMethod = findEnclosingMethod(scope);
-      JSourceInfo info = makeInfo(localDeclaration);
-      JLocal newLocal = program.createLocal(info, localDeclaration.name,
-          localType, b.isFinal(), enclosingMethod);
-      typeMap.put(b, newLocal);
-      return true;
+      try {
+        LocalVariableBinding b = localDeclaration.binding;
+        JType localType = (JType) typeMap.get(localDeclaration.type.resolvedType);
+        JMethod enclosingMethod = findEnclosingMethod(scope);
+        JSourceInfo info = makeSourceInfo(localDeclaration);
+        JLocal newLocal = program.createLocal(info, localDeclaration.name,
+            localType, b.isFinal(), enclosingMethod);
+        typeMap.put(b, newLocal);
+        return true;
+      } catch (Throwable e) {
+        throw translateException(localDeclaration, e);
+      }
     }
 
     public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
-      MethodBinding b = methodDeclaration.binding;
-      JSourceInfo info = makeSourceInfo(methodDeclaration);
-      JType returnType = (JType) typeMap.get(methodDeclaration.returnType.resolvedType);
-      JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.enclosingSourceType());
-      JMethod newMethod = program.createMethod(info,
-          methodDeclaration.selector, enclosingType, returnType,
-          b.isAbstract(), b.isStatic(), b.isFinal(), b.isPrivate(),
-          b.isNative());
+      try {
+        MethodBinding b = methodDeclaration.binding;
+        JSourceInfo info = makeSourceInfo(methodDeclaration);
+        JType returnType = (JType) typeMap.get(methodDeclaration.returnType.resolvedType);
+        JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.enclosingSourceType());
+        JMethod newMethod = program.createMethod(info,
+            methodDeclaration.selector, enclosingType, returnType,
+            b.isAbstract(), b.isStatic(), b.isFinal(), b.isPrivate(),
+            b.isNative());
 
-      mapThrownExceptions(newMethod, methodDeclaration);
-      mapParameters(newMethod, methodDeclaration);
-      typeMap.put(b, newMethod);
+        mapThrownExceptions(newMethod, methodDeclaration);
+        mapParameters(newMethod, methodDeclaration);
+        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);
-        int startPos = jsniCode.indexOf("/*-{");
-        int endPos = jsniCode.lastIndexOf("}-*/");
-        if (startPos < 0 && endPos < 0) {
-          GenerateJavaAST.reportJsniError(
-              info,
-              methodDeclaration,
-              "Native methods require a JavaScript implementation enclosed with /*-{ and }-*/");
-          return true;
-        }
-        if (startPos < 0) {
-          GenerateJavaAST.reportJsniError(info, methodDeclaration,
-              "Unable to find start of native block; begin your JavaScript block with: /*-{");
-          return true;
-        }
-        if (endPos < 0) {
-          GenerateJavaAST.reportJsniError(
-              info,
-              methodDeclaration,
-              "Unable to find end of native block; terminate your JavaScript block with: }-*/");
-          return true;
-        }
-
-        startPos += 3; // move up to open brace
-        endPos += 1; // move past close brace
-
-        jsniCode = jsniCode.substring(startPos, endPos);
-
-        // Here we parse it as an anonymous function, but we will give it a
-        // name later when we generate the JavaScript during codegen.
-        //
-        String syntheticFnHeader = "function (";
-        boolean first = true;
-        for (int i = 0; i < newMethod.params.size(); ++i) {
-          JParameter param = (JParameter) newMethod.params.get(i);
-          if (first) {
-            first = false;
-          } else {
-            syntheticFnHeader += ',';
+        if (newMethod.isNative()) {
+          // Handle JSNI block
+          char[] source = methodDeclaration.compilationResult().getCompilationUnit().getContents();
+          String jsniCode = String.valueOf(source, methodDeclaration.bodyStart,
+              methodDeclaration.bodyEnd - methodDeclaration.bodyStart + 1);
+          int startPos = jsniCode.indexOf("/*-{");
+          int endPos = jsniCode.lastIndexOf("}-*/");
+          if (startPos < 0 && endPos < 0) {
+            GenerateJavaAST.reportJsniError(
+                info,
+                methodDeclaration,
+                "Native methods require a JavaScript implementation enclosed with /*-{ and }-*/");
+            return true;
           }
-          syntheticFnHeader += param.getName();
-        }
-        syntheticFnHeader += ')';
-        StringReader sr = new StringReader(syntheticFnHeader + '\n' + jsniCode);
-        try {
-          // start at -1 to avoid counting our synthetic header
-          // TODO: get the character position start correct
-          JsStatements result = jsParser.parse(jsProgram.getScope(), sr, -1);
-          JsExprStmt jsExprStmt = (JsExprStmt) result.get(0);
-          JsFunction jsFunction = (JsFunction) jsExprStmt.getExpression();
-          ((JsniMethod) newMethod).setFunc(jsFunction);
-        } catch (IOException e) {
-          throw new InternalCompilerException(
-              "Internal error parsing JSNI in method '" + newMethod
-                  + "' in type '" + enclosingType.getName() + "'", e);
-        } catch (JsParserException e) {
-          /*
-           * count the number of characters to the problem (from the start of
-           * the JSNI code)
-           */
-          SourceDetail detail = e.getSourceDetail();
-          int line = detail.getLine();
-          char[] chars = jsniCode.toCharArray();
-          int i = 0, n = chars.length;
-          while (line > 0) {
-            // CHECKSTYLE_OFF
-            switch (chars[i]) {
-              case '\r':
-                // if skip an extra character if this is a CR/LF
-                if (i + 1 < n && chars[i + 1] == '\n') {
-                  ++i;
-                }
-                // intentional fall-through
-              case '\n':
-                --line;
-                // intentional fall-through
-              default:
-                ++i;
+          if (startPos < 0) {
+            GenerateJavaAST.reportJsniError(info, methodDeclaration,
+                "Unable to find start of native block; begin your JavaScript block with: /*-{");
+            return true;
+          }
+          if (endPos < 0) {
+            GenerateJavaAST.reportJsniError(
+                info,
+                methodDeclaration,
+                "Unable to find end of native block; terminate your JavaScript block with: }-*/");
+            return true;
+          }
+
+          startPos += 3; // move up to open brace
+          endPos += 1; // move past close brace
+
+          jsniCode = jsniCode.substring(startPos, endPos);
+
+          // Here we parse it as an anonymous function, but we will give it a
+          // name later when we generate the JavaScript during codegen.
+          //
+          String syntheticFnHeader = "function (";
+          boolean first = true;
+          for (int i = 0; i < newMethod.params.size(); ++i) {
+            JParameter param = (JParameter) newMethod.params.get(i);
+            if (first) {
+              first = false;
+            } else {
+              syntheticFnHeader += ',';
             }
-            // CHECKSTYLE_ON
+            syntheticFnHeader += param.getName();
           }
+          syntheticFnHeader += ')';
+          StringReader sr = new StringReader(syntheticFnHeader + '\n'
+              + jsniCode);
+          try {
+            // start at -1 to avoid counting our synthetic header
+            // TODO: get the character position start correct
+            JsStatements result = jsParser.parse(jsProgram.getScope(), sr, -1);
+            JsExprStmt jsExprStmt = (JsExprStmt) result.get(0);
+            JsFunction jsFunction = (JsFunction) jsExprStmt.getExpression();
+            ((JsniMethod) newMethod).setFunc(jsFunction);
+          } catch (IOException e) {
+            throw new InternalCompilerException(
+                "Internal error parsing JSNI in method '" + newMethod
+                    + "' in type '" + enclosingType.getName() + "'", e);
+          } catch (JsParserException e) {
+            /*
+             * count the number of characters to the problem (from the start of
+             * the JSNI code)
+             */
+            SourceDetail detail = e.getSourceDetail();
+            int line = detail.getLine();
+            char[] chars = jsniCode.toCharArray();
+            int i = 0, n = chars.length;
+            while (line > 0) {
+              // CHECKSTYLE_OFF
+              switch (chars[i]) {
+                case '\r':
+                  // if skip an extra character if this is a CR/LF
+                  if (i + 1 < n && chars[i + 1] == '\n') {
+                    ++i;
+                  }
+                  // intentional fall-through
+                case '\n':
+                  --line;
+                  // intentional fall-through
+                default:
+                  ++i;
+              }
+              // CHECKSTYLE_ON
+            }
 
-          // TODO: check this
-          // Map into the original source stream;
-          i += startPos + detail.getLineOffset();
-          info = new JSourceInfo(i, i, info.getStartLine() + detail.getLine(),
-              info.getFileName());
-          GenerateJavaAST.reportJsniError(info, methodDeclaration,
-              e.getMessage());
+            // TODO: check this
+            // Map into the original source stream;
+            i += startPos + detail.getLineOffset();
+            info = new JSourceInfo(i, i,
+                info.getStartLine() + detail.getLine(), info.getFileName());
+            GenerateJavaAST.reportJsniError(info, methodDeclaration,
+                e.getMessage());
+          }
         }
-      }
 
-      return true;
+        return true;
+      } catch (Throwable e) {
+        throw translateException(methodDeclaration, e);
+      }
     }
 
     public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
@@ -370,7 +403,7 @@
     private JParameter createParameter(LocalVariableBinding binding,
         JMethod enclosingMethod) {
       JType type = (JType) typeMap.get(binding.type);
-      JSourceInfo info = makeInfo(binding.declaration);
+      JSourceInfo info = makeSourceInfo(binding.declaration);
       JParameter param = program.createParameter(info, binding.name, type,
           binding.isFinal(), enclosingMethod);
       typeMap.put(binding, param);
@@ -403,7 +436,7 @@
       return (JMethod) typeMap.get(referenceMethod.binding);
     }
 
-    private JSourceInfo makeInfo(Statement stmt) {
+    private JSourceInfo makeSourceInfo(Statement stmt) {
       int startLine = ProblemHandler.searchLineNumber(
           currentSeparatorPositions, stmt.sourceStart);
       return new JSourceInfo(stmt.sourceStart, stmt.sourceEnd, startLine,
@@ -455,44 +488,63 @@
         return false;
       }
       JReferenceType type = (JReferenceType) typeMap.get(binding);
+      try {
+        if (binding.isNestedType() && !binding.isStatic()) {
+          // add synthentic fields for outer this and locals
+          assert (type instanceof JClassType);
+          NestedTypeBinding nestedBinding = (NestedTypeBinding) binding;
+          if (nestedBinding.enclosingInstances != null) {
+            for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
+              SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
+              if (arg.matchingField != null) {
+                createField(arg, type);
+              }
+            }
+          }
 
-      if (binding.isNestedType() && !binding.isStatic()) {
-        // add synthentic fields for outer this and locals
-        assert (type instanceof JClassType);
-        NestedTypeBinding nestedBinding = (NestedTypeBinding) binding;
-        if (nestedBinding.enclosingInstances != null) {
-          for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
-            SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
-            if (arg.matchingField != null) {
+          if (nestedBinding.outerLocalVariables != null) {
+            for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
+              SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
               createField(arg, type);
             }
           }
         }
 
-        if (nestedBinding.outerLocalVariables != null) {
-          for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
-            SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
-            createField(arg, type);
-          }
+        ReferenceBinding superClassBinding = binding.superclass();
+        if (superClassBinding != null) {
+          assert (binding.superclass().isClass());
+          JClassType superClass = (JClassType) typeMap.get(superClassBinding);
+          type.extnds = superClass;
         }
-      }
 
-      ReferenceBinding superClassBinding = binding.superclass();
-      if (superClassBinding != null) {
-        assert (binding.superclass().isClass());
-        JClassType superClass = (JClassType) typeMap.get(superClassBinding);
-        type.extnds = superClass;
+        ReferenceBinding[] superInterfaces = binding.superInterfaces();
+        for (int i = 0; i < superInterfaces.length; ++i) {
+          ReferenceBinding superInterfaceBinding = superInterfaces[i];
+          assert (superInterfaceBinding.isInterface());
+          JInterfaceType superInterface = (JInterfaceType) typeMap.get(superInterfaceBinding);
+          type.implments.add(superInterface);
+        }
+        typeDecls.add(typeDeclaration);
+        return true;
+      } catch (InternalCompilerException ice) {
+        ice.addNode(type);
+        throw ice;
+      } catch (Throwable e) {
+        throw new InternalCompilerException(type, "Error building type map", e);
       }
+    }
 
-      ReferenceBinding[] superInterfaces = binding.superInterfaces();
-      for (int i = 0; i < superInterfaces.length; ++i) {
-        ReferenceBinding superInterfaceBinding = superInterfaces[i];
-        assert (superInterfaceBinding.isInterface());
-        JInterfaceType superInterface = (JInterfaceType) typeMap.get(superInterfaceBinding);
-        type.implments.add(superInterface);
+    private InternalCompilerException translateException(Statement stmt,
+        Throwable e) {
+      InternalCompilerException ice;
+      if (e instanceof InternalCompilerException) {
+        ice = (InternalCompilerException) e;
+      } else {
+        ice = new InternalCompilerException("Error building type map", e);
       }
-      typeDecls.add(typeDeclaration);
-      return true;
+      ice.addNode(stmt.getClass().getName(), stmt.toString(),
+          makeSourceInfo(stmt));
+      return ice;
     }
   }
 
@@ -514,6 +566,19 @@
           fileName);
     }
 
+    private static InternalCompilerException translateException(
+        TypeDeclaration typeDecl, Throwable e) {
+      InternalCompilerException ice;
+      if (e instanceof InternalCompilerException) {
+        ice = (InternalCompilerException) e;
+      } else {
+        ice = new InternalCompilerException("Error building type map", e);
+      }
+      ice.addNode(typeDecl.getClass().getName(), typeDecl.toString(),
+          makeSourceInfo(typeDecl));
+      return ice;
+    }
+
     private final JProgram program;
     private final TypeMap typeMap;
 
@@ -537,57 +602,61 @@
     }
 
     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.
-           */
+      try {
+        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;
+        }
+
+        JSourceInfo info = makeSourceInfo(typeDeclaration);
+        JReferenceType newType;
+        if (binding.isClass()) {
+          newType = program.createClass(info, name, binding.isAbstract(),
+              binding.isFinal());
+        } else if (binding.isInterface()) {
+          newType = program.createInterface(info, name);
+        } else {
+          assert (false);
           return false;
         }
 
-        for (int i = 0, c = localName.length; i < c; ++i) {
-          if (localName[i] == '/') {
-            localName[i] = '.';
-          }
+        /**
+         * 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(null, "$clinit".toCharArray(),
+            newType, program.getTypeVoid(), false, true, true, true, false);
+        clinit.freezeParamTypes();
+
+        if (newType instanceof JClassType) {
+          JMethod init = program.createMethod(null, "$init".toCharArray(),
+              newType, program.getTypeVoid(), false, false, true, true, false);
+          init.freezeParamTypes();
         }
-        name = new char[1][0];
-        name[0] = localName;
+
+        typeMap.put(binding, newType);
+        return true;
+      } catch (Throwable e) {
+        throw translateException(typeDeclaration, e);
       }
-
-      JSourceInfo info = makeSourceInfo(typeDeclaration);
-      JReferenceType newType;
-      if (binding.isClass()) {
-        newType = program.createClass(info, name, binding.isAbstract(),
-            binding.isFinal());
-      } else if (binding.isInterface()) {
-        newType = program.createInterface(info, 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(null, "$clinit".toCharArray(),
-          newType, program.getTypeVoid(), false, true, true, true, false);
-      clinit.freezeParamTypes();
-
-      if (newType instanceof JClassType) {
-        JMethod init = program.createMethod(null, "$init".toCharArray(),
-            newType, program.getTypeVoid(), false, false, true, true, false);
-        init.freezeParamTypes();
-      }
-
-      typeMap.put(binding, newType);
-      return true;
     }
   }
 
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 c903926..e80f39c 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
@@ -84,7 +84,6 @@
 import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.internal.compiler.CompilationResult;
 import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
-import org.eclipse.jdt.internal.compiler.ast.ASTNode;
 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
 import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
@@ -225,6 +224,19 @@
       return sb.toString();
     }
 
+    private static InternalCompilerException translateException(JNode node,
+        Throwable e) {
+      InternalCompilerException ice;
+      if (e instanceof InternalCompilerException) {
+        ice = (InternalCompilerException) e;
+        ice.addNode(node);
+      } else {
+        ice = new InternalCompilerException(node,
+            "Error constructing Java AST", e);
+      }
+      return ice;
+    }
+
     private Object[] args = new Object[1];
 
     private JReferenceType currentClass;
@@ -259,54 +271,58 @@
      */
     public void processType(TypeDeclaration x) {
       currentClass = (JReferenceType) typeMap.get(x.binding);
-      currentClassScope = x.scope;
-      currentSeparatorPositions = x.compilationResult.lineSeparatorPositions;
-      currentFileName = String.valueOf(x.compilationResult.fileName);
+      try {
+        currentClassScope = x.scope;
+        currentSeparatorPositions = x.compilationResult.lineSeparatorPositions;
+        currentFileName = String.valueOf(x.compilationResult.fileName);
 
-      if (x.fields != null) {
-        // Process fields
-        for (int i = 0, n = x.fields.length; i < n; ++i) {
-          FieldDeclaration fieldDeclaration = x.fields[i];
-          if (fieldDeclaration.isStatic()) {
-            // clinit
-            currentMethod = (JMethod) currentClass.methods.get(0);
-            currentMethodScope = x.staticInitializerScope;
-          } else {
-            // init
-            currentMethod = (JMethod) currentClass.methods.get(1);
-            currentMethodScope = x.initializerScope;
-          }
+        if (x.fields != null) {
+          // Process fields
+          for (int i = 0, n = x.fields.length; i < n; ++i) {
+            FieldDeclaration fieldDeclaration = x.fields[i];
+            if (fieldDeclaration.isStatic()) {
+              // clinit
+              currentMethod = (JMethod) currentClass.methods.get(0);
+              currentMethodScope = x.staticInitializerScope;
+            } else {
+              // init
+              currentMethod = (JMethod) currentClass.methods.get(1);
+              currentMethodScope = x.initializerScope;
+            }
 
-          if (fieldDeclaration instanceof Initializer) {
-            assert (currentClass instanceof JClassType);
-            processInitializer((Initializer) fieldDeclaration);
-          } else {
-            processField(fieldDeclaration);
+            if (fieldDeclaration instanceof Initializer) {
+              assert (currentClass instanceof JClassType);
+              processInitializer((Initializer) fieldDeclaration);
+            } else {
+              processField(fieldDeclaration);
+            }
           }
         }
-      }
 
-      currentMethodScope = null;
-      currentMethod = 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 (currentClass instanceof JClassType);
-            processConstructor((ConstructorDeclaration) x.methods[i]);
-          } else if (x.methods[i].isClinit()) {
-            // nothing to do
-          } else {
-            processMethod(x.methods[i]);
+        if (x.methods != null) {
+          // Process methods
+          for (int i = 0, n = x.methods.length; i < n; ++i) {
+            if (x.methods[i].isConstructor()) {
+              assert (currentClass instanceof JClassType);
+              processConstructor((ConstructorDeclaration) x.methods[i]);
+            } else if (x.methods[i].isClinit()) {
+              // nothing to do
+            } else {
+              processMethod(x.methods[i]);
+            }
           }
         }
-      }
 
-      currentClassScope = null;
-      currentClass = null;
-      currentSeparatorPositions = null;
-      currentFileName = null;
+        currentClassScope = null;
+        currentClass = null;
+        currentSeparatorPositions = null;
+        currentFileName = null;
+      } catch (Throwable e) {
+        throw translateException(currentClass, e);
+      }
     }
 
     /**
@@ -319,35 +335,15 @@
       }
 
       try {
-        try {
-          params[0] = child.getClass();
-          Method method = getClass().getDeclaredMethod(name, params);
-          args[0] = child;
-          return (JNode) method.invoke(this, args);
-        } catch (SecurityException e) {
-          throw new InternalCompilerException("Error during dispatch", e);
-        } catch (NoSuchMethodException e) {
-          String s = params[0].getName();
-          throw new InternalCompilerException("Expecting: private void " + name
-              + "(" + s.substring(s.lastIndexOf('.') + 1) + " x) { }", e);
-        } catch (IllegalArgumentException e) {
-          throw new InternalCompilerException("Error during dispatch", e);
-        } catch (IllegalAccessException e) {
-          throw new InternalCompilerException("Error during dispatch", e);
-        } catch (InvocationTargetException e) {
-          Throwable target = e.getTargetException();
-          if (target instanceof InternalCompilerException) {
-            throw (InternalCompilerException) target;
-          } else {
-            throw new InternalCompilerException(
-                "Error during AST construction", target);
-          }
+        params[0] = child.getClass();
+        Method method = getClass().getDeclaredMethod(name, params);
+        args[0] = child;
+        return (JNode) method.invoke(this, args);
+      } catch (Throwable e) {
+        if (e instanceof InvocationTargetException) {
+          e = ((InvocationTargetException) e).getTargetException();
         }
-      } catch (InternalCompilerException ice) {
-        if (child instanceof ASTNode) {
-          ice.addNode((ASTNode) child);
-        }
-        throw ice;
+        throw translateException(child, e);
       }
     }
 
@@ -449,48 +445,64 @@
      */
     void processConstructor(ConstructorDeclaration x) {
       JMethod ctor = (JMethod) typeMap.get(x.binding);
-      JSourceInfo info = ctor.body.getSourceInfo();
+      try {
+        JSourceInfo info = ctor.body.getSourceInfo();
 
-      currentMethod = ctor;
-      currentMethodScope = x.scope;
+        currentMethod = ctor;
+        currentMethodScope = x.scope;
 
-      JMethodCall call = null;
-      ExplicitConstructorCall ctorCall = x.constructorCall;
-      if (ctorCall != null) {
-        call = processExpression(ctorCall);
-      }
+        JMethodCall call = null;
+        ExplicitConstructorCall ctorCall = x.constructorCall;
+        if (ctorCall != null) {
+          call = (JMethodCall) dispatch("processExpression", ctorCall);
+        }
 
-      /*
-       * Determine if we have an explicit this call. The presence of an explicit
-       * this call indicates we can skip certain initialization steps (as the
-       * callee will perform those steps for us). These skippable steps are 1)
-       * assigning synthetic args to fields and 2) running initializers.
-       */
-      boolean hasExplicitThis = (ctorCall != null) && !ctorCall.isSuperAccess();
+        /*
+         * Determine if we have an explicit this call. The presence of an
+         * explicit this call indicates we can skip certain initialization steps
+         * (as the callee will perform those steps for us). These skippable
+         * steps are 1) assigning synthetic args to fields and 2) running
+         * initializers.
+         */
+        boolean hasExplicitThis = (ctorCall != null)
+            && !ctorCall.isSuperAccess();
 
-      JClassType enclosingType = (JClassType) ctor.getEnclosingType();
+        JClassType enclosingType = (JClassType) ctor.getEnclosingType();
 
-      // Call clinit; $clinit is always in position 0.
-      JMethod clinitMethod = (JMethod) enclosingType.methods.get(0);
-      JMethodCall clinitCall = new JMethodCall(program, info, null,
-          clinitMethod);
-      ctor.body.statements.add(new JExpressionStatement(program, info,
-          clinitCall));
+        // Call clinit; $clinit is always in position 0.
+        JMethod clinitMethod = (JMethod) enclosingType.methods.get(0);
+        JMethodCall clinitCall = new JMethodCall(program, info, null,
+            clinitMethod);
+        ctor.body.statements.add(new JExpressionStatement(program, info,
+            clinitCall));
 
-      /*
-       * All synthetic fields must be assigned, unless we have an explicit this
-       * constructor call, in which case the callee will assign them for us.
-       */
-      if (!hasExplicitThis) {
-        ReferenceBinding declaringClass = x.binding.declaringClass;
-        if (declaringClass instanceof NestedTypeBinding) {
-          Iterator/* <JParameter> */paramIt = ctor.params.iterator();
-          NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
-          if (nestedBinding.enclosingInstances != null) {
-            for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
-              SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
-              JParameter param = (JParameter) paramIt.next();
-              if (arg.matchingField != null) {
+        /*
+         * All synthetic fields must be assigned, unless we have an explicit
+         * this constructor call, in which case the callee will assign them for
+         * us.
+         */
+        if (!hasExplicitThis) {
+          ReferenceBinding declaringClass = x.binding.declaringClass;
+          if (declaringClass instanceof NestedTypeBinding) {
+            Iterator/* <JParameter> */paramIt = ctor.params.iterator();
+            NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
+            if (nestedBinding.enclosingInstances != null) {
+              for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
+                SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
+                JParameter param = (JParameter) paramIt.next();
+                if (arg.matchingField != null) {
+                  JField field = (JField) typeMap.get(arg);
+                  ctor.body.statements.add(program.createAssignmentStmt(info,
+                      createVariableRef(info, field), createVariableRef(info,
+                          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) typeMap.get(arg);
                 ctor.body.statements.add(program.createAssignmentStmt(info,
                     createVariableRef(info, field), createVariableRef(info,
@@ -498,57 +510,48 @@
               }
             }
           }
+        }
 
-          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) typeMap.get(arg);
-              ctor.body.statements.add(program.createAssignmentStmt(info,
-                  createVariableRef(info, field),
-                  createVariableRef(info, param)));
+        // optional this or super constructor call
+        if (call != null) {
+          ctor.body.statements.add(new JExpressionStatement(program,
+              makeSourceInfo(ctorCall), call));
+        }
+
+        JExpression thisRef = createThisRef(info, enclosingType);
+
+        /*
+         * Call the synthetic instance initializer method, unless we have an
+         * explicit this constructor call, in which case the callee will.
+         */
+        if (!hasExplicitThis) {
+          // $init is always in position 1 (clinit is in 0)
+          JMethod initMethod = (JMethod) enclosingType.methods.get(1);
+          JMethodCall initCall = new JMethodCall(program, info, thisRef,
+              initMethod);
+          ctor.body.statements.add(new JExpressionStatement(program, info,
+              initCall));
+        }
+
+        // user code (finally!)
+        if (x.statements != null) {
+          for (int i = 0, n = x.statements.length; i < n; ++i) {
+            Statement origStmt = x.statements[i];
+            JStatement jstmt = dispProcessStatement(origStmt);
+            if (jstmt != null) {
+              ctor.body.statements.add(jstmt);
             }
           }
         }
+
+        currentMethodScope = null;
+        currentMethod = null;
+
+        // synthesize a return statement to emulate returning the new object
+        ctor.body.statements.add(new JReturnStatement(program, null, thisRef));
+      } catch (Throwable e) {
+        throw translateException(ctor, e);
       }
-
-      // optional this or super constructor call
-      if (call != null) {
-        ctor.body.statements.add(new JExpressionStatement(program,
-            makeSourceInfo(ctorCall), call));
-      }
-
-      JExpression thisRef = createThisRef(info, enclosingType);
-
-      /*
-       * Call the synthetic instance initializer method, unless we have an
-       * explicit this constructor call, in which case the callee will.
-       */
-      if (!hasExplicitThis) {
-        // $init is always in position 1 (clinit is in 0)
-        JMethod initMethod = (JMethod) enclosingType.methods.get(1);
-        JMethodCall initCall = new JMethodCall(program, info, thisRef,
-            initMethod);
-        ctor.body.statements.add(new JExpressionStatement(program, info,
-            initCall));
-      }
-
-      // user code (finally!)
-      if (x.statements != null) {
-        for (int i = 0, n = x.statements.length; i < n; ++i) {
-          Statement origStmt = x.statements[i];
-          JStatement jstmt = dispProcessStatement(origStmt);
-          if (jstmt != null) {
-            ctor.body.statements.add(jstmt);
-          }
-        }
-      }
-
-      currentMethodScope = null;
-      currentMethod = null;
-
-      // synthesize a return statement to emulate returning the new object
-      ctor.body.statements.add(new JReturnStatement(program, null, thisRef));
     }
 
     JExpression processExpression(AllocationExpression x) {
@@ -1198,63 +1201,69 @@
          */
         return;
       }
-      JExpression initializer = null;
-      if (declaration.initialization != null) {
-        initializer = dispProcessExpression(declaration.initialization);
-      }
+      try {
+        JExpression initializer = null;
+        if (declaration.initialization != null) {
+          initializer = dispProcessExpression(declaration.initialization);
+        }
 
-      if (initializer instanceof JLiteral) {
-        field.constInitializer = (JLiteral) initializer;
-      } else if (initializer != null) {
-        JSourceInfo info = makeSourceInfo(declaration);
-        JStatement assignStmt = program.createAssignmentStmt(info,
-            createVariableRef(info, field), initializer);
+        if (initializer instanceof JLiteral) {
+          field.constInitializer = (JLiteral) initializer;
+        } else if (initializer != null) {
+          JSourceInfo info = makeSourceInfo(declaration);
+          JStatement assignStmt = program.createAssignmentStmt(info,
+              createVariableRef(info, field), initializer);
 
-        // will either be init or clinit
-        currentMethod.body.statements.add(assignStmt);
+          // will either be init or clinit
+          currentMethod.body.statements.add(assignStmt);
+        }
+      } catch (Throwable e) {
+        throw translateException(field, e);
       }
     }
 
     void processInitializer(Initializer initializer) {
-      JBlock block = processStatement(initializer.block);
-      // will either be init or clinit
-      currentMethod.body.statements.add(block);
+      JBlock block = (JBlock) dispProcessStatement(initializer.block);
+      try {
+        // will either be init or clinit
+        currentMethod.body.statements.add(block);
+      } catch (Throwable e) {
+        throw translateException(initializer, e);
+      }
     }
 
     void processMethod(AbstractMethodDeclaration x) {
       MethodBinding b = x.binding;
       JMethod method = (JMethod) typeMap.get(b);
+      try {
+        if (b.isImplementing() || b.isOverriding()) {
+          tryFindUpRefs(method, b);
+        }
 
-      if (b.isImplementing() || b.isOverriding()) {
-        tryFindUpRefs(method, b);
-      }
+        if (x.isNative()) {
+          processNativeMethod(x, (JsniMethod) method);
+          return;
+        }
 
-      if (x.isNative()) {
-        processNativeMethod(x, (JsniMethod) method);
-        return;
-      }
+        currentMethod = method;
+        currentMethodScope = x.scope;
 
-      currentMethod = method;
-      currentMethodScope = x.scope;
-
-      if (x.statements != null) {
-        for (int i = 0, n = x.statements.length; i < n; ++i) {
-          Statement origStmt = x.statements[i];
-          JStatement jstmt = dispProcessStatement(origStmt);
-          if (jstmt != null) {
-            method.body.statements.add(jstmt);
+        if (x.statements != null) {
+          for (int i = 0, n = x.statements.length; i < n; ++i) {
+            Statement origStmt = x.statements[i];
+            JStatement jstmt = dispProcessStatement(origStmt);
+            if (jstmt != null) {
+              method.body.statements.add(jstmt);
+            }
           }
         }
+        currentMethodScope = null;
+        currentMethod = null;
+      } catch (Throwable e) {
+        throw translateException(method, e);
       }
-      currentMethodScope = null;
-      currentMethod = null;
     }
 
-    // // 5.0
-    // JStatement processStatement(ForeachStatement x) {
-    // return null;
-    // }
-
     void processNativeMethod(AbstractMethodDeclaration x,
         JsniMethod nativeMethod) {
 
@@ -1319,6 +1328,11 @@
       }
     }
 
+    // // 5.0
+    // JStatement processStatement(ForeachStatement x) {
+    // return null;
+    // }
+
     JStatement processStatement(AssertStatement x) {
       JSourceInfo info = makeSourceInfo(x);
       JExpression expr = dispProcessExpression(x.assertExpression);
@@ -1469,7 +1483,7 @@
 
     JStatement processStatement(SynchronizedStatement x) {
       JSourceInfo info = makeSourceInfo(x);
-      JBlock block = processStatement(x.block);
+      JBlock block = (JBlock) dispProcessStatement(x.block);
       JExpression expr = dispProcessExpression(x.expression);
       block.statements.add(0, new JExpressionStatement(program, info, expr));
       return block;
@@ -1483,7 +1497,7 @@
 
     JStatement processStatement(TryStatement x) {
       JSourceInfo info = makeSourceInfo(x);
-      JBlock tryBlock = processStatement(x.tryBlock);
+      JBlock tryBlock = (JBlock) dispProcessStatement(x.tryBlock);
       List/* <JLocalRef> */catchArgs = new ArrayList/* <JLocalRef> */();
       List/* <JBlock> */catchBlocks = new ArrayList/* <JBlock> */();
       if (x.catchBlocks != null) {
@@ -1492,10 +1506,10 @@
           catchArgs.add(createVariableRef(info, local));
         }
         for (int i = 0, c = x.catchBlocks.length; i < c; ++i) {
-          catchBlocks.add(processStatement(x.catchBlocks[i]));
+          catchBlocks.add(dispProcessStatement(x.catchBlocks[i]));
         }
       }
-      JBlock finallyBlock = processStatement(x.finallyBlock);
+      JBlock finallyBlock = (JBlock) dispProcessStatement(x.finallyBlock);
       return new JTryStatement(program, info, tryBlock, catchArgs, catchBlocks,
           finallyBlock);
     }
@@ -2014,6 +2028,24 @@
       return binaryOperation;
     }
 
+    private InternalCompilerException translateException(Object node,
+        Throwable e) {
+      InternalCompilerException ice;
+      if (e instanceof InternalCompilerException) {
+        ice = (InternalCompilerException) e;
+      } else {
+        ice = new InternalCompilerException("Error constructing Java AST", e);
+      }
+      String className = node.getClass().getName();
+      String description = node.toString();
+      JSourceInfo sourceInfo = null;
+      if (node instanceof Statement) {
+        sourceInfo = makeSourceInfo((Statement) node);
+      }
+      ice.addNode(className, description, sourceInfo);
+      return ice;
+    }
+
     /**
      * For a given method(and method binding), try to find all methods that it
      * overrides/implements.
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 29d3f35..9b0a299 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
@@ -16,8 +16,10 @@
 package com.google.gwt.dev.jjs.impl;
 
 import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JSourceInfo;
 
-import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Indicates the compiler encountered an unexpected and unsupported state of
@@ -25,20 +27,123 @@
  */
 public class InternalCompilerException extends RuntimeException {
 
+  /**
+   * Tracks if there's a pending addNode() to avoid recursion sickness.
+   */
+  private static final ThreadLocal pendingICE = new ThreadLocal();
+
+  /**
+   * Information regarding a node that was being processed when an
+   * InternalCompilerException was thrown.
+   */
+  public static final class NodeInfo {
+
+    private final String className;
+    private final String description;
+    private final JSourceInfo sourceInfo;
+
+    private NodeInfo(String className, String description,
+        JSourceInfo sourceInfo) {
+      this.className = className;
+      this.description = description;
+      this.sourceInfo = sourceInfo;
+    }
+
+    /**
+     * Returns the name of the Java class of the node.
+     */
+    public String getClassName() {
+      return className;
+    }
+
+    /**
+     * Returns a text description of the node; typically toString().
+     */
+    public String getDescription() {
+      return description;
+    }
+
+    /**
+     * Returns the node's source info, if available; otherwise <code>null</code>.
+     */
+    public JSourceInfo getSourceInfo() {
+      return sourceInfo;
+    }
+  }
+
+  private final List nodeTrace = new ArrayList();
+
+  /**
+   * Constructs a new exception with the specified node, message, and cause.
+   */
+  public InternalCompilerException(JNode node, String message, Throwable cause) {
+    this(message, cause);
+    addNode(node);
+  }
+
+  /**
+   * Constructs a new exception with the specified message.
+   */
   public InternalCompilerException(String message) {
     super(message);
   }
 
+  /**
+   * Constructs a new exception with the specified message and cause.
+   */
   public InternalCompilerException(String message, Throwable cause) {
     super(message, cause);
   }
 
-  public void addNode(ASTNode node) {
-    // TODO Auto-generated method stub
+  /**
+   * Adds a node to the end of the node trace. This is similar to how a stack
+   * trace works.
+   */
+  public void addNode(JNode node) {
+    InternalCompilerException other = (InternalCompilerException) pendingICE.get();
+    if (other != null) {
+      // Avoiding recursion sickness: Yet Another ICE must have occured while
+      // generating info for a prior ICE. Just bail!
+      return;
+    }
+
+    String className = null;
+    String description = null;
+    JSourceInfo sourceInfo = null;
+    try {
+      pendingICE.set(this);
+      className = node.getClass().getName();
+      sourceInfo = node.getSourceInfo();
+      description = node.toString();
+    } catch (Throwable e) {
+      // ignore any exceptions
+      if (description == null) {
+        description = "<source info not available>";
+      }
+    } finally {
+      pendingICE.set(null);
+    }
+    addNode(className, description, sourceInfo);
   }
 
-  public void addNode(JNode node) {
-    // TODO Auto-generated method stub
+  /**
+   * Adds information about a a node to the end of the node trace. This is
+   * similar to how a stack trace works.
+   */
+  public void addNode(String className, String description,
+      JSourceInfo sourceInfo) {
+    nodeTrace.add(new NodeInfo(className, description, sourceInfo));
+  }
+
+  /**
+   * Returns a list of nodes that were being processed when this exception was
+   * thrown. The list reflects the parent-child relationships of the AST and is
+   * is in order from children to parents. The first element of the returned
+   * list is the node that was most specifically being visited when the
+   * exception was thrown.
+   */
+  public List getNodeTrace() {
+    return nodeTrace;
   }
 
 }
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 ac61804..27ae042 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
@@ -49,7 +49,9 @@
   public JNode get(Binding binding) {
     JNode result = internalGet(binding);
     if (result == null) {
-      throw new RuntimeException("Failed to get JNode");
+      InternalCompilerException ice = new InternalCompilerException(
+          "Failed to get JNode");
+      ice.addNode(binding.getClass().getName(), binding.toString(), null);
     }
     return result;
   }