6993963: Project Coin: Use precise exception analysis for effectively final catch parameters

More precise rethrow analysis should be extended to effectively-final exception parameters. Multicatch parameters should be made implicitly final.

Reviewed-by: jjg, darcy
This commit is contained in:
Maurizio Cimadamore 2010-11-04 12:57:48 +00:00
parent 9b7bc21bb0
commit d4d5f60edc
22 changed files with 412 additions and 38 deletions

View File

@ -247,6 +247,11 @@ public class Flags {
*/
public static final long OVERRIDE_BRIDGE = 1L<<41;
/**
* Flag that marks an 'effectively final' local variable
*/
public static final long EFFECTIVELY_FINAL = 1L<<42;
/** Modifier masks.
*/
public static final int

View File

@ -256,6 +256,8 @@ public class Attr extends JCTree.Visitor {
} else {
log.error(pos, "cant.assign.val.to.final.var", v);
}
} else if ((v.flags() & EFFECTIVELY_FINAL) != 0) {
v.flags_field &= ~EFFECTIVELY_FINAL;
}
}
@ -799,6 +801,7 @@ public class Attr extends JCTree.Visitor {
memberEnter.memberEnter(tree, env);
annotate.flush();
}
tree.sym.flags_field |= EFFECTIVELY_FINAL;
}
VarSymbol v = tree.sym;
@ -1061,11 +1064,8 @@ public class Attr extends JCTree.Visitor {
localEnv.dup(c, localEnv.info.dup(localEnv.info.scope.dup()));
Type ctype = attribStat(c.param, catchEnv);
if (TreeInfo.isMultiCatch(c)) {
//check that multi-catch parameter is marked as final
if ((c.param.sym.flags() & FINAL) == 0) {
log.error(c.param.pos(), "multicatch.param.must.be.final", c.param.sym);
}
c.param.sym.flags_field = c.param.sym.flags() | DISJUNCTION;
//multi-catch parameter is implicitly marked as final
c.param.sym.flags_field |= FINAL | DISJUNCTION;
}
if (c.param.sym.kind == Kinds.VAR) {
c.param.sym.setData(ElementKind.EXCEPTION_PARAMETER);

View File

@ -226,7 +226,7 @@ public class Flow extends TreeScanner {
*/
Bits uninits;
HashMap<Symbol, List<Type>> multicatchTypes;
HashMap<Symbol, List<Type>> preciseRethrowTypes;
/** The set of variables that are definitely unassigned everywhere
* in current try block. This variable is maintained lazily; it is
@ -332,7 +332,7 @@ public class Flow extends TreeScanner {
if (!chk.isUnchecked(tree.pos(), exc)) {
if (!chk.isHandled(exc, caught))
pendingExits.append(new PendingExit(tree, exc));
thrown = chk.incl(exc, thrown);
thrown = chk.incl(exc, thrown);
}
}
@ -1077,12 +1077,12 @@ public class Flow extends TreeScanner {
scan(param);
inits.incl(param.sym.adr);
uninits.excl(param.sym.adr);
multicatchTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes));
preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes));
scanStat(l.head.body);
initsEnd.andSet(inits);
uninitsEnd.andSet(uninits);
nextadr = nextadrCatch;
multicatchTypes.remove(param.sym);
preciseRethrowTypes.remove(param.sym);
aliveEnd |= alive;
}
if (tree.finalizer != null) {
@ -1215,10 +1215,10 @@ public class Flow extends TreeScanner {
Symbol sym = TreeInfo.symbol(tree.expr);
if (sym != null &&
sym.kind == VAR &&
(sym.flags() & FINAL) != 0 &&
multicatchTypes.get(sym) != null &&
(sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
preciseRethrowTypes.get(sym) != null &&
allowRethrowAnalysis) {
for (Type t : multicatchTypes.get(sym)) {
for (Type t : preciseRethrowTypes.get(sym)) {
markThrown(tree, t);
}
}
@ -1422,7 +1422,7 @@ public class Flow extends TreeScanner {
firstadr = 0;
nextadr = 0;
pendingExits = new ListBuffer<PendingExit>();
multicatchTypes = new HashMap<Symbol, List<Type>>();
preciseRethrowTypes = new HashMap<Symbol, List<Type>>();
alive = true;
this.thrown = this.caught = null;
this.classDef = null;

View File

@ -187,8 +187,6 @@ compiler.err.twr.resource.may.not.be.assigned=\
automatic resource {0} may not be assigned
compiler.err.multicatch.parameter.may.not.be.assigned=\
multi-catch parameter {0} may not be assigned
compiler.err.multicatch.param.must.be.final=\
multi-catch parameter {0} must be final
compiler.err.finally.without.try=\
''finally'' without ''try''
compiler.err.foreach.not.applicable.to.type=\

View File

@ -0,0 +1,28 @@
/*
* @test /nodynamiccopyright/
* @bug 6943289
*
* @summary Project Coin: Improved Exception Handling for Java (aka 'multicatch')
* @author darcy
* @compile/fail/ref=Neg01eff_final.out -XDrawDiagnostics Neg01eff_final.java
* @compile -source 6 -XDrawDiagnostics Neg01eff_final.java
*
*/
class Neg01eff_final {
static class A extends Exception {}
static class B1 extends A {}
static class B2 extends A {}
class Test {
void m() throws A {
try {
throw new B1();
} catch (A ex1) {
try {
throw ex1; // used to throw A, now throws B1!
} catch (B2 ex2) { }//unreachable
}
}
}
}

View File

@ -0,0 +1,2 @@
Neg01eff_final.java:24:19: compiler.err.except.never.thrown.in.try: Neg01eff_final.B2
1 error

View File

@ -20,6 +20,8 @@ class Neg02 {
else {
throw new B();
}
} catch (A | B ex) { }
} catch (final A | B ex) {
ex = new B();
}
}
}

View File

@ -1,2 +1,2 @@
Neg02.java:23:24: compiler.err.multicatch.param.must.be.final: ex
Neg02.java:24:13: compiler.err.multicatch.parameter.may.not.be.assigned: ex
1 error

View File

@ -0,0 +1,27 @@
/*
* @test /nodynamiccopyright/
* @bug 6943289 6993963
*
* @summary Project Coin: Improved Exception Handling for Java (aka 'multicatch')
* @author mcimadamore
* @compile/fail/ref=Neg02eff_final.out -XDrawDiagnostics Neg02eff_final.java
*
*/
class Neg02eff_final {
static class A extends Exception {}
static class B extends Exception {}
void m() {
try {
if (true) {
throw new A();
}
else {
throw new B();
}
} catch (A | B ex) {
ex = new B();
}
}
}

View File

@ -0,0 +1,2 @@
Neg02eff_final.java:24:13: compiler.err.multicatch.parameter.may.not.be.assigned: ex
1 error

View File

@ -9,19 +9,22 @@
*/
class Neg03 {
static class A extends Exception {}
static class B extends Exception {}
void m() {
static class A extends Exception { public void m() {}; public Object f;}
static class B1 extends A {}
static class B2 extends A {}
void m() throws B1, B2 {
try {
if (true) {
throw new A();
throw new B1();
}
else {
throw new B();
throw new B2();
}
} catch (final A | B ex) {
ex = new B();
} catch (Exception ex) {
ex = new B2(); //effectively final analysis disabled!
throw ex;
}
}
}

View File

@ -1,2 +1,2 @@
Neg03.java:24:13: compiler.err.multicatch.parameter.may.not.be.assigned: ex
Neg03.java:27:13: compiler.err.unreported.exception.need.to.catch.or.throw: java.lang.Exception
1 error

View File

@ -0,0 +1,31 @@
/*
* @test /nodynamiccopyright/
* @bug 6943289
*
* @summary Project Coin: Improved Exception Handling for Java (aka 'multicatch')
* @author mcimadamore
* @compile/fail/ref=Neg04eff_final.out -XDrawDiagnostics Neg04eff_final.java
*
*/
class Neg04eff_final {
static class A extends Exception {}
static class B extends Exception {}
void test() throws B {
try {
if (true) {
throw new A();
} else if (false) {
throw new B();
} else {
throw (Throwable)new Exception();
}
}
catch (A e) {}
catch (Exception e) {
throw e;
}
catch (Throwable t) {}
}
}

View File

@ -0,0 +1,2 @@
Neg04eff_final.java:27:13: compiler.err.unreported.exception.need.to.catch.or.throw: java.lang.Exception
1 error

View File

@ -0,0 +1,33 @@
/*
* @test /nodynamiccopyright/
* @bug 6943289
*
* @summary Project Coin: Improved Exception Handling for Java (aka 'multicatch')
* @author mcimadamore
* @compile/fail/ref=Neg05.out -XDrawDiagnostics Neg05.java
*
*/
class Neg02 {
static class Foo<X> {
Foo(X x) {}
}
static interface Base<X> {}
static class A extends Exception implements Base<String> {}
static class B extends Exception implements Base<Integer> {}
void m() {
try {
if (true) {
throw new A();
}
else {
throw new B();
}
} catch (A | B ex) {
Foo<?> f = new Foo<>(ex);
}
}
}

View File

@ -0,0 +1,2 @@
Neg05.java:30:31: compiler.err.cant.apply.diamond.1: (compiler.misc.diamond: Neg02.Foo), (compiler.misc.diamond.invalid.arg: java.lang.Exception&Neg02.Base<? extends java.lang.Object&java.io.Serializable&java.lang.Comparable<? extends java.lang.Object&java.io.Serializable&java.lang.Comparable<?>>>, (compiler.misc.diamond: Neg02.Foo))
1 error

View File

@ -0,0 +1,27 @@
/*
* @test /nodynamiccopyright/
* @bug 6993963
*
* @summary Project Coin: Use precise exception analysis for effectively final catch parameters
* @author mcimadamore
* @compile Pos06.java
*
*/
class Pos06 {
static class A extends Exception {}
static class B extends Exception {}
void m() {
try {
if (true) {
throw new A();
}
else {
throw new B();
}
} catch (A | B ex) {
System.out.println(ex);
}
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2010, 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 6993963
* @summary Project Coin: Use precise exception analysis for effectively final catch parameters
* @compile Pos07.java
*/
class Pos07 {
static class A extends Exception { public void m() {}; public Object f;}
static class B1 extends A {}
static class B2 extends A {}
void m() throws B1, B2 {
try {
if (true) {
throw new B1();
}
else {
throw new B2();
}
} catch (Exception ex) { //effectively final analysis
throw ex;
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2010, 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.
*/
/**
* Annotation used by ModelChecker to mark the class whose model is to be checked
*/
@interface Check {}

View File

@ -21,18 +21,11 @@
* questions.
*/
// key: compiler.err.multicatch.param.must.be.final
import javax.lang.model.element.ElementKind;
class MulticatchMustBeFinal {
void e1() throws NullPointerException { }
void e2() throws IllegalArgumentException { }
void m() {
try {
e1();
e2();
} catch (NullPointerException | IllegalArgumentException e) {
e.printStackTrace();
}
}
/**
* Annotation used by ModelChecker to mark a member that is to be checked
*/
@interface Member {
ElementKind value();
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2010, 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.
*/
import javax.lang.model.element.ElementKind;
@Check
class Test {
class A extends Exception {
@Member(ElementKind.METHOD)
public void m() {};
@Member(ElementKind.FIELD)
public Object f;
}
class B1 extends A {}
class B2 extends A {}
void test(){
try {
if (true)
throw new B1();
else
throw new B2();
}
catch(B1 | B2 ex) { }
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2010, 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 6993963
* @summary Project Coin: Use precise exception analysis for effectively final catch parameters
* @library ../../lib
* @build JavacTestingAbstractProcessor ModelChecker
* @compile -processor ModelChecker Model01.java
*/
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import com.sun.source.util.TreePath;
import java.util.Set;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
@SupportedAnnotationTypes("Check")
public class ModelChecker extends JavacTestingAbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver())
return true;
Trees trees = Trees.instance(processingEnv);
TypeElement testAnno = elements.getTypeElement("Check");
for (Element elem: roundEnv.getElementsAnnotatedWith(testAnno)) {
TreePath p = trees.getPath(elem);
new MulticatchParamTester(trees).scan(p, null);
}
return true;
}
class MulticatchParamTester extends TreePathScanner<Void, Void> {
Trees trees;
public MulticatchParamTester(Trees trees) {
super();
this.trees = trees;
}
@Override
public Void visitVariable(VariableTree node, Void p) {
Element ex = trees.getElement(getCurrentPath());
if (ex.getSimpleName().contentEquals("ex")) {
assertTrue(ex.getKind() == ElementKind.EXCEPTION_PARAMETER, "Expected EXCEPTION_PARAMETER - found " + ex.getKind());
for (Element e : types.asElement(ex.asType()).getEnclosedElements()) {
Member m = e.getAnnotation(Member.class);
if (m != null) {
assertTrue(e.getKind() == m.value(), "Expected " + m.value() + " - found " + e.getKind());
}
}
assertTrue(assertionCount == 3, "Expected 3 assertions - found " + assertionCount);
}
return super.visitVariable(node, p);
}
}
private static void assertTrue(boolean cond, String msg) {
assertionCount++;
if (!cond)
throw new AssertionError(msg);
}
static int assertionCount = 0;
}