changeset 3351:d30f35629f0e

7020499: Project Coin: improvements to try-with-resources desugaring Summary: Avoid unnecessary check for resource nullness if the resource is known to be non-null; put resource closing code in a method that is shared by multiple try-with-resources. Reviewed-by: darcy, mcimadamore, vromero
author jlahoda
date Tue, 19 Apr 2016 14:16:39 +0200
parents c0b062e1effd
children d59948777810
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java test/tools/javac/TryWithResources/TwrAvoidNullCheck.java test/tools/javac/TryWithResources/TwrClose.java test/tools/javac/TryWithResources/TwrShareCloseCode.java test/tools/javac/annotations/typeAnnotations/referenceinfos/ResourceVariable.java test/tools/javac/flow/tests/TestCaseTry.java
diffstat 6 files changed, 533 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Mon Apr 18 19:14:50 2016 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Apr 19 14:16:39 2016 +0200
@@ -86,6 +86,7 @@
     private final TypeEnvs typeEnvs;
     private final Name dollarAssertionsDisabled;
     private final Name classDollar;
+    private final Name dollarCloseResource;
     private final Types types;
     private final boolean debugLower;
     private final PkgInfo pkginfoOpt;
@@ -109,6 +110,8 @@
             fromString(target.syntheticNameChar() + "assertionsDisabled");
         classDollar = names.
             fromString("class" + target.syntheticNameChar());
+        dollarCloseResource = names.
+            fromString(target.syntheticNameChar() + "closeResource");
 
         types = Types.instance(context);
         Options options = Options.instance(context);
@@ -1648,9 +1651,11 @@
         ListBuffer<JCStatement> stats = new ListBuffer<>();
         JCTree resource = resources.head;
         JCExpression expr = null;
+        boolean resourceNonNull;
         if (resource instanceof JCVariableDecl) {
             JCVariableDecl var = (JCVariableDecl) resource;
             expr = make.Ident(var.sym).setType(resource.type);
+            resourceNonNull = var.init != null && TreeInfo.skipParens(var.init).hasTag(NEWCLASS);
             stats.add(var);
         } else {
             Assert.check(resource instanceof JCExpression);
@@ -1665,6 +1670,7 @@
             JCVariableDecl syntheticTwrVarDecl =
                 make.VarDef(syntheticTwrVar, (JCExpression)resource);
             expr = (JCExpression)make.Ident(syntheticTwrVar);
+            resourceNonNull = TreeInfo.skipParens(resource).hasTag(NEWCLASS);
             stats.add(syntheticTwrVarDecl);
         }
 
@@ -1694,7 +1700,7 @@
 
         int oldPos = make.pos;
         make.at(TreeInfo.endPos(block));
-        JCBlock finallyClause = makeTwrFinallyClause(primaryException, expr);
+        JCBlock finallyClause = makeTwrFinallyClause(primaryException, expr, resourceNonNull);
         make.at(oldPos);
         JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block,
                                     finallyCanCompleteNormally, depth + 1),
@@ -1706,7 +1712,96 @@
         return newBlock;
     }
 
-    private JCBlock makeTwrFinallyClause(Symbol primaryException, JCExpression resource) {
+    /**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.<Type>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.<JCStatement>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.<JCExpression>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.<JCStatement>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 =
             new VarSymbol(SYNTHETIC, make.paramName(2),
@@ -1731,11 +1826,7 @@
                                         tryTree,
                                         makeResourceCloseInvocation(resource));
 
-        // if (#resource != null) { if (primaryException ...  }
-        return make.Block(0L,
-                          List.<JCStatement>of(make.If(makeNonNullCheck(resource),
-                                                       closeIfStatement,
-                                                       null)));
+        return closeIfStatement;
     }
 
     private JCStatement makeResourceCloseInvocation(JCExpression resource) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/TryWithResources/TwrAvoidNullCheck.java	Tue Apr 19 14:16:39 2016 +0200
@@ -0,0 +1,125 @@
+/*
+ * 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 try-with-resources desugaring is not generating unnecessary null checks
+ * @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.main
+ *          jdk.compiler/com.sun.tools.javac.tree
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @build toolbox.ToolBox toolbox.JavacTask TwrAvoidNullCheck
+ * @run main TwrAvoidNullCheck
+ */
+
+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.JCBinary;
+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 TwrAvoidNullCheck {
+    public static void main(String... args) throws IOException {
+        new TwrAvoidNullCheck().run();
+    }
+    void run() throws IOException {
+        run("new Test()", false);
+        run("null", true);
+        run("System.getProperty(\"test\") != null ? new Test() : null", true);
+    }
+    void run(String resourceSpecification, boolean expected) throws IOException {
+        String template = "public class Test implements AutoCloseable {\n" +
+                          "    void t() {\n" +
+                          "        try (Test resource = RESOURCE) { }\n" +
+                          "    }\n" +
+                          "    public void close() { }\n" +
+                          "}\n";
+        String code = template.replace("RESOURCE", resourceSpecification);
+        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 hasNullCheck = ((DumpLower) DumpLower.instance(ctx)).hasNullCheck;
+
+        if (hasNullCheck != expected) {
+            throw new IllegalStateException("expected: " + expected +
+                                            "; actual: " + hasNullCheck +
+                                            "; code: " + 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 hasNullCheck;
+
+        @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 visitBinary(JCBinary tree) {
+                    hasNullCheck |= tree.operator.getSimpleName().contentEquals("!=") &&
+                                    "resource".equals(String.valueOf(TreeInfo.name(tree.lhs))) &&
+                                    TreeInfo.isNull(tree.rhs);
+                    super.visitBinary(tree);
+                }
+            }.scan(result);
+
+            return result;
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/TryWithResources/TwrClose.java	Tue Apr 19 14:16:39 2016 +0200
@@ -0,0 +1,140 @@
+/*
+ * 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 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
+ */
+
+import javax.tools.JavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+import com.sun.tools.javac.comp.Lower;
+
+import toolbox.JavacTask;
+import toolbox.ToolBox;
+
+public class TwrClose {
+
+    public static void main(String... args) throws Exception {
+        for (int i = 1; i < Lower.USE_CLOSE_RESOURCE_METHOD_THRESHOLD * 2; i++) {
+            new TwrClose().compile(i);
+        }
+    }
+
+    ToolBox tb = new ToolBox();
+    JavaFileManager fm = ToolProvider.getSystemJavaCompiler()
+                                     .getStandardFileManager(null, null, null);
+
+    void compile(int trysCount) throws Exception {
+        StringBuilder testInvocations = new StringBuilder();
+        StringBuilder testMethods = new StringBuilder();
+
+        for (int i = 0; i < trysCount; i++) {
+            testInvocations.append(TEST_INVOCATIONS_TEMPLATE.replace("#N", Integer.toString(i)));
+            testMethods.append(TEST_METHOD_TEMPLATE.replace("#N", Integer.toString(i)));
+        }
+
+        String sourceCode = FILE_TEMPLATE.replace("#TEST_INVOCATIONS", testInvocations.toString())
+                                         .replace("#TEST_METHODS", testMethods.toString());
+
+        System.err.println("analyzing:");
+        System.err.println(sourceCode);
+
+        try (ToolBox.MemoryFileManager mfm = new ToolBox.MemoryFileManager()) {
+            new JavacTask(tb).fileManager(mfm)
+                             .sources(sourceCode)
+                             .run()
+                             .writeAll();
+            ClassLoader cl = new ClassLoader(TwrClose.class.getClassLoader()) {
+                @Override
+                protected Class<?> findClass(String name) throws ClassNotFoundException {
+                    byte[] data = mfm.getFileBytes(StandardLocation.CLASS_OUTPUT, name);
+                    if (data != null) {
+                        return defineClass(name, data, 0, data.length);
+                    }
+                    return super.findClass(name);
+                }
+            };
+
+            ((Runnable) cl.loadClass("Test").newInstance()).run();
+        }
+    }
+
+    final String TEST_INVOCATIONS_TEMPLATE =
+        "        test#N(false, false, Arrays.asList(\"close\"));\n" +
+        "        test#N(false, true, Arrays.asList(\"close\", \"close-exception\"));\n" +
+        "        test#N(true, false, Arrays.asList(\"close\", \"inTwr\"));\n" +
+        "        test#N(true, true, Arrays.asList(\"close\", \"inTwr\", \"close-exception\"));\n";
+
+    final String TEST_METHOD_TEMPLATE =
+        "    private void test#N(boolean failInTwr, boolean failOnClose,\n" +
+        "                        List<String> expectedMessages) {\n" +
+        "        List<String> messages = new ArrayList<>();\n" +
+        "        try {\n" +
+        "            try (CloseableImpl c = new CloseableImpl(messages, failOnClose)) {\n" +
+        "                if (failInTwr)\n" +
+        "                    throw new IllegalStateException(\"inTwr\");\n" +
+        "            }\n" +
+        "        } catch (IllegalStateException ex) {\n" +
+        "            messages.add(ex.getMessage());\n" +
+        "            for (Throwable t : ex.getSuppressed()) {\n" +
+        "                messages.add(t.getMessage());\n" +
+        "            }\n" +
+        "        }\n" +
+        "        if (!expectedMessages.equals(messages))\n" +
+        "            throw new AssertionError(\"Expected and actual messages differ; expectedMessages=\" +\n" +
+        "                                     expectedMessages + \"; actual=\" + messages);\n" +
+        "    }\n";
+
+    final String FILE_TEMPLATE =
+        "import java.util.*;\n" +
+        "public class Test implements Runnable {\n" +
+        "    public void run() {\n" +
+        "#TEST_INVOCATIONS" +
+        "    }\n" +
+        "#TEST_METHODS" +
+        "    static class CloseableImpl implements AutoCloseable {\n" +
+        "        private final List<String> messages;\n" +
+        "        private final boolean failOnClose;\n" +
+        "        public CloseableImpl(List<String> messages, boolean failOnClose) {\n" +
+        "            this.messages = messages;\n" +
+        "            this.failOnClose = failOnClose;\n" +
+        "        }\n" +
+        "        @Override\n" +
+        "        public void close() {\n" +
+        "            messages.add(\"close\");\n" +
+        "            if (failOnClose)\n" +
+        "                throw new IllegalStateException(\"close-exception\");\n" +
+        "        }\n" +
+        "    }\n" +
+        "}\n";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/TryWithResources/TwrShareCloseCode.java	Tue Apr 19 14:16:39 2016 +0200
@@ -0,0 +1,158 @@
+/*
+ * 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;
+        }
+
+    }
+}
--- a/test/tools/javac/annotations/typeAnnotations/referenceinfos/ResourceVariable.java	Mon Apr 18 19:14:50 2016 -0700
+++ b/test/tools/javac/annotations/typeAnnotations/referenceinfos/ResourceVariable.java	Tue Apr 19 14:16:39 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -36,9 +36,9 @@
 public class ResourceVariable {
 
     @TADescription(annotation = "TA", type = RESOURCE_VARIABLE,
-            lvarOffset = {10}, lvarLength = {118}, lvarIndex = {1})
+            lvarOffset = {10}, lvarLength = {106}, lvarIndex = {1})
     @TADescription(annotation = "TB", type = RESOURCE_VARIABLE,
-            lvarOffset = {22}, lvarLength = {35}, lvarIndex = {3})
+            lvarOffset = {22}, lvarLength = {31}, lvarIndex = {3})
     public String testResourceVariable() {
         return
                 "public void f() throws IOException {" + lineSeparator() +
@@ -49,7 +49,7 @@
     }
 
     @TADescription(annotation = "RTAs", type = RESOURCE_VARIABLE,
-            lvarOffset = {10}, lvarLength = {30}, lvarIndex = {1})
+            lvarOffset = {10}, lvarLength = {26}, 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 = {30}, lvarIndex = {1})
+            lvarOffset = {10}, lvarLength = {26}, 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 = {118}, lvarIndex = {1})
+            lvarOffset = {10}, lvarLength = {106}, lvarIndex = {1})
     @TADescription(annotation = "TB", type = RESOURCE_VARIABLE,
-            lvarOffset = {22}, lvarLength = {35}, lvarIndex = {3})
+            lvarOffset = {22}, lvarLength = {31}, lvarIndex = {3})
     public String testSeveralVariablesInTryWithResources() {
         return
                 "public void f() throws IOException {" + lineSeparator() +
--- a/test/tools/javac/flow/tests/TestCaseTry.java	Mon Apr 18 19:14:50 2016 -0700
+++ b/test/tools/javac/flow/tests/TestCaseTry.java	Tue Apr 19 14:16:39 2016 +0200
@@ -52,9 +52,9 @@
         o = "";
     }
 
-    @AliveRange(varName="o", bytecodeStart=22, bytecodeLength=38)
-    @AliveRange(varName="o", bytecodeStart=103, bytecodeLength=3)
-    @AliveRange(varName="o", bytecodeStart=110, bytecodeLength=1)
+    @AliveRange(varName="o", bytecodeStart=22, bytecodeLength=13)
+    @AliveRange(varName="o", bytecodeStart=53, bytecodeLength=3)
+    @AliveRange(varName="o", bytecodeStart=60, bytecodeLength=1)
     void m3() {
         Object o;
         try (BufferedReader br =
@@ -65,8 +65,8 @@
         o = "";
     }
 
-    @AliveRange(varName="o", bytecodeStart=12, bytecodeLength=96)
-    @AliveRange(varName="o", bytecodeStart=112, bytecodeLength=1)
+    @AliveRange(varName="o", bytecodeStart=12, bytecodeLength=46)
+    @AliveRange(varName="o", bytecodeStart=62, bytecodeLength=1)
     void m4() {
         String o;
         try (BufferedReader br =