Adds the remaining (and more complex) Java 7 new language features.

Adds the remaining Java 7 new language features: namely, multiexception catch
and try-with-resources.

Fixes issue 7999, issue 6960.

Change-Id: If973b8c847d1202aca794221f32ae4b33b616f9c
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
index 6c12533..d149f4f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
@@ -50,7 +50,7 @@
   /**
    * Construct a bare-bones deserialized external class.
    */
-  private JClassType(String name) {
+  JClassType(String name) {
     super(SourceOrigin.UNKNOWN, name);
     isAbstract = false;
     setExternal(true);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index dede802..f11627e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -47,7 +47,7 @@
     }
 
     private Object readResolve() {
-      return new JMethod(signature, enclosingType);
+      return new JMethod(signature, enclosingType, false);
     }
   }
 
@@ -141,15 +141,30 @@
   }
 
   /**
+   * Creates an externalized representation for a method that needs to be resolved.
+   * Useful to refer to methods of magic classes during GwtAstBuilder execution.
+   *
+   * @param fullClassName the class where the method is defined.
+   * @param signature the signature of the method (including its name).
+   *
+   */
+  public static JMethod getExternalizedMethod(String fullClassName, String signature,
+      boolean isStatic) {
+
+    JClassType cls = new JClassType(fullClassName);
+    return new JMethod(signature, cls, isStatic);
+  }
+
+  /**
    * Construct a bare-bones deserialized external method.
    */
-  private JMethod(String signature, JDeclaredType enclosingType) {
+  private JMethod(String signature, JDeclaredType enclosingType, boolean isStatic) {
     super(SourceOrigin.UNKNOWN);
     this.name = signature.substring(0, signature.indexOf('('));
     this.enclosingType = enclosingType;
     this.signature = signature;
     this.isAbstract = false;
-    this.isStatic = false;
+    this.isStatic = isStatic;
     this.access = AccessModifier.PUBLIC.ordinal();
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
index 9afa7be..ee89165 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
@@ -100,7 +100,8 @@
    * allows us to preserve type information during the latter phases of
    * compilation.
    */
-  public JMethodCall(SourceInfo info, JExpression instance, JMethod method, JType overrideReturnType) {
+  public JMethodCall(SourceInfo info, JExpression instance, JMethod method,
+      JType overrideReturnType) {
     super(info);
     assert (method != null);
     assert (instance != null || method.isStatic());
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTryStatement.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTryStatement.java
index 1f2d0ca..1c2a707 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTryStatement.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTryStatement.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dev.jjs.SourceInfo;
 
+import java.io.Serializable;
 import java.util.List;
 
 /**
@@ -24,27 +25,58 @@
  */
 public class JTryStatement extends JStatement {
 
-  private final List<JLocalRef> catchArgs;
-  private final List<JBlock> catchBlocks;
+  /**
+   * Represents the catch clause parts of the try statement.
+   */
+  public static class CatchClause implements Serializable {
+    private final List<JType> catchTypes;
+    private final JLocalRef arg;
+    private final JBlock block;
+
+    public CatchClause(List<JType> catchTypes, JLocalRef arg, JBlock block) {
+      this.catchTypes = catchTypes;
+      this.arg = arg;
+      this.block = block;
+    }
+
+    public List<JType> getTypes() {
+      return catchTypes;
+    }
+
+    public JLocalRef getArg() {
+      return arg;
+    }
+
+    public JBlock getBlock() {
+      return block;
+    }
+  }
+
+  private final List<CatchClause> catchClauses;
   private final JBlock finallyBlock;
   private final JBlock tryBlock;
 
-  public JTryStatement(SourceInfo info, JBlock tryBlock, List<JLocalRef> catchArgs,
-      List<JBlock> catchBlocks, JBlock finallyBlock) {
+  /**
+   * Construct a Java try statement.
+   *
+   * Parameters catchTypes, catchArgs and catchBlocks must agree on size. Each element of each
+   * of these lists corresponds to a catch statement.
+   *
+   * @param info the source information.
+   * @param tryBlock the statement block inside the try construct.
+   * @param catchClauses  each element of this list contains a catch clause.
+   * @param finallyBlock the statement block corresponding to the finally construct.
+   */
+  public JTryStatement(SourceInfo info, JBlock tryBlock, List<CatchClause> catchClauses,
+      JBlock finallyBlock) {
     super(info);
-    assert (catchArgs.size() == catchBlocks.size());
     this.tryBlock = tryBlock;
-    this.catchArgs = catchArgs;
-    this.catchBlocks = catchBlocks;
+    this.catchClauses = catchClauses;
     this.finallyBlock = finallyBlock;
   }
 
-  public List<JLocalRef> getCatchArgs() {
-    return catchArgs;
-  }
-
-  public List<JBlock> getCatchBlocks() {
-    return catchBlocks;
+  public List<CatchClause> getCatchClauses() {
+    return catchClauses;
   }
 
   public JBlock getFinallyBlock() {
@@ -58,8 +90,11 @@
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
       visitor.accept(tryBlock);
-      visitor.accept(catchArgs);
-      visitor.accept(catchBlocks);
+
+      for (CatchClause clause : catchClauses) {
+        visitor.accept(clause.getArg());
+        visitor.accept(clause.getBlock());
+      }
       // TODO: normalize this so it's never null?
       if (finallyBlock != null) {
         visitor.accept(finallyBlock);
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 c055674..f903e8b 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
@@ -17,6 +17,8 @@
 
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JBinaryOperation;
+import com.google.gwt.dev.jjs.ast.JBinaryOperator;
 import com.google.gwt.dev.jjs.ast.JBlock;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JExpression;
@@ -28,11 +30,13 @@
 import com.google.gwt.dev.jjs.ast.JMethodBody;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JStatement;
 import com.google.gwt.dev.jjs.ast.JThrowStatement;
 import com.google.gwt.dev.jjs.ast.JTryStatement;
+import com.google.gwt.dev.jjs.ast.JType;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -58,11 +62,11 @@
     // @Override
     @Override
     public void endVisit(JTryStatement x, Context ctx) {
-      if (x.getCatchBlocks().isEmpty()) {
+      if (x.getCatchClauses().isEmpty()) {
         return;
       }
 
-      SourceInfo catchInfo = x.getCatchBlocks().get(0).getSourceInfo();
+      SourceInfo catchInfo = x.getCatchClauses().get(0).getBlock().getSourceInfo();
       JLocal exVar = popTempLocal();
       JBlock newCatchBlock = new JBlock(catchInfo);
 
@@ -77,19 +81,34 @@
 
       /*
        * Build up a series of if, else if statements to test the type of the
-       * exception object against the type of the user's catch block.
+       * exception object against the types of the user's catch block. Each catch block might have
+       * multiple types in Java 7.
        * 
        * Go backwards so we can nest the else statements in the correct order!
        */
-      // rethrow the current exception if no one caught it
+      // rethrow the current exception if no one caught it.
       JStatement cur = new JThrowStatement(catchInfo, new JLocalRef(catchInfo, exVar));
-      for (int i = x.getCatchBlocks().size() - 1; i >= 0; --i) {
-        JBlock block = x.getCatchBlocks().get(i);
-        JLocalRef arg = x.getCatchArgs().get(i);
+      for (int i = x.getCatchClauses().size() - 1; i >= 0; i--) {
+        JTryStatement.CatchClause clause = x.getCatchClauses().get(i);
+        JBlock block = clause.getBlock();
+        JLocalRef arg = clause.getArg();
+        List<JType> exceptionsTypes = clause.getTypes();
         catchInfo = block.getSourceInfo();
-        JReferenceType argType = (JReferenceType) arg.getType();
-        // if ($e instanceof ArgType) { var userVar = $e; <user code> }
-        JExpression ifTest = new JInstanceOf(catchInfo, argType, new JLocalRef(catchInfo, exVar));
+
+        // if ($e instanceof ArgType1 or $e instanceof ArgType2 ...) {
+        //   var userVar = $e; <user code>
+        // }
+
+        // Handle the first Exception type.
+        JExpression ifTest = new JInstanceOf(catchInfo, (JReferenceType) exceptionsTypes.get(0),
+            new JLocalRef(catchInfo, exVar));
+        // Handle the rest of the Exception types if any.
+        for (int j = 1; j < exceptionsTypes.size(); j++) {
+          JExpression orExp = new JInstanceOf(catchInfo, (JReferenceType) exceptionsTypes.get(j),
+              new JLocalRef(catchInfo, exVar));
+          ifTest = new JBinaryOperation(catchInfo, JPrimitiveType.BOOLEAN, JBinaryOperator.OR,
+              ifTest, orExp);
+        }
         JDeclarationStatement declaration =
             new JDeclarationStatement(catchInfo, arg, new JLocalRef(catchInfo, exVar));
         block.addStmt(0, declaration);
@@ -98,10 +117,13 @@
       }
 
       newCatchBlock.addStmt(cur);
-      x.getCatchArgs().clear();
-      x.getCatchArgs().add(new JLocalRef(newCatchBlock.getSourceInfo(), exVar));
-      x.getCatchBlocks().clear();
-      x.getCatchBlocks().add(newCatchBlock);
+
+      // Replace with a single catch block.
+      x.getCatchClauses().clear();
+      List<JType> newCatchTypes = new ArrayList<JType>(1);
+      newCatchTypes.add(exVar.getType());
+      x.getCatchClauses().add(new JTryStatement.CatchClause(newCatchTypes,
+          new JLocalRef(newCatchBlock.getSourceInfo(), exVar), newCatchBlock));
     }
 
     // @Override
@@ -115,7 +137,7 @@
     // @Override
     @Override
     public boolean visit(JTryStatement x, Context ctx) {
-      if (!x.getCatchBlocks().isEmpty()) {
+      if (!x.getCatchClauses().isEmpty()) {
         pushTempLocal(x.getSourceInfo());
       }
       return true;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index 0907c57..2c778cf 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -579,24 +579,31 @@
     @Override
     public void endVisit(JTryStatement x, Context ctx) {
       // 1) Remove catch blocks whose exception type is not instantiable.
-      List<JLocalRef> catchArgs = x.getCatchArgs();
-      List<JBlock> catchBlocks = x.getCatchBlocks();
-      Iterator<JLocalRef> itA = catchArgs.iterator();
-      Iterator<JBlock> itB = catchBlocks.iterator();
-      while (itA.hasNext()) {
-        JLocalRef localRef = itA.next();
-        itB.next();
-        JReferenceType type = (JReferenceType) localRef.getType();
-        if (!program.typeOracle.isInstantiatedType(type) || type == program.getTypeNull()) {
-          itA.remove();
-          itB.remove();
+      List<JTryStatement.CatchClause> catchClauses = x.getCatchClauses();
+
+      Iterator<JTryStatement.CatchClause> itClauses = catchClauses.iterator();
+      while (itClauses.hasNext()) {
+        JTryStatement.CatchClause clause = itClauses.next();
+        // Go over the types in the multiexception and remove the ones that are not instantiable.
+        Iterator<JType> itTypes = clause.getTypes().iterator();
+        while (itTypes.hasNext()) {
+          JReferenceType type = (JReferenceType) itTypes.next();
+          if (!program.typeOracle.isInstantiatedType(type) || type == program.getTypeNull()) {
+            itTypes.remove();
+            madeChanges();
+          }
+        }
+
+        // if all exception types are gone then remove whole clause.
+        if (clause.getTypes().isEmpty()) {
+          itClauses.remove();
           madeChanges();
         }
       }
 
       // Compute properties regarding the state of this try statement
       boolean noTry = Simplifier.isEmpty(x.getTryBlock());
-      boolean noCatch = catchArgs.size() == 0;
+      boolean noCatch = catchClauses.size() == 0;
       boolean noFinally = Simplifier.isEmpty(x.getFinallyBlock());
 
       if (noTry) {
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 81e13ee..c0e1423 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
@@ -432,12 +432,9 @@
     @Override
     public boolean visit(JTryStatement x, Context ctx) {
       accept(x.getTryBlock());
-
-      List<JLocalRef> catchArgs = x.getCatchArgs();
-      List<JBlock> catchBlocks = x.getCatchBlocks();
-      for (int i = 0, c = catchArgs.size(); i < c; ++i) {
-        JLocalRef arg = catchArgs.get(i);
-        JBlock catchBlock = catchBlocks.get(i);
+      for (JTryStatement.CatchClause clause : x.getCatchClauses()) {
+        JLocalRef arg = clause.getArg();
+        JBlock catchBlock = clause.getBlock();
         JsCatch jsCatch = new JsCatch(x.getSourceInfo(), peek(), arg.getTarget().getName());
         JsParameter jsParam = jsCatch.getParameter();
         names.put(arg.getTarget(), jsParam.getName());
@@ -1466,12 +1463,12 @@
         }
       }
 
-      int size = x.getCatchArgs().size();
-      assert (size < 2 && size == x.getCatchBlocks().size());
+      int size = x.getCatchClauses().size();
+      assert (size < 2);
       if (size == 1) {
         JsBlock catchBlock = (JsBlock) pop(); // catchBlocks
         pop(); // catchArgs
-        JsCatch jsCatch = catchMap.get(x.getCatchBlocks().get(0));
+        JsCatch jsCatch = catchMap.get(x.getCatchClauses().get(0).getBlock());
         jsCatch.setBody(catchBlock);
         jsTry.getCatches().add(jsCatch);
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index c6c75e5..efa5c90 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -177,7 +177,9 @@
 import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
 import org.eclipse.jdt.internal.compiler.ast.TryStatement;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
 import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
+import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.WhileStatement;
 import org.eclipse.jdt.internal.compiler.impl.Constant;
 import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
@@ -286,7 +288,8 @@
         }
       }
 
-      private void processClassLiteral(JsNameRef nameRef, SourceInfo info, JType type, JsContext ctx) {
+      private void processClassLiteral(JsNameRef nameRef, SourceInfo info, JType type,
+          JsContext ctx) {
         assert !ctx.isLvalue();
         JsniClassLiteral classLiteral = new JsniClassLiteral(info, nameRef.getIdent(), type);
         nativeMethodBody.addClassRef(classLiteral);
@@ -1423,19 +1426,162 @@
         List<JBlock> catchBlocks = pop(x.catchBlocks);
         JBlock tryBlock = pop(x.tryBlock);
 
-        List<JLocalRef> catchArgs = new ArrayList<JLocalRef>();
+        if (x.resources.length > 0) {
+          tryBlock = normalizeTryWithResources(info, x, tryBlock, scope);
+        }
+        List<JTryStatement.CatchClause> catchClauses =
+            new ArrayList<JTryStatement.CatchClause>();
         if (x.catchBlocks != null) {
-          for (Argument argument : x.catchArguments) {
+          for (int i = 0; i < x.catchArguments.length; i++) {
+            Argument argument = x.catchArguments[i];
             JLocal local = (JLocal) curMethod.locals.get(argument.binding);
-            catchArgs.add(new JLocalRef(info, local));
+
+            List<JType> catchTypes = new ArrayList<JType>();
+            if (argument.type instanceof UnionTypeReference) {
+              // This is a multiexception
+              for (TypeReference type : ((UnionTypeReference) argument.type).typeReferences) {
+                catchTypes.add(typeMap.get(type.resolvedType));
+              }
+            } else {
+              // Regular exception
+              catchTypes.add(local.getType());
+            }
+            catchClauses.add(new JTryStatement.CatchClause(catchTypes, new JLocalRef(info, local),
+                catchBlocks.get(i)));
           }
         }
-        push(new JTryStatement(info, tryBlock, catchArgs, catchBlocks, finallyBlock));
+        push(new JTryStatement(info, tryBlock, catchClauses, finallyBlock));
       } catch (Throwable e) {
         throw translateException(x, e);
       }
     }
 
+    private JBlock normalizeTryWithResources(SourceInfo info, TryStatement x, JBlock tryBlock,
+        BlockScope scope) {
+      /**
+       * Apply the following source transformation:
+       *
+       * try (A1 a1 = new A1(); ... ; An an = new An()) {
+       *   ... tryBlock...
+       *  } ...catch/finally blocks
+       *
+       *  to
+       *
+       * try {
+       *   A1 a1 = new A1();... ; An an = new An();
+       *   Throwable $exception = null;
+       *   try {
+       *     ... tryBlock...
+       *   } catch (Throwable t) {
+       *     $exception = t;
+       *     throw t;
+       *   } finally {
+       *    $exception = Exceptions.safeClose(an, $exception);
+       *    ...
+       *    $exception = Exceptions.safeClose(a1, $exception);
+       *  if ($exception != null) {
+       *    throw $exception;
+       *  }
+       * } ...catch/finally blocks
+       *
+       */
+
+      JBlock innerBlock = new JBlock(info);
+      // add resource variables
+      List<JLocal> resourceVariables = new ArrayList<JLocal>();
+      for (int i = x.resources.length - 1; i >= 0; i--) {
+        // Needs to iterate back to front to be inline with the contents of the stack.
+
+        JDeclarationStatement resourceDecl = pop(x.resources[i]);
+
+        JLocal resourceVar = (JLocal) curMethod.locals.get(x.resources[i].binding);
+        resourceVariables.add(0, resourceVar);
+        innerBlock.addStmt(0, resourceDecl);
+      }
+
+      // add exception variable
+      JLocal exceptionVar =
+          createTempLocal(info, "$primary_ex", javaLangThrowable, false, curMethod.body);
+
+      innerBlock.addStmt(makeDeclaration(info, exceptionVar, JNullLiteral.INSTANCE));
+
+      // create catch block
+      List<JTryStatement.CatchClause> catchClauses = new ArrayList<JTryStatement.CatchClause>(1);
+
+      List<JType> clauseTypes = new ArrayList<JType>(1);
+      clauseTypes.add(javaLangThrowable);
+
+      //     add catch exception variable.
+      JLocal catchVar =
+          createTempLocal(info, "$caught_ex", javaLangThrowable, false, curMethod.body);
+
+      JBlock catchBlock = new JBlock(info);
+      catchBlock.addStmt(createAssignment(info, javaLangThrowable, exceptionVar, catchVar));
+      catchBlock.addStmt(new JThrowStatement(info, new JLocalRef(info, exceptionVar)));
+
+      catchClauses.add(new JTryStatement.CatchClause(clauseTypes, new JLocalRef(info, catchVar),
+          catchBlock));
+
+      // create finally block
+      JBlock finallyBlock = new JBlock(info);
+      for (int i = x.resources.length - 1; i >= 0; i--) {
+        finallyBlock.addStmt(createCloseBlockFor(info, x.resources[i],
+            resourceVariables.get(i), exceptionVar, scope));
+      }
+
+      // if (exception != null) throw exception
+      JExpression exceptionNotNull = new JBinaryOperation(info, JPrimitiveType.BOOLEAN,
+          JBinaryOperator.NEQ, new JLocalRef(info, exceptionVar), JNullLiteral.INSTANCE);
+      finallyBlock.addStmt(new JIfStatement(info, exceptionNotNull,
+          new JThrowStatement(info, new JLocalRef(info, exceptionVar)), null));
+
+      // Stitch all together into a inner try block
+      innerBlock.addStmt(new JTryStatement(info, tryBlock, catchClauses,
+            finallyBlock));
+      return innerBlock;
+    }
+
+    private JLocal createTempLocal(SourceInfo info, String prefix, JType type, boolean isFinal,
+        JMethodBody enclosingMethodBody) {
+      int index = curMethod.body.getLocals().size() + 1;
+      return JProgram.createLocal(info, prefix + "_" + index,
+            javaLangThrowable, false, curMethod.body);
+    }
+
+    private JStatement createCloseBlockFor(final SourceInfo info, final LocalDeclaration resource,
+        JLocal resourceVar, JLocal exceptionVar, BlockScope scope) {
+      /**
+       * Create the following code:
+       *
+       * $ex = Exceptions.safeClose(resource, $ex);
+       *
+       * which is equivalent to
+       *
+       * if (resource != null) {
+       *   try {
+       *     resource.close();
+       *   } catch (Throwable t) {
+       *     if ($ex == null) {
+       *       $ex = t;
+       *     } else {
+       *      $ex.addSuppressed(t);
+       *     }
+       *   }
+       */
+
+      JMethodCall safeCloseCall = new JMethodCall(info, null, SAFE_CLOSE_METHOD);
+      safeCloseCall.addArg(0, new JLocalRef(info, resourceVar));
+      safeCloseCall.addArg(1, new JLocalRef(info, exceptionVar));
+
+      return new JBinaryOperation(info, javaLangThrowable, JBinaryOperator.ASG, new JLocalRef(info,
+          exceptionVar), safeCloseCall).makeStatement();
+    }
+
+    private JStatement createAssignment(SourceInfo info, JType type, JLocal lhs, JLocal rhs) {
+      return new JBinaryOperation(info, type, JBinaryOperator.ASG, new JLocalRef(info, lhs),
+          new JLocalRef(info, rhs)).makeStatement();
+    }
+
     @Override
     public void endVisit(TypeDeclaration x, ClassScope scope) {
       endVisit(x);
@@ -2784,6 +2930,8 @@
   private static final char[] VALUE = "Value".toCharArray();
   private static final char[] VALUE_OF = "valueOf".toCharArray();
   private static final char[] VALUES = "values".toCharArray();
+  private static final char[] CLOSE = "close".toCharArray();
+  private static final char[] ADDSUPRESSED = "addSuppressed".toCharArray();
 
   static {
     InternalCompilerException.preload();
@@ -2896,6 +3044,8 @@
 
   JClassType javaLangString = null;
 
+  JClassType javaLangThrowable = null;
+
   Map<MethodDeclaration, JsniMethod> jsniMethods;
 
   Map<String, Binding> jsniRefs;
@@ -2909,6 +3059,16 @@
   private String sourceMapPath;
 
   /**
+   * Externalized class and method form for Exceptions.safeClose() to provide support
+   * for try-with-resources.
+   *
+   * The externalized form will be resolved during AST stitching.
+   */
+  static JMethod SAFE_CLOSE_METHOD = JMethod.getExternalizedMethod("com.google.gwt.lang.Exceptions",
+      "safeClose(Ljava/lang/AutoCloseable;Ljava/lang/Throwable;)Ljava/lang/Throwable;", true);
+
+
+  /**
    * Builds all the GWT AST nodes that correspond to one Java source file.
    *
    * @param cud The compiled form of the Java source from the JDT.
@@ -2939,6 +3099,7 @@
     javaLangObject = (JClassType) typeMap.get(cud.scope.getJavaLangObject());
     javaLangString = (JClassType) typeMap.get(cud.scope.getJavaLangString());
     javaLangClass = (JClassType) typeMap.get(cud.scope.getJavaLangClass());
+    javaLangThrowable = (JClassType) typeMap.get(cud.scope.getJavaLangThrowable());
 
     for (TypeDeclaration typeDecl : cud.types) {
       // Resolve super type / interface relationships.
@@ -2964,7 +3125,7 @@
     javaLangObject = null;
     javaLangString = null;
     javaLangClass = null;
-
+    javaLangThrowable = null;
     return result;
   }
 
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 691752a..d2c69de 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
@@ -132,6 +132,7 @@
   protected static final char[] CHARS_NATIVE = "native ".toCharArray();
   protected static final char[] CHARS_NEW = "new ".toCharArray();
   protected static final char[] CHARS_NULL = "null".toCharArray();
+  protected static final char[] CHARS_PIPE = " | ".toCharArray();
   protected static final char[] CHARS_PRIVATE = "private ".toCharArray();
   protected static final char[] CHARS_PROTECTED = "protected ".toCharArray();
   protected static final char[] CHARS_PUBLIC = "public ".toCharArray();
@@ -887,15 +888,22 @@
   public boolean visit(JTryStatement x, Context ctx) {
     print(CHARS_TRY);
     accept(x.getTryBlock());
-    for (int i = 0, c = x.getCatchArgs().size(); i < c; ++i) {
+    for (JTryStatement.CatchClause clause : x.getCatchClauses()) {
       print(CHARS_CATCH);
       lparen();
-      JLocalRef localRef = x.getCatchArgs().get(i);
-      accept(localRef.getTarget());
+
+      Iterator<JType> it = clause.getTypes().iterator();
+      printTypeName(it.next());
+      while (it.hasNext()) {
+        print(CHARS_PIPE);
+        printTypeName(it.next());
+      }
+      space();
+
+      printName(clause.getArg().getTarget());
       rparen();
       space();
-      JBlock block = x.getCatchBlocks().get(i);
-      accept(block);
+      accept(clause.getBlock());
     }
     if (x.getFinallyBlock() != null) {
       print(CHARS_FINALLY);
@@ -1150,11 +1158,15 @@
   }
 
   protected void visitCollectionWithCommas(Iterator<? extends JNode> iter) {
+    visitCollectionWith(CHARS_COMMA, iter);
+  }
+
+  protected void visitCollectionWith(char[] ch, Iterator<? extends JNode> iter) {
     if (iter.hasNext()) {
       accept(iter.next());
     }
     while (iter.hasNext()) {
-      print(CHARS_COMMA);
+      print(ch);
       accept(iter.next());
     }
   }
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 ae6205c..5ac6ea7 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
@@ -34,7 +34,6 @@
 import com.google.gwt.dev.jjs.ast.JInstanceOf;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JLocal;
-import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
@@ -291,8 +290,8 @@
     public void endVisit(JTryStatement x, Context ctx) {
       // Never tighten args to catch blocks
       // Fake an assignment-to-self to prevent tightening
-      for (JLocalRef arg : x.getCatchArgs()) {
-        addAssignment(arg.getTarget(), arg);
+      for (JTryStatement.CatchClause clause : x.getCatchClauses()) {
+        addAssignment(clause.getArg().getTarget(), clause.getArg());
       }
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
index 5aa134c..346eff1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
@@ -61,6 +61,7 @@
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JThisRef;
+import com.google.gwt.dev.jjs.ast.JTryStatement;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
@@ -318,6 +319,20 @@
     }
 
     @Override
+    public void endVisit(JTryStatement x, Context ctx) {
+      // Needs to resolve the Exceptions Types explicitly they are multiple in Java 7 and
+      // potentially different from the one in the exception variable.
+      for (JTryStatement.CatchClause clause : x.getCatchClauses()) {
+        List<JType> types = clause.getTypes();
+        for (int i = 0; i <  types.size(); i++) {
+          JReferenceType resolvedType = translate((JReferenceType) types.get(i));
+          assert resolvedType.replaces(types.get(i));
+          types.set(i, resolvedType);
+        }
+      }
+    }
+
+    @Override
     public void endVisit(JVariable x, Context ctx) {
       x.setType(translate(x.getType()));
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilder.java
index f643eb7..9b7f6d3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/cfg/CfgBuilder.java
@@ -763,7 +763,8 @@
       List<Integer> catchBlockPos = new ArrayList<Integer>();
       List<List<Exit>> catchExits = new ArrayList<List<Exit>>();
 
-      for (JBlock b : x.getCatchBlocks()) {
+      for (JTryStatement.CatchClause clause : x.getCatchClauses()) {
+        JBlock b = clause.getBlock();
         catchBlockPos.add(nodes.size());
         accept(b);
         catchExits.add(removeCurrentExits());
@@ -790,12 +791,16 @@
           if (e.isThrow()) {
             // If execution of the try block completes abruptly because of a
             // throw of a value V, then there is a choice:
-            nextCatchBlock : for (int i = 0; i < x.getCatchArgs().size(); ++i) {
+            nextCatchBlock : for (int i = 0; i < x.getCatchClauses().size(); ++i) {
               // If the run-time type of V is assignable (�5.2) to the
               // Parameter of any catch clause of the try statement, then
               // the first (leftmost) such catch clause is selected.
-              JClassType catchType = 
-                (JClassType) x.getCatchArgs().get(i).getType();
+
+              // TODO(rluble): we are safely overapproximating the exception
+              // caught in this block by the type of the exception variable.
+              // We could do better in multiexceptions.
+              JClassType catchType =
+                (JClassType) x.getCatchClauses().get(i).getArg().getType();
               JType exceptionType = e.getExceptionType();
 
               boolean canCatch = false;
@@ -806,7 +811,12 @@
                 // Catch clause fully covers exception type. We'll land
                 // here for sure.
                 canCatch = true;
-                fullCatch = true;
+                // Safe approximation. If it is a multi exception with only one
+                // exception declared in the clause then the variable is of the
+                // the same type as the exception declared  and the approximation
+                // is exact hence and this is a full catch. 
+                fullCatch = x.getCatchClauses().get(i).getTypes().size() == 1 &&
+                    x.getCatchClauses().get(i).getTypes().get(0) == catchType;
               } else if (typeOracle.canTriviallyCast(catchType, exceptionType)) {
                 // We can land here if we throw some subclass of
                 // exceptionType
@@ -864,12 +874,16 @@
             // If execution of the try block completes abruptly because of a
             // throw of a value V, then there is a choice:
 
-            nextCatchBlock : for (int i = 0; i < x.getCatchArgs().size(); ++i) {
+            nextCatchBlock : for (int i = 0; i < x.getCatchClauses().size(); ++i) {
               // If the run-time type of V is assignable to the parameter of any
               // catch clause of the try statement, then the first
               // (leftmost) such catch clause is selected.
-              JClassType catchType = 
-                (JClassType) x.getCatchArgs().get(i).getType();
+
+              // TODO(rluble): we are safely overapproximating the exception
+              // caught in this block by the type of the exception variable.
+              // We could do better in multiexceptions.
+              JClassType catchType =
+                (JClassType) x.getCatchClauses().get(i).getArg().getType();
               JType exceptionType = e.getExceptionType();
 
               boolean canCatch = false;
@@ -880,7 +894,12 @@
                 // Catch clause fully covers exception type. We'll land
                 // here for sure.
                 canCatch = true;
-                fullCatch = true;
+                // Safe approximation. If it is a multi exception with only one
+                // exception declared in the clause then the variable is of the
+                // the same type as the exception declared  and the approximation
+                // is exact hence and this is a full catch. 
+                fullCatch = x.getCatchClauses().get(i).getTypes().size() == 1 &&
+                    x.getCatchClauses().get(i).getTypes().get(0) == catchType;
               } else if (typeOracle.canTriviallyCast(catchType, exceptionType)) {
                 // We can land here if we throw some subclass of
                 // exceptionType
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Exceptions.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Exceptions.java
index c437453..09e4bab 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Exceptions.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Exceptions.java
@@ -65,5 +65,29 @@
   static boolean throwAssertionError_Object(Object message) {
     throw new AssertionError(message);
   }
+
+  /**
+   * Use by the try-with-resources construct. Look at
+   * {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder.createCloseBlockFor}.
+   *
+   * @param resource a resource implementing the AutoCloseable interface.
+   * @param mainException  an exception being propagated.
+   * @return an exception to propagate or {@code null} if none.
+   */
+  static Throwable safeClose(AutoCloseable resource, Throwable mainException) {
+    if (resource == null) {
+      return mainException;
+    }
+
+    try {
+      resource.close();
+    } catch (Throwable e) {
+      if (mainException == null) {
+        return e;
+      }
+      mainException.addSuppressed(e);
+    }
+    return mainException;
+  }
   // CHECKSTYLE_ON
 }
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JJSTestBase.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JJSTestBase.java
index 747ff4a..a17a51e 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JJSTestBase.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JJSTestBase.java
@@ -255,12 +255,29 @@
       }
     });
 
+    sourceOracle.addOrReplace(new MockJavaResource("java.lang.AutoCloseable") {
+      @Override
+      public CharSequence getContent() {
+        return ""
+            + "package java.lang;"
+            + "public interface AutoCloseable { "
+            + "  void close() throws Exception;"
+            + "}";
+      }
+    });
+
     sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.lang.Exceptions") {
       @Override
       public CharSequence getContent() {
-        return "package com.google.gwt.lang;" +
-          "public class Exceptions { static boolean throwAssertionError() { throw new RuntimeException(); } }";
-      }
+        return ""
+            + "package com.google.gwt.lang;"
+            + "public class Exceptions { "
+            + "  static boolean throwAssertionError() { throw new RuntimeException(); }"
+            + "  static Throwable safeClose(AutoCloseable resource, Throwable mainException) {"
+            + "    return mainException;"
+            + "  } "
+            + "}";
+        }
     });
 
     sourceOracle.addOrReplace(new MockJavaResource("java.lang.String") {
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/Java7AstTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/Java7AstTest.java
index 5e261ea..3ed0dbf 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/Java7AstTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/Java7AstTest.java
@@ -32,10 +32,14 @@
  */
 public class Java7AstTest extends JJSTestBase {
 
+  // TODO(rluble): add similar tests to ensure that the AST construction is correct for all types
+  // of nodes.
   @Override
   public void setUp() {
     sourceLevel = SourceLevel.JAVA7;
-    addAll(Java7MockResources.LIST_T, Java7MockResources.ARRAYLIST_T);
+    addAll(Java7MockResources.LIST_T, Java7MockResources.ARRAYLIST_T,
+        JavaResourceBase.AUTOCLOSEABLE, Java7MockResources.TEST_RESOURCE,
+        Java7MockResources.EXCEPTION1, Java7MockResources.EXCEPTION2);
   }
 
   public void testCompileNewStyleLiterals() throws Exception {
@@ -115,5 +119,4 @@
     JMethodBody body = (JMethodBody) mainMethod.getBody();
     return body.getBlock();
   }
-
 }
diff --git a/user/src/com/google/gwt/core/shared/SerializableThrowable.java b/user/src/com/google/gwt/core/shared/SerializableThrowable.java
index 82ccaae..cbbd612 100644
--- a/user/src/com/google/gwt/core/shared/SerializableThrowable.java
+++ b/user/src/com/google/gwt/core/shared/SerializableThrowable.java
@@ -22,6 +22,7 @@
  * A serializable copy of a {@link Throwable}, including its causes and stack trace. It overrides
  * {@code #toString} to mimic original {@link Throwable#toString()} so that {@link #printStackTrace}
  * will work as if it is coming from the original exception.
+ *
  * <p>
  * This class is especially useful for logging and testing as the emulated Throwable class does not
  * serialize recursively and does not serialize the stack trace. This class, as an alternative, can
@@ -30,6 +31,8 @@
  * <p>
  * Please note that, to get more useful stack traces from client side, this class needs to be used
  * in conjunction with {@link com.google.gwt.core.server.StackTraceDeobfuscator}.
+ * <p>
+ * NOTE: Does not serialize suppressed exceptions to remain compatible with Java 6 and below.
  */
 public final class SerializableThrowable extends Throwable {
 
diff --git a/user/super/com/google/gwt/emul/java/lang/AutoCloseable.java b/user/super/com/google/gwt/emul/java/lang/AutoCloseable.java
new file mode 100644
index 0000000..7bf4144
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/lang/AutoCloseable.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 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 java.lang;
+
+/**
+ * See <a
+ * href="http://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html">the
+ * official Java API doc</a> for details.
+ */
+public interface AutoCloseable {
+
+  /**
+   * Closes this resource.
+   */
+  void close() throws Exception;
+}
diff --git a/user/super/com/google/gwt/emul/java/lang/Throwable.java b/user/super/com/google/gwt/emul/java/lang/Throwable.java
index dd9ecb9..e929e59 100644
--- a/user/super/com/google/gwt/emul/java/lang/Throwable.java
+++ b/user/super/com/google/gwt/emul/java/lang/Throwable.java
@@ -37,30 +37,73 @@
    * to ensure that only the detailMessage field is serialized. Changing the
    * field modifiers below may necessitate a change to the server's
    * SerializabilityUtil.fieldQualifiesForSerialization(Field) method.
+   *
+   * TODO(rluble): Add remaining functionality for suppressed Exceptions (e.g.
+   * printing). Also review the class for missing Java 7 compatibility.
    */
   private transient Throwable cause;
   private String detailMessage;
+  private transient Throwable[] suppressedExceptions;
   private transient StackTraceElement[] stackTrace;
-
-  {
-    fillInStackTrace();
-  }
+  private transient boolean disableSuppression;
 
   public Throwable() {
+    fillInStackTrace();
   }
 
   public Throwable(String message) {
     this.detailMessage = message;
+    fillInStackTrace();
   }
 
   public Throwable(String message, Throwable cause) {
     this.cause = cause;
     this.detailMessage = message;
+    fillInStackTrace();
   }
 
   public Throwable(Throwable cause) {
     this.detailMessage = (cause == null) ? null : cause.toString();
     this.cause = cause;
+    fillInStackTrace();
+  }
+
+  /**
+   * Constructor that allows subclasses disabling exception suppression and stack traces.
+   * Those features should only be disabled in very specific cases.
+   */
+  protected Throwable(String message, Throwable cause, boolean enableSuppression,
+      boolean writetableStackTrace) {
+    if (writetableStackTrace) {
+      fillInStackTrace();
+    }
+    this.cause = cause;
+    this.detailMessage = message;
+    this.disableSuppression = !enableSuppression;
+  }
+
+  /**
+   * Call to add an exception that was suppressed. Used by try-with-resources.
+   */
+  public final void addSuppressed(Throwable exception) {
+    if (exception == null) {
+      throw new NullPointerException("Cannot suppress a null exception.");
+    }
+    if (exception == this) {
+      throw new IllegalArgumentException("Exception can not suppress itself.");
+    }
+
+    if (disableSuppression) {
+      return;
+    }
+
+    if (suppressedExceptions == null) {
+      suppressedExceptions = new Throwable[] { exception };
+    } else {
+      // TRICK: This is not correct Java (would give an OOBE, but it works in JS and
+      // this code will only be executed in JS.
+      suppressedExceptions[suppressedExceptions.length] = exception;
+    }
   }
 
   /**
@@ -97,6 +140,17 @@
     return stackTrace;
   }
 
+  /**
+   * Returns the array of Exception that this one suppressedExceptions.
+   */
+  public final Throwable[] getSuppressed() {
+    if (suppressedExceptions == null) {
+      suppressedExceptions = new Throwable[0];
+    }
+
+    return suppressedExceptions;
+  }
+
   public Throwable initCause(Throwable cause) {
     if (this.cause != null) {
       throw new IllegalStateException("Can't overwrite cause");
diff --git a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java7Test.java b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java7Test.java
index f849634..26a32ac 100644
--- a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java7Test.java
+++ b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java7Test.java
@@ -63,4 +63,245 @@
     }
     assertEquals(1, result);
   }
+
+  final List<String> log = new ArrayList<String>();
+
+  public class Resource implements AutoCloseable {
+
+    String name;
+    public Resource(String name) {
+      this.name = name;
+      log.add("Open " + name);
+    }
+
+    public void doSomething() {
+      log.add("doSomething " + name);
+    }
+
+    public void throwException(String text) throws E1 {
+      throw new E1(text + " in " + name);
+    }
+
+    public void close() throws Exception {
+      log.add("Close " + name);
+    }
+  }
+
+  public class ResourceWithExceptionOnClose extends Resource {
+
+
+    public ResourceWithExceptionOnClose(String name) {
+      super(name);
+    }
+
+    public void close() throws Exception {
+      throw new E1("Exception in close " + name);
+    }
+  }
+
+  public void testResource() throws Exception{
+    log.clear();
+    try (Resource c = new Resource("A")) {
+
+      c.doSomething();
+    }
+
+    assertContentsInOrder(log,
+        "Open A",
+        "doSomething A",
+        "Close A");
+  }
+
+  public void test3Resources() throws Exception{
+    log.clear();
+    try (Resource rA = new Resource("A");
+        Resource rB = new Resource("B");
+        Resource rC = new Resource("C")) {
+
+      rA.doSomething();
+      rB.doSomething();
+      rC.doSomething();
+    }
+
+    assertContentsInOrder(log,
+        "Open A",
+        "Open B",
+        "Open C",
+        "doSomething A",
+        "doSomething B",
+        "doSomething C",
+        "Close C",
+        "Close B",
+        "Close A"
+    );
+  }
+
+  public void testResourcesWithExceptions() throws Exception{
+    log.clear();
+    try (Resource rA = new Resource("A");
+        Resource rB = new ResourceWithExceptionOnClose("B");
+        Resource rC = new Resource("C")) {
+
+      rA.doSomething();
+      rB.doSomething();
+      rC.doSomething();
+    } catch (Exception e) {
+      log.add(e.getMessage());
+    } finally {
+      log.add("finally");
+    }
+
+    assertContentsInOrder(log,
+        "Open A",
+        "Open B",
+        "Open C",
+        "doSomething A",
+        "doSomething B",
+        "doSomething C",
+        "Close C",
+        "Close A",
+        "Exception in close B",
+        "finally"
+        );
+  }
+
+  public void testResourcesWithSuppressedExceptions() throws Exception{
+    log.clear();
+    try (Resource rA = new ResourceWithExceptionOnClose("A");
+        Resource rB = new Resource("B");
+        Resource rC = new ResourceWithExceptionOnClose("C")) {
+
+      rA.doSomething();
+      rB.doSomething();
+      rC.doSomething();
+    } catch (Exception e) {
+      log.add(e.getMessage());
+      for (Throwable t : e.getSuppressed()) {
+        log.add("Suppressed: " + t.getMessage());
+      }
+    }
+
+    assertContentsInOrder(log,
+        "Open A",
+        "Open B",
+        "Open C",
+        "doSomething A",
+        "doSomething B",
+        "doSomething C",
+        "Close B",
+        "Exception in close C",
+        "Suppressed: Exception in close A"
+    );
+
+    log.clear();
+    try (Resource rA = new Resource("A");
+        Resource rB = new ResourceWithExceptionOnClose("B");
+        Resource rC = new Resource("C")) {
+
+      rA.doSomething();
+      rB.throwException("E1 here");
+      rC.doSomething();
+    } catch (Exception e) {
+      log.add(e.getMessage());
+      for (Throwable t : e.getSuppressed()) {
+        log.add("Suppressed: " + t.getMessage());
+      }
+    } finally {
+      log.add("finally");
+    }
+
+    assertContentsInOrder(log,
+        "Open A",
+        "Open B",
+        "Open C",
+        "doSomething A",
+        "Close C",
+        "Close A",
+        "E1 here in B",
+        "Suppressed: Exception in close B",
+        "finally"
+        );
+  }
+
+  public void testAddSuppressedExceptions() {
+    Throwable throwable = new Throwable("primary");
+    assertNotNull(throwable.getSuppressed());
+    assertEquals(0, throwable.getSuppressed().length);
+    Throwable suppressed1 = new Throwable("suppressed1");
+    throwable.addSuppressed(suppressed1);
+    assertEquals(1, throwable.getSuppressed().length);
+    assertEquals(suppressed1, throwable.getSuppressed()[0]);
+    Throwable suppressed2 = new Throwable("suppressed2");
+    throwable.addSuppressed(suppressed2);
+    assertEquals(2, throwable.getSuppressed().length);
+    assertEquals(suppressed1, throwable.getSuppressed()[0]);
+    assertEquals(suppressed2, throwable.getSuppressed()[1]);
+  }
+
+  private void assertContentsInOrder(Iterable<String> contents, String... elements ) {
+    int sz = elements.length;
+    Iterator<String> it = contents.iterator();
+    for (int i = 0; i < sz; i++) {
+      assertTrue(it.hasNext());
+      String expected = it.next();
+      assertEquals(elements[i], expected);
+    }
+    assertFalse(it.hasNext());
+  }
+
+  public static class E1 extends Exception {
+    String name;
+    public E1(String name) {
+      this.name = name;
+    }
+
+    public int methodE1() {
+      return 0;
+    }
+
+    @Override
+    public String getMessage() {
+      return name;
+    }
+  }
+
+  public static class E2 extends E1 {
+    public E2(String name) {
+      super(name);
+    }
+
+    public int methodE2() {
+      return 1;
+    }
+  }
+
+  public static class E3 extends E1 {
+    public E3(String name) {
+      super(name);
+    }
+
+    public int methodE3() {
+      return 2;
+    }
+  }
+
+  public void testMultiExceptions() {
+
+    int choose = 0;
+
+    try {
+      if (choose == 0) {
+        throw new E1("e1");
+      } else if (choose ==1) {
+        throw new E2("e2");
+      }
+
+      fail("Exception was not trown");
+    } catch (E2 | E3 x) {
+      // The compiler will assign x a common supertype/superinterface of E2 and E3.
+      // Here we make sure that this clause is not entered when the supertype is thrown.
+      fail("Caught E1 instead of E2|E3");
+    } catch (E1 x) {
+    }
+  }
 }
diff --git a/user/test/com/google/gwt/dev/jjs/test/Java7Test.java b/user/test/com/google/gwt/dev/jjs/test/Java7Test.java
index 2ef1c47..e463279 100644
--- a/user/test/com/google/gwt/dev/jjs/test/Java7Test.java
+++ b/user/test/com/google/gwt/dev/jjs/test/Java7Test.java
@@ -49,4 +49,22 @@
 
   public void testSwitchOnString() {
   }
+
+  public void testResource() throws Exception {
+  }
+
+  public void test3Resources() throws Exception {
+  }
+
+  public void testResourcesWithExceptions() throws Exception {
+  }
+
+  public void testResourcesWithSuppressedExceptions() throws Exception {
+  }
+
+  public void testMultiExceptions() {
+  }
+
+  public void testAddSuppressedExceptions() {
+  }
 }