8068517: Compiler may generate wrong InnerClasses attribute for static enum reference

Fri, 13 Feb 2015 17:18:21 +0100

author
jlahoda
date
Fri, 13 Feb 2015 17:18:21 +0100
changeset 2717
11743872bfc9
parent 2713
e59ced856c92
child 2718
20bf47dc2a91

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

src/share/classes/com/sun/tools/javac/code/Symbol.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/code/Types.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Attr.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Check.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java file | annotate | diff | comparison | revisions
test/tools/javac/classfiles/InnerClasses/T8068517.java file | annotate | diff | comparison | revisions
     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 +}

mercurial