Fri, 13 Feb 2015 17:18:21 +0100
8068517: Compiler may generate wrong InnerClasses attribute for static enum reference
Summary: Making sure enum's abstractness is resolved before writing InnerClasses entry about it.
Reviewed-by: mcimadamore
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Wed Feb 11 12:18:57 2015 -0800 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Feb 13 17:18:21 2015 +0100 1.3 @@ -1153,6 +1153,16 @@ 1.4 public <R, P> R accept(Symbol.Visitor<R, P> v, P p) { 1.5 return v.visitClassSymbol(this, p); 1.6 } 1.7 + 1.8 + public void markAbstractIfNeeded(Types types) { 1.9 + if (types.enter.getEnv(this) != null && 1.10 + (flags() & ENUM) != 0 && types.supertype(type).tsym == types.syms.enumSym && 1.11 + (flags() & (FINAL | ABSTRACT)) == 0) { 1.12 + if (types.firstUnimplementedAbstract(this) != null) 1.13 + // add the ABSTRACT flag to an enum 1.14 + flags_field |= ABSTRACT; 1.15 + } 1.16 + } 1.17 } 1.18 1.19
2.1 --- a/src/share/classes/com/sun/tools/javac/code/Types.java Wed Feb 11 12:18:57 2015 -0800 2.2 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Fri Feb 13 17:18:21 2015 +0100 2.3 @@ -47,6 +47,7 @@ 2.4 import com.sun.tools.javac.util.*; 2.5 import static com.sun.tools.javac.code.BoundKind.*; 2.6 import static com.sun.tools.javac.code.Flags.*; 2.7 +import static com.sun.tools.javac.code.Kinds.MTH; 2.8 import static com.sun.tools.javac.code.Scope.*; 2.9 import static com.sun.tools.javac.code.Symbol.*; 2.10 import static com.sun.tools.javac.code.Type.*; 2.11 @@ -85,6 +86,7 @@ 2.12 final boolean allowBoxing; 2.13 final boolean allowCovariantReturns; 2.14 final boolean allowObjectToPrimitiveCast; 2.15 + final boolean allowDefaultMethods; 2.16 final ClassReader reader; 2.17 final Check chk; 2.18 final Enter enter; 2.19 @@ -111,6 +113,7 @@ 2.20 allowBoxing = source.allowBoxing(); 2.21 allowCovariantReturns = source.allowCovariantReturns(); 2.22 allowObjectToPrimitiveCast = source.allowObjectToPrimitiveCast(); 2.23 + allowDefaultMethods = source.allowDefaultMethods(); 2.24 reader = ClassReader.instance(context); 2.25 chk = Check.instance(context); 2.26 enter = Enter.instance(context); 2.27 @@ -2763,6 +2766,59 @@ 2.28 // </editor-fold> 2.29 2.30 2.31 + /** Return first abstract member of class `sym'. 2.32 + */ 2.33 + public MethodSymbol firstUnimplementedAbstract(ClassSymbol sym) { 2.34 + try { 2.35 + return firstUnimplementedAbstractImpl(sym, sym); 2.36 + } catch (CompletionFailure ex) { 2.37 + chk.completionError(enter.getEnv(sym).tree.pos(), ex); 2.38 + return null; 2.39 + } 2.40 + } 2.41 + //where: 2.42 + private MethodSymbol firstUnimplementedAbstractImpl(ClassSymbol impl, ClassSymbol c) { 2.43 + MethodSymbol undef = null; 2.44 + // Do not bother to search in classes that are not abstract, 2.45 + // since they cannot have abstract members. 2.46 + if (c == impl || (c.flags() & (ABSTRACT | INTERFACE)) != 0) { 2.47 + Scope s = c.members(); 2.48 + for (Scope.Entry e = s.elems; 2.49 + undef == null && e != null; 2.50 + e = e.sibling) { 2.51 + if (e.sym.kind == MTH && 2.52 + (e.sym.flags() & (ABSTRACT|IPROXY|DEFAULT)) == ABSTRACT) { 2.53 + MethodSymbol absmeth = (MethodSymbol)e.sym; 2.54 + MethodSymbol implmeth = absmeth.implementation(impl, this, true); 2.55 + if (implmeth == null || implmeth == absmeth) { 2.56 + //look for default implementations 2.57 + if (allowDefaultMethods) { 2.58 + MethodSymbol prov = interfaceCandidates(impl.type, absmeth).head; 2.59 + if (prov != null && prov.overrides(absmeth, impl, this, true)) { 2.60 + implmeth = prov; 2.61 + } 2.62 + } 2.63 + } 2.64 + if (implmeth == null || implmeth == absmeth) { 2.65 + undef = absmeth; 2.66 + } 2.67 + } 2.68 + } 2.69 + if (undef == null) { 2.70 + Type st = supertype(c.type); 2.71 + if (st.hasTag(CLASS)) 2.72 + undef = firstUnimplementedAbstractImpl(impl, (ClassSymbol)st.tsym); 2.73 + } 2.74 + for (List<Type> l = interfaces(c.type); 2.75 + undef == null && l.nonEmpty(); 2.76 + l = l.tail) { 2.77 + undef = firstUnimplementedAbstractImpl(impl, (ClassSymbol)l.head.tsym); 2.78 + } 2.79 + } 2.80 + return undef; 2.81 + } 2.82 + 2.83 + 2.84 //where 2.85 public List<MethodSymbol> interfaceCandidates(Type site, MethodSymbol ms) { 2.86 Filter<Symbol> filter = new MethodFilter(ms, site);
3.1 --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Wed Feb 11 12:18:57 2015 -0800 3.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Fri Feb 13 17:18:21 2015 +0100 3.3 @@ -4269,6 +4269,8 @@ 3.4 chk.validate(tree.implementing, env); 3.5 } 3.6 3.7 + c.markAbstractIfNeeded(types); 3.8 + 3.9 // If this is a non-abstract class, check that it has no abstract 3.10 // methods or unimplemented methods of an implemented interface. 3.11 if ((c.flags() & (ABSTRACT | INTERFACE)) == 0) {
4.1 --- a/src/share/classes/com/sun/tools/javac/comp/Check.java Wed Feb 11 12:18:57 2015 -0800 4.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Check.java Fri Feb 13 17:18:21 2015 +0100 4.3 @@ -2049,70 +2049,15 @@ 4.4 * @param c The class. 4.5 */ 4.6 void checkAllDefined(DiagnosticPosition pos, ClassSymbol c) { 4.7 - try { 4.8 - MethodSymbol undef = firstUndef(c, c); 4.9 - if (undef != null) { 4.10 - if ((c.flags() & ENUM) != 0 && 4.11 - types.supertype(c.type).tsym == syms.enumSym && 4.12 - (c.flags() & FINAL) == 0) { 4.13 - // add the ABSTRACT flag to an enum 4.14 - c.flags_field |= ABSTRACT; 4.15 - } else { 4.16 - MethodSymbol undef1 = 4.17 - new MethodSymbol(undef.flags(), undef.name, 4.18 - types.memberType(c.type, undef), undef.owner); 4.19 - log.error(pos, "does.not.override.abstract", 4.20 - c, undef1, undef1.location()); 4.21 - } 4.22 - } 4.23 - } catch (CompletionFailure ex) { 4.24 - completionError(pos, ex); 4.25 + MethodSymbol undef = types.firstUnimplementedAbstract(c); 4.26 + if (undef != null) { 4.27 + MethodSymbol undef1 = 4.28 + new MethodSymbol(undef.flags(), undef.name, 4.29 + types.memberType(c.type, undef), undef.owner); 4.30 + log.error(pos, "does.not.override.abstract", 4.31 + c, undef1, undef1.location()); 4.32 } 4.33 } 4.34 -//where 4.35 - /** Return first abstract member of class `c' that is not defined 4.36 - * in `impl', null if there is none. 4.37 - */ 4.38 - private MethodSymbol firstUndef(ClassSymbol impl, ClassSymbol c) { 4.39 - MethodSymbol undef = null; 4.40 - // Do not bother to search in classes that are not abstract, 4.41 - // since they cannot have abstract members. 4.42 - if (c == impl || (c.flags() & (ABSTRACT | INTERFACE)) != 0) { 4.43 - Scope s = c.members(); 4.44 - for (Scope.Entry e = s.elems; 4.45 - undef == null && e != null; 4.46 - e = e.sibling) { 4.47 - if (e.sym.kind == MTH && 4.48 - (e.sym.flags() & (ABSTRACT|IPROXY|DEFAULT)) == ABSTRACT) { 4.49 - MethodSymbol absmeth = (MethodSymbol)e.sym; 4.50 - MethodSymbol implmeth = absmeth.implementation(impl, types, true); 4.51 - if (implmeth == null || implmeth == absmeth) { 4.52 - //look for default implementations 4.53 - if (allowDefaultMethods) { 4.54 - MethodSymbol prov = types.interfaceCandidates(impl.type, absmeth).head; 4.55 - if (prov != null && prov.overrides(absmeth, impl, types, true)) { 4.56 - implmeth = prov; 4.57 - } 4.58 - } 4.59 - } 4.60 - if (implmeth == null || implmeth == absmeth) { 4.61 - undef = absmeth; 4.62 - } 4.63 - } 4.64 - } 4.65 - if (undef == null) { 4.66 - Type st = types.supertype(c.type); 4.67 - if (st.hasTag(CLASS)) 4.68 - undef = firstUndef(impl, (ClassSymbol)st.tsym); 4.69 - } 4.70 - for (List<Type> l = types.interfaces(c.type); 4.71 - undef == null && l.nonEmpty(); 4.72 - l = l.tail) { 4.73 - undef = firstUndef(impl, (ClassSymbol)l.head.tsym); 4.74 - } 4.75 - } 4.76 - return undef; 4.77 - } 4.78 4.79 void checkNonCyclicDecl(JCClassDecl tree) { 4.80 CycleChecker cc = new CycleChecker();
5.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Wed Feb 11 12:18:57 2015 -0800 5.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Fri Feb 13 17:18:21 2015 +0100 5.3 @@ -1027,6 +1027,7 @@ 5.4 l.nonEmpty(); 5.5 l = l.tail) { 5.6 ClassSymbol inner = l.head; 5.7 + inner.markAbstractIfNeeded(types); 5.8 char flags = (char) adjustFlags(inner.flags_field); 5.9 if ((flags & INTERFACE) != 0) flags |= ABSTRACT; // Interfaces are always ABSTRACT 5.10 if (inner.name.isEmpty()) flags &= ~FINAL; // Anonymous class: unset FINAL flag
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/tools/javac/classfiles/InnerClasses/T8068517.java Fri Feb 13 17:18:21 2015 +0100 6.3 @@ -0,0 +1,126 @@ 6.4 +/* 6.5 + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.7 + * 6.8 + * This code is free software; you can redistribute it and/or modify it 6.9 + * under the terms of the GNU General Public License version 2 only, as 6.10 + * published by the Free Software Foundation. 6.11 + * 6.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 6.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.15 + * version 2 for more details (a copy is included in the LICENSE file that 6.16 + * accompanied this code). 6.17 + * 6.18 + * You should have received a copy of the GNU General Public License version 6.19 + * 2 along with this work; if not, write to the Free Software Foundation, 6.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.21 + * 6.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 6.23 + * or visit www.oracle.com if you need additional information or have any 6.24 + * questions. 6.25 + */ 6.26 + 6.27 +/** @test 6.28 + * @bug 8068517 6.29 + * @summary Verify that nested enums have correct abstract flag in the InnerClasses attribute. 6.30 + * @library /tools/javac/lib 6.31 + * @build ToolBox T8068517 6.32 + * @run main T8068517 6.33 + */ 6.34 + 6.35 +import com.sun.tools.javac.util.Assert; 6.36 +import java.io.File; 6.37 +import java.nio.file.Files; 6.38 +import java.util.Arrays; 6.39 +import java.util.List; 6.40 +import java.util.stream.Collectors; 6.41 +import java.util.stream.Stream; 6.42 +import javax.tools.JavaCompiler; 6.43 +import javax.tools.ToolProvider; 6.44 + 6.45 +public class T8068517 { 6.46 + 6.47 + public static void main(String[] args) throws Exception { 6.48 + new T8068517().run(); 6.49 + } 6.50 + 6.51 + void run() throws Exception { 6.52 + runTest("class A {\n" + 6.53 + " enum AInner implements Runnable {\n" + 6.54 + " A {\n" + 6.55 + " public void run() {}\n" + 6.56 + " };\n" + 6.57 + " }\n" + 6.58 + "}\n", 6.59 + "class B {\n" + 6.60 + " A.AInner a;\n" + 6.61 + "}"); 6.62 + runTest("class A {\n" + 6.63 + " enum AInner implements Runnable {\n" + 6.64 + " A {\n" + 6.65 + " public void run() {}\n" + 6.66 + " };\n" + 6.67 + " }\n" + 6.68 + " AInner aInner;\n" + 6.69 + "}\n", 6.70 + "class B {\n" + 6.71 + " void test(A a) {;\n" + 6.72 + " switch (a.aInner) {\n" + 6.73 + " case A: break;\n" + 6.74 + " }\n" + 6.75 + " };\n" + 6.76 + "}"); 6.77 + runTest("class A {\n" + 6.78 + " enum AInner implements Runnable {\n" + 6.79 + " A {\n" + 6.80 + " public void run() {}\n" + 6.81 + " };\n" + 6.82 + " }\n" + 6.83 + " AInner aInner;\n" + 6.84 + "}\n", 6.85 + "class B {\n" + 6.86 + " void test(A a) {;\n" + 6.87 + " System.err.println(a.aInner.toString());\n" + 6.88 + " };\n" + 6.89 + "}"); 6.90 + runTest("class A {\n" + 6.91 + " enum AInner implements Runnable {\n" + 6.92 + " A {\n" + 6.93 + " public void run() {}\n" + 6.94 + " };\n" + 6.95 + " }\n" + 6.96 + " AInner aInner() {\n" + 6.97 + " return null;\n" + 6.98 + " }\n" + 6.99 + "}\n", 6.100 + "class B {\n" + 6.101 + " void test(A a) {;\n" + 6.102 + " System.err.println(a.aInner().toString());\n" + 6.103 + " };\n" + 6.104 + "}"); 6.105 + } 6.106 + 6.107 + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 6.108 + int testN = 0; 6.109 + 6.110 + void runTest(String aJava, String bJava) throws Exception { 6.111 + File testClasses = new File(System.getProperty("test.classes")); 6.112 + File target1 = new File(testClasses, "T8068517s" + testN++); 6.113 + doCompile(target1, aJava, bJava); 6.114 + File target2 = new File(testClasses, "T8068517s" + testN++); 6.115 + doCompile(target2, bJava, aJava); 6.116 + 6.117 + Assert.check(Arrays.equals(Files.readAllBytes(new File(target1, "B.class").toPath()), 6.118 + Files.readAllBytes(new File(target2, "B.class").toPath()))); 6.119 + } 6.120 + 6.121 + void doCompile(File target, String... sources) throws Exception { 6.122 + target.mkdirs(); 6.123 + List<String> options = Arrays.asList("-d", target.getAbsolutePath()); 6.124 + List<ToolBox.JavaSource> files = Stream.of(sources) 6.125 + .map(ToolBox.JavaSource::new) 6.126 + .collect(Collectors.toList()); 6.127 + compiler.getTask(null, null, null, options, null, files).call(); 6.128 + } 6.129 +}