8194978: Javac produces dead code for try-with-resource
authorjlahoda
Thu, 22 Mar 2018 15:28:33 +0100
changeset 49280 c2a3a2aa2475
parent 49279 1d46f84cb930
child 49281 6712bdd93e4e
8194978: Javac produces dead code for try-with-resource Summary: For try-with-resources, using simplified distinct close code for try body and catch clause, to avoid creating multiple copies of the full finally code. Reviewed-by: mcimadamore
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java
test/langtools/tools/javac/TryWithResources/TwrClose.java
test/langtools/tools/javac/TryWithResources/TwrShareCloseCode.java
test/langtools/tools/javac/TryWithResources/TwrSimpleClose.java
test/langtools/tools/javac/annotations/typeAnnotations/referenceinfos/ResourceVariable.java
test/langtools/tools/javac/flow/tests/TestCaseTry.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java	Thu Mar 22 09:41:29 2018 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java	Thu Mar 22 15:28:33 2018 +0100
@@ -313,6 +313,12 @@
      */
     public static final long ANONCONSTR_BASED = 1L<<57;
 
+    /**
+     * Flag that marks finalize block as body-only, should not be copied into catch clauses.
+     * Used to implement try-with-resources.
+     */
+    public static final long BODY_ONLY_FINALIZE = 1L<<17; //blocks only
+
     /** Modifier masks.
      */
     public static final int
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Thu Mar 22 09:41:29 2018 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Thu Mar 22 15:28:33 2018 +0100
@@ -1519,26 +1519,22 @@
      *
      * {
      *   final VariableModifiers_minus_final R #resource = Expression;
-     *   Throwable #primaryException = null;
      *
      *   try ResourceSpecificationtail
      *     Block
-     *   catch (Throwable #t) {
-     *     #primaryException = t;
-     *     throw #t;
-     *   } finally {
-     *     if (#resource != null) {
-     *       if (#primaryException != null) {
-     *         try {
-     *           #resource.close();
-     *         } catch(Throwable #suppressedException) {
-     *           #primaryException.addSuppressed(#suppressedException);
-     *         }
-     *       } else {
+     *   } body-only-finally {
+     *     if (#resource != null) //nullcheck skipped if Expression is provably non-null
      *         #resource.close();
-     *       }
-     *     }
+     *   } catch (Throwable #primaryException) {
+     *       if (#resource != null) //nullcheck skipped if Expression is provably non-null
+     *           try {
+     *               #resource.close();
+     *           } catch (Throwable #suppressedException) {
+     *              #primaryException.addSuppressed(#suppressedException);
+     *           }
+     *       throw #primaryException;
      *   }
+     * }
      *
      * @param tree  The try statement to inspect.
      * @return A a desugared try-with-resources tree, or the original
@@ -1547,8 +1543,7 @@
     JCTree makeTwrTry(JCTry tree) {
         make_at(tree.pos());
         twrVars = twrVars.dup();
-        JCBlock twrBlock = makeTwrBlock(tree.resources, tree.body,
-                tree.finallyCanCompleteNormally, 0);
+        JCBlock twrBlock = makeTwrBlock(tree.resources, tree.body, 0);
         if (tree.catchers.isEmpty() && tree.finalizer == null)
             result = translate(twrBlock);
         else
@@ -1557,19 +1552,18 @@
         return result;
     }
 
-    private JCBlock makeTwrBlock(List<JCTree> resources, JCBlock block,
-            boolean finallyCanCompleteNormally, int depth) {
+    private JCBlock makeTwrBlock(List<JCTree> resources, JCBlock block, int depth) {
         if (resources.isEmpty())
             return block;
 
         // Add resource declaration or expression to block statements
         ListBuffer<JCStatement> stats = new ListBuffer<>();
         JCTree resource = resources.head;
-        JCExpression expr = null;
+        JCExpression resourceUse;
         boolean resourceNonNull;
         if (resource instanceof JCVariableDecl) {
             JCVariableDecl var = (JCVariableDecl) resource;
-            expr = make.Ident(var.sym).setType(resource.type);
+            resourceUse = make.Ident(var.sym).setType(resource.type);
             resourceNonNull = var.init != null && TreeInfo.skipParens(var.init).hasTag(NEWCLASS);
             stats.add(var);
         } else {
@@ -1584,164 +1578,82 @@
             twrVars.enter(syntheticTwrVar);
             JCVariableDecl syntheticTwrVarDecl =
                 make.VarDef(syntheticTwrVar, (JCExpression)resource);
-            expr = (JCExpression)make.Ident(syntheticTwrVar);
-            resourceNonNull = TreeInfo.skipParens(resource).hasTag(NEWCLASS);
+            resourceUse = (JCExpression)make.Ident(syntheticTwrVar);
+            resourceNonNull = false;
             stats.add(syntheticTwrVarDecl);
         }
 
-        // Add primaryException declaration
+        //create (semi-) finally block that will be copied into the main try body:
+        int oldPos = make.pos;
+        make.at(TreeInfo.endPos(block));
+
+        // if (#resource != null) { #resource.close(); }
+        JCStatement bodyCloseStatement = makeResourceCloseInvocation(resourceUse);
+
+        if (!resourceNonNull) {
+            bodyCloseStatement = make.If(makeNonNullCheck(resourceUse),
+                                         bodyCloseStatement,
+                                         null);
+        }
+
+        JCBlock finallyClause = make.Block(BODY_ONLY_FINALIZE, List.of(bodyCloseStatement));
+        make.at(oldPos);
+
+        // Create catch clause that saves exception, closes the resource and then rethrows the exception:
         VarSymbol primaryException =
-            new VarSymbol(SYNTHETIC,
-                          makeSyntheticName(names.fromString("primaryException" +
-                          depth), twrVars),
-                          syms.throwableType,
-                          currentMethodSym);
-        twrVars.enter(primaryException);
-        JCVariableDecl primaryExceptionTreeDecl = make.VarDef(primaryException, makeNull());
-        stats.add(primaryExceptionTreeDecl);
-
-        // Create catch clause that saves exception and then rethrows it
-        VarSymbol param =
             new VarSymbol(FINAL|SYNTHETIC,
                           names.fromString("t" +
                                            target.syntheticNameChar()),
                           syms.throwableType,
                           currentMethodSym);
-        JCVariableDecl paramTree = make.VarDef(param, null);
-        JCStatement assign = make.Assignment(primaryException, make.Ident(param));
-        JCStatement rethrowStat = make.Throw(make.Ident(param));
-        JCBlock catchBlock = make.Block(0L, List.of(assign, rethrowStat));
-        JCCatch catchClause = make.Catch(paramTree, catchBlock);
-
-        int oldPos = make.pos;
-        make.at(TreeInfo.endPos(block));
-        JCBlock finallyClause = makeTwrFinallyClause(primaryException, expr, resourceNonNull);
-        make.at(oldPos);
-        JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block,
-                                    finallyCanCompleteNormally, depth + 1),
-                                  List.of(catchClause),
-                                  finallyClause);
-        outerTry.finallyCanCompleteNormally = finallyCanCompleteNormally;
-        stats.add(outerTry);
-        JCBlock newBlock = make.Block(0L, stats.toList());
-        return newBlock;
-    }
-
-    /**If the estimated number of copies the close resource code in a single class is above this
-     * threshold, generate and use a method for the close resource code, leading to smaller code.
-     * As generating a method has overhead on its own, generating the method for cases below the
-     * threshold could lead to an increase in code size.
-     */
-    public static final int USE_CLOSE_RESOURCE_METHOD_THRESHOLD = 4;
-
-    private JCBlock makeTwrFinallyClause(Symbol primaryException, JCExpression resource,
-            boolean resourceNonNull) {
-        MethodSymbol closeResource = (MethodSymbol)lookupSynthetic(dollarCloseResource,
-                                                                   currentClass.members());
-
-        if (closeResource == null && shouldUseCloseResourceMethod()) {
-            closeResource = new MethodSymbol(
-                PRIVATE | STATIC | SYNTHETIC,
-                dollarCloseResource,
-                new MethodType(
-                    List.of(syms.throwableType, syms.autoCloseableType),
-                    syms.voidType,
-                    List.nil(),
-                    syms.methodClass),
-                currentClass);
-            enterSynthetic(resource.pos(), closeResource, currentClass.members());
-
-            JCMethodDecl md = make.MethodDef(closeResource, null);
-            List<JCVariableDecl> params = md.getParameters();
-            md.body = make.Block(0, List.of(makeTwrCloseStatement(params.get(0).sym,
-                                                                               make.Ident(params.get(1)))));
-
-            JCClassDecl currentClassDecl = classDef(currentClass);
-            currentClassDecl.defs = currentClassDecl.defs.prepend(md);
-        }
-
-        JCStatement closeStatement;
-
-        if (closeResource != null) {
-            //$closeResource(#primaryException, #resource)
-            closeStatement = make.Exec(make.Apply(List.nil(),
-                                                  make.Ident(closeResource),
-                                                  List.of(make.Ident(primaryException),
-                                                          resource)
-                                                 ).setType(syms.voidType));
-        } else {
-            closeStatement = makeTwrCloseStatement(primaryException, resource);
-        }
-
-        JCStatement finallyStatement;
-
-        if (resourceNonNull) {
-            finallyStatement = closeStatement;
-        } else {
-            // if (#resource != null) { $closeResource(...); }
-            finallyStatement = make.If(makeNonNullCheck(resource),
-                                       closeStatement,
-                                       null);
-        }
-
-        return make.Block(0L,
-                          List.of(finallyStatement));
-    }
-        //where:
-        private boolean shouldUseCloseResourceMethod() {
-            class TryFinder extends TreeScanner {
-                int closeCount;
-                @Override
-                public void visitTry(JCTry tree) {
-                    boolean empty = tree.body.stats.isEmpty();
-
-                    for (JCTree r : tree.resources) {
-                        closeCount += empty ? 1 : 2;
-                        empty = false; //with multiple resources, only the innermost try can be empty.
-                    }
-                    super.visitTry(tree);
-                }
-                @Override
-                public void scan(JCTree tree) {
-                    if (useCloseResourceMethod())
-                        return;
-                    super.scan(tree);
-                }
-                boolean useCloseResourceMethod() {
-                    return closeCount >= USE_CLOSE_RESOURCE_METHOD_THRESHOLD;
-                }
-            }
-            TryFinder tryFinder = new TryFinder();
-            tryFinder.scan(classDef(currentClass));
-            return tryFinder.useCloseResourceMethod();
-        }
-
-    private JCStatement makeTwrCloseStatement(Symbol primaryException, JCExpression resource) {
-        // primaryException.addSuppressed(catchException);
-        VarSymbol catchException =
+        JCVariableDecl primaryExceptionDecl = make.VarDef(primaryException, null);
+
+        // close resource:
+        // try {
+        //     #resource.close();
+        // } catch (Throwable #suppressedException) {
+        //     #primaryException.addSuppressed(#suppressedException);
+        // }
+        VarSymbol suppressedException =
             new VarSymbol(SYNTHETIC, make.paramName(2),
                           syms.throwableType,
                           currentMethodSym);
-        JCStatement addSuppressionStatement =
+        JCStatement addSuppressedStatement =
             make.Exec(makeCall(make.Ident(primaryException),
                                names.addSuppressed,
-                               List.of(make.Ident(catchException))));
-
-        // try { resource.close(); } catch (e) { primaryException.addSuppressed(e); }
-        JCBlock tryBlock =
-            make.Block(0L, List.of(makeResourceCloseInvocation(resource)));
-        JCVariableDecl catchExceptionDecl = make.VarDef(catchException, null);
-        JCBlock catchBlock = make.Block(0L, List.of(addSuppressionStatement));
-        List<JCCatch> catchClauses = List.of(make.Catch(catchExceptionDecl, catchBlock));
-        JCTry tryTree = make.Try(tryBlock, catchClauses, null);
-        tryTree.finallyCanCompleteNormally = true;
-
-        // if (primaryException != null) {try...} else resourceClose;
-        JCIf closeIfStatement = make.If(makeNonNullCheck(make.Ident(primaryException)),
-                                        tryTree,
-                                        makeResourceCloseInvocation(resource));
-
-        return closeIfStatement;
+                               List.of(make.Ident(suppressedException))));
+        JCBlock closeResourceTryBlock =
+            make.Block(0L, List.of(makeResourceCloseInvocation(resourceUse)));
+        JCVariableDecl catchSuppressedDecl = make.VarDef(suppressedException, null);
+        JCBlock catchSuppressedBlock = make.Block(0L, List.of(addSuppressedStatement));
+        List<JCCatch> catchSuppressedClauses =
+                List.of(make.Catch(catchSuppressedDecl, catchSuppressedBlock));
+        JCTry closeResourceTry = make.Try(closeResourceTryBlock, catchSuppressedClauses, null);
+        closeResourceTry.finallyCanCompleteNormally = true;
+
+        JCStatement exceptionalCloseStatement = closeResourceTry;
+
+        if (!resourceNonNull) {
+            // if (#resource != null) {  }
+            exceptionalCloseStatement = make.If(makeNonNullCheck(resourceUse),
+                                                exceptionalCloseStatement,
+                                                null);
+        }
+
+        JCStatement exceptionalRethrow = make.Throw(make.Ident(primaryException));
+        JCBlock exceptionalCloseBlock = make.Block(0L, List.of(exceptionalCloseStatement, exceptionalRethrow));
+        JCCatch exceptionalCatchClause = make.Catch(primaryExceptionDecl, exceptionalCloseBlock);
+
+        //create the main try statement with the close:
+        JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block, depth + 1),
+                                  List.of(exceptionalCatchClause),
+                                  finallyClause);
+
+        outerTry.finallyCanCompleteNormally = true;
+        stats.add(outerTry);
+
+        JCBlock newBlock = make.Block(0L, stats.toList());
+        return newBlock;
     }
 
     private JCStatement makeResourceCloseInvocation(JCExpression resource) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Thu Mar 22 09:41:29 2018 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Thu Mar 22 15:28:33 2018 +0100
@@ -1342,6 +1342,16 @@
             boolean hasFinalizer() {
                 return tree.finalizer != null;
             }
+
+            @Override
+            void afterBody() {
+                if (tree.finalizer != null && (tree.finalizer.flags & BODY_ONLY_FINALIZE) != 0) {
+                    //for body-only finally, remove the GenFinalizer after try body
+                    //so that the finally is not generated to catch bodies:
+                    tryEnv.info.finalize = null;
+                }
+            }
+
         };
         tryEnv.info.gaps = new ListBuffer<>();
         genTry(tree.body, tree.catchers, tryEnv);
@@ -1358,15 +1368,16 @@
             Code.State stateTry = code.state.dup();
             genStat(body, env, CRT_BLOCK);
             int endpc = code.curCP();
-            boolean hasFinalizer =
-                env.info.finalize != null &&
-                env.info.finalize.hasFinalizer();
             List<Integer> gaps = env.info.gaps.toList();
             code.statBegin(TreeInfo.endPos(body));
             genFinalizer(env);
             code.statBegin(TreeInfo.endPos(env.tree));
             Chain exitChain = code.branch(goto_);
             endFinalizerGap(env);
+            env.info.finalize.afterBody();
+            boolean hasFinalizer =
+                env.info.finalize != null &&
+                env.info.finalize.hasFinalizer();
             if (startpc != endpc) for (List<JCCatch> l = catchers; l.nonEmpty(); l = l.tail) {
                 // start off with exception on stack
                 code.entryPoint(stateTry, l.head.param.sym.type);
@@ -2219,6 +2230,9 @@
 
         /** Does this finalizer have some nontrivial cleanup to perform? */
         boolean hasFinalizer() { return true; }
+
+        /** Should be invoked after the try's body has been visited. */
+        void afterBody() {}
     }
 
     /** code generation contexts,
--- a/test/langtools/tools/javac/TryWithResources/TwrClose.java	Thu Mar 22 09:41:29 2018 -0400
+++ b/test/langtools/tools/javac/TryWithResources/TwrClose.java	Thu Mar 22 15:28:33 2018 +0100
@@ -27,7 +27,6 @@
  * @summary Verify that the close resource code works properly in all cases
  * @library /tools/lib
  * @modules jdk.compiler/com.sun.tools.javac.api
- *          jdk.compiler/com.sun.tools.javac.comp
  *          jdk.compiler/com.sun.tools.javac.main
  * @build toolbox.ToolBox TwrClose
  * @run main TwrClose
@@ -37,15 +36,14 @@
 import javax.tools.StandardLocation;
 import javax.tools.ToolProvider;
 
-import com.sun.tools.javac.comp.Lower;
-
 import toolbox.JavacTask;
 import toolbox.ToolBox;
 
 public class TwrClose {
 
+    private static final int MAX_RESOURCES = 5;
     public static void main(String... args) throws Exception {
-        for (int i = 1; i < Lower.USE_CLOSE_RESOURCE_METHOD_THRESHOLD * 2; i++) {
+        for (int i = 1; i < MAX_RESOURCES * 2; i++) {
             new TwrClose().compile(i);
         }
     }
--- a/test/langtools/tools/javac/TryWithResources/TwrShareCloseCode.java	Thu Mar 22 09:41:29 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * @test
- * @bug 7020499
- * @summary Verify that the code that closes the resources is shared by among try-with-resources
- * @library /tools/lib
- * @modules jdk.compiler/com.sun.tools.javac.api
- *          jdk.compiler/com.sun.tools.javac.code
- *          jdk.compiler/com.sun.tools.javac.comp
- *          jdk.compiler/com.sun.tools.javac.tree
- *          jdk.compiler/com.sun.tools.javac.util
- * @build toolbox.ToolBox TwrShareCloseCode
- * @run main TwrShareCloseCode
- */
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import com.sun.source.util.JavacTask;
-import com.sun.tools.javac.api.JavacTool;
-import com.sun.tools.javac.comp.AttrContext;
-import com.sun.tools.javac.comp.Env;
-import com.sun.tools.javac.comp.Lower;
-import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
-import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
-import com.sun.tools.javac.tree.TreeInfo;
-import com.sun.tools.javac.tree.TreeMaker;
-import com.sun.tools.javac.tree.TreeScanner;
-import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.util.Context.Factory;
-import com.sun.tools.javac.util.List;
-
-import toolbox.ToolBox;
-
-public class TwrShareCloseCode {
-    public static void main(String... args) throws IOException {
-        new TwrShareCloseCode().run();
-    }
-
-    void run() throws IOException {
-        run("try (Test t1 = new Test()) { }", true);
-        run("try (Test t1 = new Test()) { }\n" +
-            "try (Test t2 = new Test()) { }", true);
-        run("try (Test t1 = new Test();\n" +
-            "     Test t2 = new Test()) { }", true);
-        run("try (Test t1 = new Test()) { }\n" +
-            "try (Test t2 = new Test()) { }\n" +
-            "try (Test t3 = new Test()) { }", true);
-        run("try (Test t1 = new Test();\n" +
-            "     Test t2 = new Test();\n" +
-            "     Test t3 = new Test()) { }", false);
-        run("try (Test t1 = new Test()) { }\n" +
-            "try (Test t2 = new Test()) { }\n" +
-            "try (Test t3 = new Test()) { }\n" +
-            "try (Test t4 = new Test()) { }", false);
-
-        run("try (Test t1 = new Test()) { i++; }", true);
-        run("try (Test t1 = new Test()) { i++; }\n" +
-            "try (Test t2 = new Test()) { i++; }", false);
-
-        run("try (Test t1 = new Test(); Test t2 = new Test()) { i++; }", false);
-
-        run("try (Test t1 = new Test()) { i++; }\n" +
-            "try (Test t2 = new Test()) { }", true);
-
-        run("try (Test t1 = new Test()) { i++; }\n" +
-            "try (Test t2 = new Test()) { }\n" +
-            "try (Test t3 = new Test()) { }", false);
-
-        run("try (Test t1 = new Test()) { i++; }\n" +
-            "try (Test t2 = new Test()) { i++; }\n" +
-            "try (Test t3 = new Test()) { }", false);
-    }
-    void run(String trySpec, boolean expected) throws IOException {
-        String template = "public class Test implements AutoCloseable {\n" +
-                          "    void t(int i) {\n" +
-                          "        TRY\n" +
-                          "    }\n" +
-                          "    public void close() { }\n" +
-                          "}\n";
-        String code = template.replace("TRY", trySpec);
-        Context ctx = new Context();
-        DumpLower.preRegister(ctx);
-        Iterable<ToolBox.JavaSource> files = Arrays.asList(new ToolBox.JavaSource(code));
-        JavacTask task = JavacTool.create().getTask(null, null, null, null, null, files, ctx);
-        task.call();
-        boolean actual = ((DumpLower) DumpLower.instance(ctx)).closeSeen;
-
-        if (expected != actual) {
-            throw new IllegalStateException("expected: " + expected + "; actual: " + actual + "; code:\n" + code);
-        }
-    }
-
-    static class DumpLower extends Lower {
-
-        public static void preRegister(Context ctx) {
-            ctx.put(lowerKey, new Factory<Lower>() {
-                @Override
-                public Lower make(Context c) {
-                    return new DumpLower(c);
-                }
-            });
-        }
-
-        public DumpLower(Context context) {
-            super(context);
-        }
-
-        boolean closeSeen;
-
-        @Override
-        public List<JCTree> translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
-            List<JCTree> result = super.translateTopLevelClass(env, cdef, make);
-
-            new TreeScanner() {
-                @Override
-                public void visitMethodDef(JCMethodDecl tree) {
-                    if (!tree.name.contentEquals("t"))
-                        return;
-
-                    super.visitMethodDef(tree);
-                }
-
-                @Override
-                public void visitApply(JCMethodInvocation tree) {
-                    closeSeen |= TreeInfo.symbol(tree.meth).name.contentEquals("close");
-                    super.visitApply(tree);
-                }
-            }.scan(result);
-
-            return result;
-        }
-
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/TryWithResources/TwrSimpleClose.java	Thu Mar 22 15:28:33 2018 +0100
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8194978
+ * @summary Verify than an appropriate number of close method invocations is generated.
+ * @library /tools/lib
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ * @build toolbox.ToolBox TwrSimpleClose
+ * @run main TwrSimpleClose
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Methodref_info;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_NameAndType_info;
+import com.sun.tools.classfile.Instruction;
+import com.sun.tools.classfile.Method;
+import com.sun.tools.classfile.Opcode;
+
+import toolbox.ToolBox;
+
+public class TwrSimpleClose {
+
+    private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+
+    public static void main(String... args) throws Exception {
+        new TwrSimpleClose().run();
+    }
+
+    void run() throws Exception {
+        run("try (Test t = new Test()) { System.err.println(0); }", 2);
+        run("try (Test t = new Test()) {\n" +
+            "    if (t.hashCode() == 42)\n" +
+            "        return;\n" +
+            "    System.err.println(0);\n" +
+            "}\n", 3);
+    }
+
+    void run(String trySpec, int expectedCloseCount) throws Exception {
+        String template = "public class Test implements AutoCloseable {\n" +
+                          "    void t(int i) {\n" +
+                          "        TRY\n" +
+                          "    }\n" +
+                          "    public void close() { }\n" +
+                          "}\n";
+        String code = template.replace("TRY", trySpec);
+        int closeCount = 0;
+
+        try (StandardJavaFileManager sfm = compiler.getStandardFileManager(null, null, null);
+             JFMImpl fm = new JFMImpl(sfm)) {
+            Iterable<ToolBox.JavaSource> files = Arrays.asList(new ToolBox.JavaSource(code));
+            JavacTask task = (JavacTask) compiler.getTask(null, fm, null, null, null, files);
+            task.call();
+
+            if (fm.classBytes.size() != 1) {
+                throw new AssertionError();
+            }
+
+            byte[] data = fm.classBytes.values().iterator().next();
+            ClassFile cf = ClassFile.read(new ByteArrayInputStream(data));
+
+            for (Method m : cf.methods) {
+                Code_attribute codeAttr = (Code_attribute) m.attributes.map.get(Attribute.Code);
+                for (Instruction i : codeAttr.getInstructions()) {
+                    if (i.getOpcode() == Opcode.INVOKEVIRTUAL) {
+                        CONSTANT_Methodref_info method =
+                                (CONSTANT_Methodref_info) cf.constant_pool.get(i.getShort(1));
+                        CONSTANT_NameAndType_info nameAndType =
+                                cf.constant_pool.getNameAndTypeInfo(method.name_and_type_index);
+                        if ("close".equals(nameAndType.getName())) {
+                            closeCount++;
+                        }
+                    }
+                }
+            }
+            if (expectedCloseCount != closeCount) {
+                throw new IllegalStateException("expected close count: " + expectedCloseCount +
+                                                "; actual: " + closeCount + "; code:\n" + code);
+            }
+        }
+    }
+
+    private static final class JFMImpl extends ForwardingJavaFileManager<JavaFileManager> {
+
+        private final Map<String, byte[]> classBytes = new HashMap<>();
+
+        public JFMImpl(JavaFileManager fileManager) {
+            super(fileManager);
+        }
+
+        @Override
+        public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind,
+                                                   FileObject sibling) throws IOException {
+            try {
+                return new SimpleJavaFileObject(new URI("mem://" + className + ".class"), kind) {
+                    @Override
+                    public OutputStream openOutputStream() throws IOException {
+                        return new ByteArrayOutputStream() {
+                            @Override
+                            public void close() throws IOException {
+                                super.close();
+                                classBytes.put(className, toByteArray());
+                            }
+                        };
+                    }
+                };
+            } catch (URISyntaxException ex) {
+                throw new IOException(ex);
+            }
+        }
+    }
+
+}
--- a/test/langtools/tools/javac/annotations/typeAnnotations/referenceinfos/ResourceVariable.java	Thu Mar 22 09:41:29 2018 -0400
+++ b/test/langtools/tools/javac/annotations/typeAnnotations/referenceinfos/ResourceVariable.java	Thu Mar 22 15:28:33 2018 +0100
@@ -36,9 +36,9 @@
 public class ResourceVariable {
 
     @TADescription(annotation = "TA", type = RESOURCE_VARIABLE,
-            lvarOffset = {10}, lvarLength = {106}, lvarIndex = {1})
+            lvarOffset = {10}, lvarLength = {37}, lvarIndex = {1})
     @TADescription(annotation = "TB", type = RESOURCE_VARIABLE,
-            lvarOffset = {22}, lvarLength = {31}, lvarIndex = {3})
+            lvarOffset = {20}, lvarLength = {4}, lvarIndex = {2})
     public String testResourceVariable() {
         return
                 "public void f() throws IOException {" + lineSeparator() +
@@ -49,7 +49,7 @@
     }
 
     @TADescription(annotation = "RTAs", type = RESOURCE_VARIABLE,
-            lvarOffset = {10}, lvarLength = {26}, lvarIndex = {1})
+            lvarOffset = {10}, lvarLength = {4}, lvarIndex = {1})
     public String testRepeatedAnnotation1() {
         return
                 "public void f() throws IOException {" + lineSeparator() +
@@ -58,7 +58,7 @@
     }
 
     @TADescription(annotation = "RTAs", type = RESOURCE_VARIABLE,
-            lvarOffset = {10}, lvarLength = {26}, lvarIndex = {1})
+            lvarOffset = {10}, lvarLength = {4}, lvarIndex = {1})
     public String testRepeatedAnnotation2() {
         return
                 "public void f() throws IOException {" + lineSeparator() +
@@ -67,9 +67,9 @@
     }
 
     @TADescription(annotation = "TA", type = RESOURCE_VARIABLE,
-            lvarOffset = {10}, lvarLength = {106}, lvarIndex = {1})
+            lvarOffset = {10}, lvarLength = {37}, lvarIndex = {1})
     @TADescription(annotation = "TB", type = RESOURCE_VARIABLE,
-            lvarOffset = {22}, lvarLength = {31}, lvarIndex = {3})
+            lvarOffset = {20}, lvarLength = {4}, lvarIndex = {2})
     public String testSeveralVariablesInTryWithResources() {
         return
                 "public void f() throws IOException {" + lineSeparator() +
--- a/test/langtools/tools/javac/flow/tests/TestCaseTry.java	Thu Mar 22 09:41:29 2018 -0400
+++ b/test/langtools/tools/javac/flow/tests/TestCaseTry.java	Thu Mar 22 15:28:33 2018 +0100
@@ -52,9 +52,9 @@
         o = "";
     }
 
-    @AliveRange(varName="o", bytecodeStart=22, bytecodeLength=13)
-    @AliveRange(varName="o", bytecodeStart=53, bytecodeLength=3)
-    @AliveRange(varName="o", bytecodeStart=60, bytecodeLength=1)
+    @AliveRange(varName="o", bytecodeStart=20, bytecodeLength=12)
+    @AliveRange(varName="o", bytecodeStart=50, bytecodeLength=3)
+    @AliveRange(varName="o", bytecodeStart=57, bytecodeLength=1)
     void m3() {
         Object o;
         try (BufferedReader br =
@@ -65,8 +65,8 @@
         o = "";
     }
 
-    @AliveRange(varName="o", bytecodeStart=12, bytecodeLength=46)
-    @AliveRange(varName="o", bytecodeStart=62, bytecodeLength=1)
+    @AliveRange(varName="o", bytecodeStart=12, bytecodeLength=43)
+    @AliveRange(varName="o", bytecodeStart=59, bytecodeLength=1)
     void m4() {
         String o;
         try (BufferedReader br =