Sat, 18 Sep 2010 14:24:09 -0700
6863465: javac doesn't detect circular subclass dependencies via qualified names
Summary: class inheritance circularity check should look at trees, not just symbols
Reviewed-by: jjg
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Check.java Sat Sep 18 09:56:23 2010 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Check.java Sat Sep 18 14:24:09 2010 -0700 1.3 @@ -60,6 +60,7 @@ 1.4 private final Names names; 1.5 private final Log log; 1.6 private final Symtab syms; 1.7 + private final Enter enter; 1.8 private final Infer infer; 1.9 private final Types types; 1.10 private final JCDiagnostic.Factory diags; 1.11 @@ -86,6 +87,7 @@ 1.12 names = Names.instance(context); 1.13 log = Log.instance(context); 1.14 syms = Symtab.instance(context); 1.15 + enter = Enter.instance(context); 1.16 infer = Infer.instance(context); 1.17 this.types = Types.instance(context); 1.18 diags = JCDiagnostic.Factory.instance(context); 1.19 @@ -1727,6 +1729,113 @@ 1.20 return undef; 1.21 } 1.22 1.23 + void checkNonCyclicDecl(JCClassDecl tree) { 1.24 + CycleChecker cc = new CycleChecker(); 1.25 + cc.scan(tree); 1.26 + if (!cc.errorFound && !cc.partialCheck) { 1.27 + tree.sym.flags_field |= ACYCLIC; 1.28 + } 1.29 + } 1.30 + 1.31 + class CycleChecker extends TreeScanner { 1.32 + 1.33 + List<Symbol> seenClasses = List.nil(); 1.34 + boolean errorFound = false; 1.35 + boolean partialCheck = false; 1.36 + 1.37 + private void checkSymbol(DiagnosticPosition pos, Symbol sym) { 1.38 + if (sym != null && sym.kind == TYP) { 1.39 + Env<AttrContext> classEnv = enter.getEnv((TypeSymbol)sym); 1.40 + if (classEnv != null) { 1.41 + DiagnosticSource prevSource = log.currentSource(); 1.42 + try { 1.43 + log.useSource(classEnv.toplevel.sourcefile); 1.44 + scan(classEnv.tree); 1.45 + } 1.46 + finally { 1.47 + log.useSource(prevSource.getFile()); 1.48 + } 1.49 + } else if (sym.kind == TYP) { 1.50 + checkClass(pos, sym, List.<JCTree>nil()); 1.51 + } 1.52 + } else { 1.53 + //not completed yet 1.54 + partialCheck = true; 1.55 + } 1.56 + } 1.57 + 1.58 + @Override 1.59 + public void visitSelect(JCFieldAccess tree) { 1.60 + super.visitSelect(tree); 1.61 + checkSymbol(tree.pos(), tree.sym); 1.62 + } 1.63 + 1.64 + @Override 1.65 + public void visitIdent(JCIdent tree) { 1.66 + checkSymbol(tree.pos(), tree.sym); 1.67 + } 1.68 + 1.69 + @Override 1.70 + public void visitTypeApply(JCTypeApply tree) { 1.71 + scan(tree.clazz); 1.72 + } 1.73 + 1.74 + @Override 1.75 + public void visitTypeArray(JCArrayTypeTree tree) { 1.76 + scan(tree.elemtype); 1.77 + } 1.78 + 1.79 + @Override 1.80 + public void visitClassDef(JCClassDecl tree) { 1.81 + List<JCTree> supertypes = List.nil(); 1.82 + if (tree.getExtendsClause() != null) { 1.83 + supertypes = supertypes.prepend(tree.getExtendsClause()); 1.84 + } 1.85 + if (tree.getImplementsClause() != null) { 1.86 + for (JCTree intf : tree.getImplementsClause()) { 1.87 + supertypes = supertypes.prepend(intf); 1.88 + } 1.89 + } 1.90 + checkClass(tree.pos(), tree.sym, supertypes); 1.91 + } 1.92 + 1.93 + void checkClass(DiagnosticPosition pos, Symbol c, List<JCTree> supertypes) { 1.94 + if ((c.flags_field & ACYCLIC) != 0) 1.95 + return; 1.96 + if (seenClasses.contains(c)) { 1.97 + errorFound = true; 1.98 + noteCyclic(pos, (ClassSymbol)c); 1.99 + } else if (!c.type.isErroneous()) { 1.100 + try { 1.101 + seenClasses = seenClasses.prepend(c); 1.102 + if (c.type.tag == CLASS) { 1.103 + if (supertypes.nonEmpty()) { 1.104 + scan(supertypes); 1.105 + } 1.106 + else { 1.107 + ClassType ct = (ClassType)c.type; 1.108 + if (ct.supertype_field == null || 1.109 + ct.interfaces_field == null) { 1.110 + //not completed yet 1.111 + partialCheck = true; 1.112 + return; 1.113 + } 1.114 + checkSymbol(pos, ct.supertype_field.tsym); 1.115 + for (Type intf : ct.interfaces_field) { 1.116 + checkSymbol(pos, intf.tsym); 1.117 + } 1.118 + } 1.119 + if (c.owner.kind == TYP) { 1.120 + checkSymbol(pos, c.owner); 1.121 + } 1.122 + } 1.123 + } finally { 1.124 + seenClasses = seenClasses.tail; 1.125 + } 1.126 + } 1.127 + } 1.128 + } 1.129 + 1.130 /** Check for cyclic references. Issue an error if the 1.131 * symbol of the type referred to has a LOCKED flag set. 1.132 *
2.1 --- a/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Sat Sep 18 09:56:23 2010 -0700 2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Sat Sep 18 14:24:09 2010 -0700 2.3 @@ -927,7 +927,7 @@ 2.4 tp.accept(new TypeAnnotate(baseEnv)); 2.5 tree.accept(new TypeAnnotate(env)); 2.6 2.7 - chk.checkNonCyclic(tree.pos(), c.type); 2.8 + chk.checkNonCyclicDecl(tree); 2.9 2.10 attr.attribTypeVariables(tree.typarams, baseEnv); 2.11
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/tools/javac/6863465/T6863465a.java Sat Sep 18 14:24:09 2010 -0700 3.3 @@ -0,0 +1,14 @@ 3.4 +/** 3.5 + * @test /nodynamiccopyright/ 3.6 + * @bug 6863465 3.7 + * @summary javac doesn't detect circular subclass dependencies via qualified names 3.8 + * @author Maurizio Cimadamore 3.9 + * @compile/fail/ref=T6863465a.out -XDrawDiagnostics T6863465a.java 3.10 + */ 3.11 + 3.12 +class T6863465a { 3.13 + static class a { static interface b {} } 3.14 + static class c extends a implements z.y {} 3.15 + static class x { static interface y {} } 3.16 + static class z extends x implements c.b {} 3.17 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/tools/javac/6863465/T6863465a.out Sat Sep 18 14:24:09 2010 -0700 4.3 @@ -0,0 +1,2 @@ 4.4 +T6863465a.java:11:12: compiler.err.cyclic.inheritance: T6863465a.c 4.5 +1 error
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/tools/javac/6863465/T6863465b.java Sat Sep 18 14:24:09 2010 -0700 5.3 @@ -0,0 +1,14 @@ 5.4 +/** 5.5 + * @test /nodynamiccopyright/ 5.6 + * @bug 6863465 5.7 + * @summary javac doesn't detect circular subclass dependencies via qualified names 5.8 + * @author Maurizio Cimadamore 5.9 + * @compile/fail/ref=T6863465b.out -XDrawDiagnostics T6863465b.java 5.10 + */ 5.11 + 5.12 +class T6863465b { 5.13 + static class a { static interface b { static interface d {} } } 5.14 + static class c extends a implements z.y, z.d {} 5.15 + static class x { static interface y {} } 5.16 + static class z extends x implements c.b {} 5.17 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/tools/javac/6863465/T6863465b.out Sat Sep 18 14:24:09 2010 -0700 6.3 @@ -0,0 +1,2 @@ 6.4 +T6863465b.java:11:12: compiler.err.cyclic.inheritance: T6863465b.c 6.5 +1 error
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/test/tools/javac/6863465/T6863465c.java Sat Sep 18 14:24:09 2010 -0700 7.3 @@ -0,0 +1,14 @@ 7.4 +/** 7.5 + * @test /nodynamiccopyright/ 7.6 + * @bug 6863465 7.7 + * @summary javac doesn't detect circular subclass dependencies via qualified names 7.8 + * @author Maurizio Cimadamore 7.9 + * @compile/fail/ref=T6863465c.out -XDrawDiagnostics T6863465c.java 7.10 + */ 7.11 + 7.12 +class T6863465c { 7.13 + static class x { static interface y {} } 7.14 + static class z extends x implements c.b {} 7.15 + static class a { static interface b { static interface d {} } } 7.16 + static class c extends a implements z.y, z.d {} 7.17 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/test/tools/javac/6863465/T6863465c.out Sat Sep 18 14:24:09 2010 -0700 8.3 @@ -0,0 +1,3 @@ 8.4 +T6863465c.java:13:47: compiler.err.cant.resolve.location: kindname.class, d, , , kindname.class, T6863465c.z 8.5 +T6863465c.java:11:12: compiler.err.cyclic.inheritance: T6863465c.z 8.6 +2 errors
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/test/tools/javac/6863465/T6863465d.java Sat Sep 18 14:24:09 2010 -0700 9.3 @@ -0,0 +1,14 @@ 9.4 +/** 9.5 + * @test /nodynamiccopyright/ 9.6 + * @bug 6863465 9.7 + * @summary javac doesn't detect circular subclass dependencies via qualified names 9.8 + * @author Maurizio Cimadamore 9.9 + * @compile/fail/ref=T6863465d.out -XDrawDiagnostics T6863465d.java 9.10 + */ 9.11 + 9.12 +class T6863465d { 9.13 + static class a { static interface b { static interface d {} } } 9.14 + static class c extends a implements z.y, z.d {} 9.15 + static class x { static interface y { static interface w {} } } 9.16 + static class z extends x implements c.b, c.w {} 9.17 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/test/tools/javac/6863465/T6863465d.out Sat Sep 18 14:24:09 2010 -0700 10.3 @@ -0,0 +1,3 @@ 10.4 +T6863465d.java:13:47: compiler.err.cant.resolve.location: kindname.class, w, , , kindname.class, T6863465d.c 10.5 +T6863465d.java:11:12: compiler.err.cyclic.inheritance: T6863465d.c 10.6 +2 errors
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/test/tools/javac/6863465/TestCircularClassfile.java Sat Sep 18 14:24:09 2010 -0700 11.3 @@ -0,0 +1,123 @@ 11.4 +/* 11.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 11.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 11.7 + * 11.8 + * This code is free software; you can redistribute it and/or modify it 11.9 + * under the terms of the GNU General Public License version 2 only, as 11.10 + * published by the Free Software Foundation. 11.11 + * 11.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 11.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11.15 + * version 2 for more details (a copy is included in the LICENSE file that 11.16 + * accompanied this code). 11.17 + * 11.18 + * You should have received a copy of the GNU General Public License version 11.19 + * 2 along with this work; if not, write to the Free Software Foundation, 11.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 11.21 + * 11.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 11.23 + * or visit www.oracle.com if you need additional information or have any 11.24 + * questions. 11.25 + */ 11.26 + 11.27 +/* 11.28 + * @test 11.29 + * @bug 6863465 11.30 + * @summary javac doesn't detect circular subclass dependencies via qualified names 11.31 + * @run main TestCircularClassfile 11.32 + */ 11.33 + 11.34 +import java.io.*; 11.35 +import java.net.URI; 11.36 +import java.util.Arrays; 11.37 +import javax.tools.Diagnostic; 11.38 +import javax.tools.DiagnosticListener; 11.39 +import javax.tools.JavaCompiler; 11.40 +import javax.tools.JavaFileObject; 11.41 +import javax.tools.SimpleJavaFileObject; 11.42 +import javax.tools.ToolProvider; 11.43 + 11.44 +import com.sun.source.util.JavacTask; 11.45 +import java.util.EnumSet; 11.46 + 11.47 +public class TestCircularClassfile { 11.48 + 11.49 + enum ClassName { 11.50 + A("A"), 11.51 + B("B"), 11.52 + C("C"), 11.53 + OBJECT("Object"); 11.54 + 11.55 + String name; 11.56 + 11.57 + ClassName(String name) { 11.58 + this.name = name; 11.59 + } 11.60 + } 11.61 + 11.62 + static class JavaSource extends SimpleJavaFileObject { 11.63 + 11.64 + final static String sourceStub = "class #C extends #S {}"; 11.65 + 11.66 + String source; 11.67 + 11.68 + public JavaSource(ClassName clazz, ClassName sup) { 11.69 + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 11.70 + source = sourceStub.replace("#C", clazz.name).replace("#S", sup.name); 11.71 + } 11.72 + 11.73 + @Override 11.74 + public CharSequence getCharContent(boolean ignoreEncodingErrors) { 11.75 + return source; 11.76 + } 11.77 + } 11.78 + 11.79 + public static void main(String... args) throws Exception { 11.80 + int count = 0; 11.81 + for (ClassName clazz : EnumSet.of(ClassName.A, ClassName.B, ClassName.C)) { 11.82 + for (ClassName sup : EnumSet.of(ClassName.A, ClassName.B, ClassName.C)) { 11.83 + if (sup.ordinal() < clazz.ordinal()) continue; 11.84 + check("sub_"+count++, clazz, sup); 11.85 + } 11.86 + } 11.87 + } 11.88 + 11.89 + static JavaSource[] initialSources = new JavaSource[] { 11.90 + new JavaSource(ClassName.A, ClassName.OBJECT), 11.91 + new JavaSource(ClassName.B, ClassName.A), 11.92 + new JavaSource(ClassName.C, ClassName.B) 11.93 + }; 11.94 + 11.95 + static String workDir = System.getProperty("user.dir"); 11.96 + 11.97 + static void check(String destPath, ClassName clazz, ClassName sup) throws Exception { 11.98 + File destDir = new File(workDir, destPath); destDir.mkdir(); 11.99 + final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 11.100 + JavacTask ct = (JavacTask)tool.getTask(null, null, null, 11.101 + Arrays.asList("-d", destPath), null, Arrays.asList(initialSources)); 11.102 + ct.generate(); 11.103 + File fileToRemove = new File(destPath, clazz.name + ".class"); 11.104 + fileToRemove.delete(); 11.105 + JavaSource newSource = new JavaSource(clazz, sup); 11.106 + DiagnosticChecker checker = new DiagnosticChecker(); 11.107 + ct = (JavacTask)tool.getTask(null, null, checker, 11.108 + Arrays.asList("-cp", destPath), null, Arrays.asList(newSource)); 11.109 + ct.analyze(); 11.110 + if (!checker.errorFound) { 11.111 + throw new AssertionError(newSource.source); 11.112 + } 11.113 + } 11.114 + 11.115 + static class DiagnosticChecker implements DiagnosticListener<JavaFileObject> { 11.116 + 11.117 + boolean errorFound = false; 11.118 + 11.119 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 11.120 + if (diagnostic.getKind() == Diagnostic.Kind.ERROR && 11.121 + diagnostic.getCode().equals("compiler.err.cyclic.inheritance")) { 11.122 + errorFound = true; 11.123 + } 11.124 + } 11.125 + } 11.126 +}
12.1 --- a/test/tools/javac/CyclicInheritance.out Sat Sep 18 09:56:23 2010 -0700 12.2 +++ b/test/tools/javac/CyclicInheritance.out Sat Sep 18 14:24:09 2010 -0700 12.3 @@ -4,6 +4,6 @@ 12.4 CyclicInheritance.java:22:1: compiler.err.cyclic.inheritance: I11 12.5 CyclicInheritance.java:27:1: compiler.err.cyclic.inheritance: C211 12.6 CyclicInheritance.java:31:1: compiler.err.cyclic.inheritance: C212 12.7 -CyclicInheritance.java:36:27: compiler.err.report.access: C221.I, private, C221 12.8 -CyclicInheritance.java:40:24: compiler.err.report.access: C222.C, private, C222 12.9 +CyclicInheritance.java:36:1: compiler.err.cyclic.inheritance: C221 12.10 +CyclicInheritance.java:40:1: compiler.err.cyclic.inheritance: C222 12.11 8 errors
13.1 --- a/test/tools/javac/NameCollision.out Sat Sep 18 09:56:23 2010 -0700 13.2 +++ b/test/tools/javac/NameCollision.out Sat Sep 18 14:24:09 2010 -0700 13.3 @@ -1,2 +1,3 @@ 13.4 NameCollision.java:13:31: compiler.err.intf.expected.here 13.5 -1 error 13.6 +NameCollision.java:13:5: compiler.err.cyclic.inheritance: NameCollision.Runnable 13.7 +2 errors