8068517: Compiler may generate wrong InnerClasses attribute for static enum reference
authorjlahoda
Fri, 16 Jan 2015 14:47:25 +0100
changeset 28459 48a68a485760
parent 28458 f20c5dd05e34
child 28460 422ba63d8dda
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
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
langtools/test/tools/javac/classfiles/InnerClasses/T8068517.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Fri Jan 16 12:02:41 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Fri Jan 16 14:47:25 2015 +0100
@@ -1175,6 +1175,16 @@
             return v.visitClassSymbol(this, p);
         }
 
+        public void markAbstractIfNeeded(Types types) {
+            if (types.enter.getEnv(this) != null &&
+                (flags() & ENUM) != 0 && types.supertype(type).tsym == types.syms.enumSym &&
+                (flags() & (FINAL | ABSTRACT)) == 0) {
+                if (types.firstUnimplementedAbstract(this) != null)
+                    // add the ABSTRACT flag to an enum
+                    flags_field |= ABSTRACT;
+            }
+        }
+
         /**Resets the Symbol into the state good for next round of annotation processing.*/
         public void reset() {
             kind = TYP;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Fri Jan 16 12:02:41 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Fri Jan 16 14:47:25 2015 +0100
@@ -48,6 +48,7 @@
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
 import static com.sun.tools.javac.code.Scope.*;
+import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
 import static com.sun.tools.javac.code.Symbol.*;
 import static com.sun.tools.javac.code.Type.*;
 import static com.sun.tools.javac.code.TypeTag.*;
@@ -82,6 +83,7 @@
     final JavacMessages messages;
     final Names names;
     final boolean allowObjectToPrimitiveCast;
+    final boolean allowDefaultMethods;
     final Check chk;
     final Enter enter;
     JCDiagnostic.Factory diags;
@@ -105,6 +107,7 @@
         names = Names.instance(context);
         Source source = Source.instance(context);
         allowObjectToPrimitiveCast = source.allowObjectToPrimitiveCast();
+        allowDefaultMethods = source.allowDefaultMethods();
         chk = Check.instance(context);
         enter = Enter.instance(context);
         capturedName = names.fromString("<captured wildcard>");
@@ -2775,6 +2778,58 @@
     // </editor-fold>
 
 
+    /** Return first abstract member of class `sym'.
+     */
+    public MethodSymbol firstUnimplementedAbstract(ClassSymbol sym) {
+        try {
+            return firstUnimplementedAbstractImpl(sym, sym);
+        } catch (CompletionFailure ex) {
+            chk.completionError(enter.getEnv(sym).tree.pos(), ex);
+            return null;
+        }
+    }
+        //where:
+        private MethodSymbol firstUnimplementedAbstractImpl(ClassSymbol impl, ClassSymbol c) {
+            MethodSymbol undef = null;
+            // Do not bother to search in classes that are not abstract,
+            // since they cannot have abstract members.
+            if (c == impl || (c.flags() & (ABSTRACT | INTERFACE)) != 0) {
+                Scope s = c.members();
+                for (Symbol sym : s.getSymbols(NON_RECURSIVE)) {
+                    if (sym.kind == MTH &&
+                        (sym.flags() & (ABSTRACT|IPROXY|DEFAULT)) == ABSTRACT) {
+                        MethodSymbol absmeth = (MethodSymbol)sym;
+                        MethodSymbol implmeth = absmeth.implementation(impl, this, true);
+                        if (implmeth == null || implmeth == absmeth) {
+                            //look for default implementations
+                            if (allowDefaultMethods) {
+                                MethodSymbol prov = interfaceCandidates(impl.type, absmeth).head;
+                                if (prov != null && prov.overrides(absmeth, impl, this, true)) {
+                                    implmeth = prov;
+                                }
+                            }
+                        }
+                        if (implmeth == null || implmeth == absmeth) {
+                            undef = absmeth;
+                            break;
+                        }
+                    }
+                }
+                if (undef == null) {
+                    Type st = supertype(c.type);
+                    if (st.hasTag(CLASS))
+                        undef = firstUnimplementedAbstractImpl(impl, (ClassSymbol)st.tsym);
+                }
+                for (List<Type> l = interfaces(c.type);
+                     undef == null && l.nonEmpty();
+                     l = l.tail) {
+                    undef = firstUnimplementedAbstractImpl(impl, (ClassSymbol)l.head.tsym);
+                }
+            }
+            return undef;
+        }
+
+
     //where
     public List<MethodSymbol> interfaceCandidates(Type site, MethodSymbol ms) {
         Filter<Symbol> filter = new MethodFilter(ms, site);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jan 16 12:02:41 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jan 16 14:47:25 2015 +0100
@@ -4188,6 +4188,8 @@
             chk.validate(tree.implementing, env);
         }
 
+        c.markAbstractIfNeeded(types);
+
         // If this is a non-abstract class, check that it has no abstract
         // methods or unimplemented methods of an implemented interface.
         if ((c.flags() & (ABSTRACT | INTERFACE)) == 0) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Fri Jan 16 12:02:41 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Fri Jan 16 14:47:25 2015 +0100
@@ -2019,69 +2019,15 @@
      *  @param c            The class.
      */
     void checkAllDefined(DiagnosticPosition pos, ClassSymbol c) {
-        try {
-            MethodSymbol undef = firstUndef(c, c);
-            if (undef != null) {
-                if ((c.flags() & ENUM) != 0 &&
-                    types.supertype(c.type).tsym == syms.enumSym &&
-                    (c.flags() & FINAL) == 0) {
-                    // add the ABSTRACT flag to an enum
-                    c.flags_field |= ABSTRACT;
-                } else {
-                    MethodSymbol undef1 =
-                        new MethodSymbol(undef.flags(), undef.name,
-                                         types.memberType(c.type, undef), undef.owner);
-                    log.error(pos, "does.not.override.abstract",
-                              c, undef1, undef1.location());
-                }
-            }
-        } catch (CompletionFailure ex) {
-            completionError(pos, ex);
+        MethodSymbol undef = types.firstUnimplementedAbstract(c);
+        if (undef != null) {
+            MethodSymbol undef1 =
+                new MethodSymbol(undef.flags(), undef.name,
+                                 types.memberType(c.type, undef), undef.owner);
+            log.error(pos, "does.not.override.abstract",
+                      c, undef1, undef1.location());
         }
     }
-//where
-        /** Return first abstract member of class `c' that is not defined
-         *  in `impl', null if there is none.
-         */
-        private MethodSymbol firstUndef(ClassSymbol impl, ClassSymbol c) {
-            MethodSymbol undef = null;
-            // Do not bother to search in classes that are not abstract,
-            // since they cannot have abstract members.
-            if (c == impl || (c.flags() & (ABSTRACT | INTERFACE)) != 0) {
-                Scope s = c.members();
-                for (Symbol sym : s.getSymbols(NON_RECURSIVE)) {
-                    if (sym.kind == MTH &&
-                        (sym.flags() & (ABSTRACT|IPROXY|DEFAULT)) == ABSTRACT) {
-                        MethodSymbol absmeth = (MethodSymbol)sym;
-                        MethodSymbol implmeth = absmeth.implementation(impl, types, true);
-                        if (implmeth == null || implmeth == absmeth) {
-                            //look for default implementations
-                            if (allowDefaultMethods) {
-                                MethodSymbol prov = types.interfaceCandidates(impl.type, absmeth).head;
-                                if (prov != null && prov.overrides(absmeth, impl, types, true)) {
-                                    implmeth = prov;
-                                }
-                            }
-                        }
-                        if (implmeth == null || implmeth == absmeth) {
-                            undef = absmeth;
-                            break;
-                        }
-                    }
-                }
-                if (undef == null) {
-                    Type st = types.supertype(c.type);
-                    if (st.hasTag(CLASS))
-                        undef = firstUndef(impl, (ClassSymbol)st.tsym);
-                }
-                for (List<Type> l = types.interfaces(c.type);
-                     undef == null && l.nonEmpty();
-                     l = l.tail) {
-                    undef = firstUndef(impl, (ClassSymbol)l.head.tsym);
-                }
-            }
-            return undef;
-        }
 
     void checkNonCyclicDecl(JCClassDecl tree) {
         CycleChecker cc = new CycleChecker();
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Fri Jan 16 12:02:41 2015 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Fri Jan 16 14:47:25 2015 +0100
@@ -1000,6 +1000,7 @@
              l.nonEmpty();
              l = l.tail) {
             ClassSymbol inner = l.head;
+            inner.markAbstractIfNeeded(types);
             char flags = (char) adjustFlags(inner.flags_field);
             if ((flags & INTERFACE) != 0) flags |= ABSTRACT; // Interfaces are always ABSTRACT
             if (inner.name.isEmpty()) flags &= ~FINAL; // Anonymous class: unset FINAL flag
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/classfiles/InnerClasses/T8068517.java	Fri Jan 16 14:47:25 2015 +0100
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015, 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 8034854
+ *  @summary Verify that nested enums have correct abstract flag in the InnerClasses attribute.
+ *  @library /tools/lib
+ *  @build ToolBox T8068517
+ *  @run main T8068517
+ */
+
+import com.sun.tools.javac.util.Assert;
+import java.util.Arrays;
+import javax.tools.JavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+public class T8068517 {
+
+    public static void main(String[] args) throws Exception {
+        new T8068517().run();
+    }
+
+    void run() throws Exception {
+        runTest("class A {\n" +
+                "    enum AInner implements Runnable {\n" +
+                "        A {\n" +
+                "            public void run() {}\n" +
+                "        };\n" +
+                "    }\n" +
+                "}\n",
+                "class B {\n" +
+                "    A.AInner a;\n" +
+                "}");
+        runTest("class A {\n" +
+                "    enum AInner implements Runnable {\n" +
+                "        A {\n" +
+                "            public void run() {}\n" +
+                "        };\n" +
+                "    }\n" +
+                "    AInner aInner;\n" +
+                "}\n",
+                "class B {\n" +
+                "    void test(A a) {;\n" +
+                "        switch (a.aInner) {\n" +
+                "            case A: break;\n" +
+                "        }\n" +
+                "    };\n" +
+                "}");
+        runTest("class A {\n" +
+                "    enum AInner implements Runnable {\n" +
+                "        A {\n" +
+                "            public void run() {}\n" +
+                "        };\n" +
+                "    }\n" +
+                "    AInner aInner;\n" +
+                "}\n",
+                "class B {\n" +
+                "    void test(A a) {;\n" +
+                "        System.err.println(a.aInner.toString());\n" +
+                "    };\n" +
+                "}");
+        runTest("class A {\n" +
+                "    enum AInner implements Runnable {\n" +
+                "        A {\n" +
+                "            public void run() {}\n" +
+                "        };\n" +
+                "    }\n" +
+                "    AInner aInner() {\n" +
+                "        return null;\n" +
+                "    }\n" +
+                "}\n",
+                "class B {\n" +
+                "    void test(A a) {;\n" +
+                "        System.err.println(a.aInner().toString());\n" +
+                "    };\n" +
+                "}");
+    }
+
+    void runTest(String aJava, String bJava) throws Exception {
+        try (JavaFileManager fm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null)) {
+            ToolBox tb = new ToolBox();
+            ToolBox.MemoryFileManager memoryFM1 = new ToolBox.MemoryFileManager(fm);
+            tb.new JavacTask().fileManager(memoryFM1)
+                              .sources(aJava, bJava)
+                              .run();
+            ToolBox.MemoryFileManager memoryFM2 = new ToolBox.MemoryFileManager(fm);
+            tb.new JavacTask().fileManager(memoryFM2)
+                              .sources(bJava, aJava)
+                              .run();
+
+            Assert.check(Arrays.equals(memoryFM1.getFileBytes(StandardLocation.CLASS_OUTPUT, "B"),
+                                       memoryFM2.getFileBytes(StandardLocation.CLASS_OUTPUT, "B")));
+        }
+    }
+}