mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 02:54:35 +02:00
7020499: Project Coin: improvements to try-with-resources desugaring
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
This commit is contained in:
parent
28ed819ae9
commit
d6521300a0
6 changed files with 533 additions and 19 deletions
|
@ -86,6 +86,7 @@ public class Lower extends TreeTranslator {
|
||||||
private final TypeEnvs typeEnvs;
|
private final TypeEnvs typeEnvs;
|
||||||
private final Name dollarAssertionsDisabled;
|
private final Name dollarAssertionsDisabled;
|
||||||
private final Name classDollar;
|
private final Name classDollar;
|
||||||
|
private final Name dollarCloseResource;
|
||||||
private final Types types;
|
private final Types types;
|
||||||
private final boolean debugLower;
|
private final boolean debugLower;
|
||||||
private final PkgInfo pkginfoOpt;
|
private final PkgInfo pkginfoOpt;
|
||||||
|
@ -109,6 +110,8 @@ public class Lower extends TreeTranslator {
|
||||||
fromString(target.syntheticNameChar() + "assertionsDisabled");
|
fromString(target.syntheticNameChar() + "assertionsDisabled");
|
||||||
classDollar = names.
|
classDollar = names.
|
||||||
fromString("class" + target.syntheticNameChar());
|
fromString("class" + target.syntheticNameChar());
|
||||||
|
dollarCloseResource = names.
|
||||||
|
fromString(target.syntheticNameChar() + "closeResource");
|
||||||
|
|
||||||
types = Types.instance(context);
|
types = Types.instance(context);
|
||||||
Options options = Options.instance(context);
|
Options options = Options.instance(context);
|
||||||
|
@ -1648,9 +1651,11 @@ public class Lower extends TreeTranslator {
|
||||||
ListBuffer<JCStatement> stats = new ListBuffer<>();
|
ListBuffer<JCStatement> stats = new ListBuffer<>();
|
||||||
JCTree resource = resources.head;
|
JCTree resource = resources.head;
|
||||||
JCExpression expr = null;
|
JCExpression expr = null;
|
||||||
|
boolean resourceNonNull;
|
||||||
if (resource instanceof JCVariableDecl) {
|
if (resource instanceof JCVariableDecl) {
|
||||||
JCVariableDecl var = (JCVariableDecl) resource;
|
JCVariableDecl var = (JCVariableDecl) resource;
|
||||||
expr = make.Ident(var.sym).setType(resource.type);
|
expr = make.Ident(var.sym).setType(resource.type);
|
||||||
|
resourceNonNull = var.init != null && TreeInfo.skipParens(var.init).hasTag(NEWCLASS);
|
||||||
stats.add(var);
|
stats.add(var);
|
||||||
} else {
|
} else {
|
||||||
Assert.check(resource instanceof JCExpression);
|
Assert.check(resource instanceof JCExpression);
|
||||||
|
@ -1665,6 +1670,7 @@ public class Lower extends TreeTranslator {
|
||||||
JCVariableDecl syntheticTwrVarDecl =
|
JCVariableDecl syntheticTwrVarDecl =
|
||||||
make.VarDef(syntheticTwrVar, (JCExpression)resource);
|
make.VarDef(syntheticTwrVar, (JCExpression)resource);
|
||||||
expr = (JCExpression)make.Ident(syntheticTwrVar);
|
expr = (JCExpression)make.Ident(syntheticTwrVar);
|
||||||
|
resourceNonNull = TreeInfo.skipParens(resource).hasTag(NEWCLASS);
|
||||||
stats.add(syntheticTwrVarDecl);
|
stats.add(syntheticTwrVarDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1694,7 +1700,7 @@ public class Lower extends TreeTranslator {
|
||||||
|
|
||||||
int oldPos = make.pos;
|
int oldPos = make.pos;
|
||||||
make.at(TreeInfo.endPos(block));
|
make.at(TreeInfo.endPos(block));
|
||||||
JCBlock finallyClause = makeTwrFinallyClause(primaryException, expr);
|
JCBlock finallyClause = makeTwrFinallyClause(primaryException, expr, resourceNonNull);
|
||||||
make.at(oldPos);
|
make.at(oldPos);
|
||||||
JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block,
|
JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block,
|
||||||
finallyCanCompleteNormally, depth + 1),
|
finallyCanCompleteNormally, depth + 1),
|
||||||
|
@ -1706,7 +1712,96 @@ public class Lower extends TreeTranslator {
|
||||||
return newBlock;
|
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);
|
// primaryException.addSuppressed(catchException);
|
||||||
VarSymbol catchException =
|
VarSymbol catchException =
|
||||||
new VarSymbol(SYNTHETIC, make.paramName(2),
|
new VarSymbol(SYNTHETIC, make.paramName(2),
|
||||||
|
@ -1731,11 +1826,7 @@ public class Lower extends TreeTranslator {
|
||||||
tryTree,
|
tryTree,
|
||||||
makeResourceCloseInvocation(resource));
|
makeResourceCloseInvocation(resource));
|
||||||
|
|
||||||
// if (#resource != null) { if (primaryException ... }
|
return closeIfStatement;
|
||||||
return make.Block(0L,
|
|
||||||
List.<JCStatement>of(make.If(makeNonNullCheck(resource),
|
|
||||||
closeIfStatement,
|
|
||||||
null)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private JCStatement makeResourceCloseInvocation(JCExpression resource) {
|
private JCStatement makeResourceCloseInvocation(JCExpression resource) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
140
langtools/test/tools/javac/TryWithResources/TwrClose.java
Normal file
140
langtools/test/tools/javac/TryWithResources/TwrClose.java
Normal file
|
@ -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";
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -36,9 +36,9 @@ import static java.lang.System.lineSeparator;
|
||||||
public class ResourceVariable {
|
public class ResourceVariable {
|
||||||
|
|
||||||
@TADescription(annotation = "TA", type = RESOURCE_VARIABLE,
|
@TADescription(annotation = "TA", type = RESOURCE_VARIABLE,
|
||||||
lvarOffset = {10}, lvarLength = {118}, lvarIndex = {1})
|
lvarOffset = {10}, lvarLength = {106}, lvarIndex = {1})
|
||||||
@TADescription(annotation = "TB", type = RESOURCE_VARIABLE,
|
@TADescription(annotation = "TB", type = RESOURCE_VARIABLE,
|
||||||
lvarOffset = {22}, lvarLength = {35}, lvarIndex = {3})
|
lvarOffset = {22}, lvarLength = {31}, lvarIndex = {3})
|
||||||
public String testResourceVariable() {
|
public String testResourceVariable() {
|
||||||
return
|
return
|
||||||
"public void f() throws IOException {" + lineSeparator() +
|
"public void f() throws IOException {" + lineSeparator() +
|
||||||
|
@ -49,7 +49,7 @@ public class ResourceVariable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@TADescription(annotation = "RTAs", type = RESOURCE_VARIABLE,
|
@TADescription(annotation = "RTAs", type = RESOURCE_VARIABLE,
|
||||||
lvarOffset = {10}, lvarLength = {30}, lvarIndex = {1})
|
lvarOffset = {10}, lvarLength = {26}, lvarIndex = {1})
|
||||||
public String testRepeatedAnnotation1() {
|
public String testRepeatedAnnotation1() {
|
||||||
return
|
return
|
||||||
"public void f() throws IOException {" + lineSeparator() +
|
"public void f() throws IOException {" + lineSeparator() +
|
||||||
|
@ -58,7 +58,7 @@ public class ResourceVariable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@TADescription(annotation = "RTAs", type = RESOURCE_VARIABLE,
|
@TADescription(annotation = "RTAs", type = RESOURCE_VARIABLE,
|
||||||
lvarOffset = {10}, lvarLength = {30}, lvarIndex = {1})
|
lvarOffset = {10}, lvarLength = {26}, lvarIndex = {1})
|
||||||
public String testRepeatedAnnotation2() {
|
public String testRepeatedAnnotation2() {
|
||||||
return
|
return
|
||||||
"public void f() throws IOException {" + lineSeparator() +
|
"public void f() throws IOException {" + lineSeparator() +
|
||||||
|
@ -67,9 +67,9 @@ public class ResourceVariable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@TADescription(annotation = "TA", type = RESOURCE_VARIABLE,
|
@TADescription(annotation = "TA", type = RESOURCE_VARIABLE,
|
||||||
lvarOffset = {10}, lvarLength = {118}, lvarIndex = {1})
|
lvarOffset = {10}, lvarLength = {106}, lvarIndex = {1})
|
||||||
@TADescription(annotation = "TB", type = RESOURCE_VARIABLE,
|
@TADescription(annotation = "TB", type = RESOURCE_VARIABLE,
|
||||||
lvarOffset = {22}, lvarLength = {35}, lvarIndex = {3})
|
lvarOffset = {22}, lvarLength = {31}, lvarIndex = {3})
|
||||||
public String testSeveralVariablesInTryWithResources() {
|
public String testSeveralVariablesInTryWithResources() {
|
||||||
return
|
return
|
||||||
"public void f() throws IOException {" + lineSeparator() +
|
"public void f() throws IOException {" + lineSeparator() +
|
||||||
|
|
|
@ -52,9 +52,9 @@ public class TestCaseTry {
|
||||||
o = "";
|
o = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@AliveRange(varName="o", bytecodeStart=22, bytecodeLength=38)
|
@AliveRange(varName="o", bytecodeStart=22, bytecodeLength=13)
|
||||||
@AliveRange(varName="o", bytecodeStart=103, bytecodeLength=3)
|
@AliveRange(varName="o", bytecodeStart=53, bytecodeLength=3)
|
||||||
@AliveRange(varName="o", bytecodeStart=110, bytecodeLength=1)
|
@AliveRange(varName="o", bytecodeStart=60, bytecodeLength=1)
|
||||||
void m3() {
|
void m3() {
|
||||||
Object o;
|
Object o;
|
||||||
try (BufferedReader br =
|
try (BufferedReader br =
|
||||||
|
@ -65,8 +65,8 @@ public class TestCaseTry {
|
||||||
o = "";
|
o = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@AliveRange(varName="o", bytecodeStart=12, bytecodeLength=96)
|
@AliveRange(varName="o", bytecodeStart=12, bytecodeLength=46)
|
||||||
@AliveRange(varName="o", bytecodeStart=112, bytecodeLength=1)
|
@AliveRange(varName="o", bytecodeStart=62, bytecodeLength=1)
|
||||||
void m4() {
|
void m4() {
|
||||||
String o;
|
String o;
|
||||||
try (BufferedReader br =
|
try (BufferedReader br =
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue