7003595: IncompatibleClassChangeError with unreferenced local class with subclass

Tue, 13 Sep 2011 14:15:22 +0100

author
mcimadamore
date
Tue, 13 Sep 2011 14:15:22 +0100
changeset 1086
f595d8bc0599
parent 1085
ed338593b0b6
child 1087
3a2200681d69

7003595: IncompatibleClassChangeError with unreferenced local class with subclass
Summary: Compiler omits unreferenced local inner classes from the InnerClasses attribute
Reviewed-by: jjg

src/share/classes/com/sun/tools/javac/code/Symbol.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Lower.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java file | annotate | diff | comparison | revisions
test/tools/javac/7003595/T7003595.java file | annotate | diff | comparison | revisions
test/tools/javac/7003595/T7003595b.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Sep 13 14:14:57 2011 +0100
     1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Sep 13 14:15:22 2011 +0100
     1.3 @@ -726,6 +726,11 @@
     1.4           */
     1.5          public JavaFileObject classfile;
     1.6  
     1.7 +        /** the list of translated local classes (used for generating
     1.8 +         * InnerClasses attribute)
     1.9 +         */
    1.10 +        public List<ClassSymbol> trans_local;
    1.11 +
    1.12          /** the constant pool of the class
    1.13           */
    1.14          public Pool pool;
     2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Sep 13 14:14:57 2011 +0100
     2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Sep 13 14:15:22 2011 +0100
     2.3 @@ -2271,6 +2271,14 @@
     2.4          tree.extending = translate(tree.extending);
     2.5          tree.implementing = translate(tree.implementing);
     2.6  
     2.7 +        if (currentClass.isLocal()) {
     2.8 +            ClassSymbol encl = currentClass.owner.enclClass();
     2.9 +            if (encl.trans_local == null) {
    2.10 +                encl.trans_local = List.nil();
    2.11 +            }
    2.12 +            encl.trans_local = encl.trans_local.prepend(currentClass);
    2.13 +        }
    2.14 +
    2.15          // Recursively translate members, taking into account that new members
    2.16          // might be created during the translation and prepended to the member
    2.17          // list `tree.defs'.
     3.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Tue Sep 13 14:14:57 2011 +0100
     3.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Tue Sep 13 14:15:22 2011 +0100
     3.3 @@ -863,10 +863,10 @@
     3.4          }
     3.5          if (c.type.tag != CLASS) return; // arrays
     3.6          if (pool != null && // pool might be null if called from xClassName
     3.7 -            c.owner.kind != PCK &&
     3.8 +            c.owner.enclClass() != null &&
     3.9              (innerClasses == null || !innerClasses.contains(c))) {
    3.10  //          log.errWriter.println("enter inner " + c);//DEBUG
    3.11 -            if (c.owner.kind == TYP) enterInner((ClassSymbol)c.owner);
    3.12 +            enterInner(c.owner.enclClass());
    3.13              pool.put(c);
    3.14              pool.put(c.name);
    3.15              if (innerClasses == null) {
    3.16 @@ -1505,6 +1505,13 @@
    3.17              default : Assert.error();
    3.18              }
    3.19          }
    3.20 +
    3.21 +        if (c.trans_local != null) {
    3.22 +            for (ClassSymbol local : c.trans_local) {
    3.23 +                enterInner(local);
    3.24 +            }
    3.25 +        }
    3.26 +
    3.27          databuf.appendChar(fieldsCount);
    3.28          writeFields(c.members().elems);
    3.29          databuf.appendChar(methodsCount);
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/test/tools/javac/7003595/T7003595.java	Tue Sep 13 14:15:22 2011 +0100
     4.3 @@ -0,0 +1,233 @@
     4.4 +/*
     4.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
     4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4.7 + *
     4.8 + * This code is free software; you can redistribute it and/or modify it
     4.9 + * under the terms of the GNU General Public License version 2 only, as
    4.10 + * published by the Free Software Foundation.
    4.11 + *
    4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    4.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    4.15 + * version 2 for more details (a copy is included in the LICENSE file that
    4.16 + * accompanied this code).
    4.17 + *
    4.18 + * You should have received a copy of the GNU General Public License version
    4.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    4.21 + *
    4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    4.23 + * or visit www.oracle.com if you need additional information or have any
    4.24 + * questions.
    4.25 + */
    4.26 +
    4.27 +/*
    4.28 + * @test
    4.29 + * @bug 7003595
    4.30 + * @summary IncompatibleClassChangeError with unreferenced local class with subclass
    4.31 + */
    4.32 +
    4.33 +import com.sun.source.util.JavacTask;
    4.34 +import com.sun.tools.classfile.Attribute;
    4.35 +import com.sun.tools.classfile.ClassFile;
    4.36 +import com.sun.tools.classfile.InnerClasses_attribute;
    4.37 +import com.sun.tools.classfile.ConstantPool.*;
    4.38 +import com.sun.tools.javac.api.JavacTool;
    4.39 +
    4.40 +import java.io.File;
    4.41 +import java.net.URI;
    4.42 +import java.util.Arrays;
    4.43 +import java.util.ArrayList;
    4.44 +import javax.tools.JavaCompiler;
    4.45 +import javax.tools.JavaFileObject;
    4.46 +import javax.tools.SimpleJavaFileObject;
    4.47 +import javax.tools.StandardJavaFileManager;
    4.48 +import javax.tools.ToolProvider;
    4.49 +
    4.50 +
    4.51 +public class T7003595 {
    4.52 +
    4.53 +    /** global decls ***/
    4.54 +
    4.55 +    // Create a single file manager and reuse it for each compile to save time.
    4.56 +    static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
    4.57 +
    4.58 +    //statistics
    4.59 +    static int checkCount = 0;
    4.60 +
    4.61 +    enum ClassKind {
    4.62 +        NESTED("static class #N { #B }", "$", true),
    4.63 +        INNER("class #N { #B }", "$", false),
    4.64 +        LOCAL_REF("void test() { class #N { #B }; new #N(); }", "$1", false),
    4.65 +        LOCAL_NOREF("void test() { class #N { #B }; }", "$1", false),
    4.66 +        ANON("void test() { new Object() { #B }; }", "$1", false),
    4.67 +        NONE("", "", false);
    4.68 +
    4.69 +        String memberInnerStr;
    4.70 +        String sep;
    4.71 +        boolean staticAllowed;
    4.72 +
    4.73 +        private ClassKind(String memberInnerStr, String sep, boolean staticAllowed) {
    4.74 +            this.memberInnerStr = memberInnerStr;
    4.75 +            this.sep = sep;
    4.76 +            this.staticAllowed = staticAllowed;
    4.77 +        }
    4.78 +
    4.79 +        String getSource(String className, String outerName, String nested) {
    4.80 +            return memberInnerStr.replaceAll("#O", outerName).
    4.81 +                    replaceAll("#N", className).replaceAll("#B", nested);
    4.82 +        }
    4.83 +
    4.84 +        static String getClassfileName(String[] names, ClassKind[] outerKinds, int pos) {
    4.85 +            System.out.println(" pos = " + pos + " kind = " + outerKinds[pos] + " sep = " + outerKinds[pos].sep);
    4.86 +            String name = outerKinds[pos] != ANON ?
    4.87 +                    names[pos] : "";
    4.88 +            if (pos == 0) {
    4.89 +                return "Test" + outerKinds[pos].sep + name;
    4.90 +            } else {
    4.91 +                String outerStr = getClassfileName(names, outerKinds, pos - 1);
    4.92 +                return outerStr + outerKinds[pos].sep + name;
    4.93 +            }
    4.94 +        }
    4.95 +
    4.96 +        boolean isAllowed(ClassKind nestedKind) {
    4.97 +            return nestedKind != NESTED ||
    4.98 +                    staticAllowed;
    4.99 +        }
   4.100 +    }
   4.101 +
   4.102 +    enum LocalInnerClass {
   4.103 +        LOCAL_REF("class L {}; new L();", "Test$1L"),
   4.104 +        LOCAL_NOREF("class L {};", "Test$1L"),
   4.105 +        ANON("new Object() {};", "Test$1"),
   4.106 +        NONE("", "");
   4.107 +
   4.108 +        String localInnerStr;
   4.109 +        String canonicalInnerStr;
   4.110 +
   4.111 +        private LocalInnerClass(String localInnerStr, String canonicalInnerStr) {
   4.112 +            this.localInnerStr = localInnerStr;
   4.113 +            this.canonicalInnerStr = canonicalInnerStr;
   4.114 +        }
   4.115 +    }
   4.116 +
   4.117 +    public static void main(String... args) throws Exception {
   4.118 +        for (ClassKind ck1 : ClassKind.values()) {
   4.119 +            String cname1 = "C1";
   4.120 +            for (ClassKind ck2 : ClassKind.values()) {
   4.121 +                if (!ck1.isAllowed(ck2)) continue;
   4.122 +                String cname2 = "C2";
   4.123 +                for (ClassKind ck3 : ClassKind.values()) {
   4.124 +                    if (!ck2.isAllowed(ck3)) continue;
   4.125 +                    String cname3 = "C3";
   4.126 +                    new T7003595(new ClassKind[] {ck1, ck2, ck3}, new String[] { cname1, cname2, cname3 }).compileAndCheck();
   4.127 +                }
   4.128 +            }
   4.129 +        }
   4.130 +
   4.131 +        System.out.println("Total checks made: " + checkCount);
   4.132 +    }
   4.133 +
   4.134 +    /** instance decls **/
   4.135 +
   4.136 +    ClassKind[] cks;
   4.137 +    String[] cnames;
   4.138 +
   4.139 +    T7003595(ClassKind[] cks, String[] cnames) {
   4.140 +        this.cks = cks;
   4.141 +        this.cnames = cnames;
   4.142 +    }
   4.143 +
   4.144 +    void compileAndCheck() throws Exception {
   4.145 +        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
   4.146 +        JavaSource source = new JavaSource();
   4.147 +        JavacTask ct = (JavacTask)tool.getTask(null, fm, null,
   4.148 +                null, null, Arrays.asList(source));
   4.149 +        ct.call();
   4.150 +        verifyBytecode(source);
   4.151 +    }
   4.152 +
   4.153 +    void verifyBytecode(JavaSource source) {
   4.154 +        for (int i = 0; i < 3 ; i ++) {
   4.155 +            if (cks[i] == ClassKind.NONE) break;
   4.156 +            checkCount++;
   4.157 +            String filename = cks[i].getClassfileName(cnames, cks, i);
   4.158 +            File compiledTest = new File(filename + ".class");
   4.159 +            try {
   4.160 +                ClassFile cf = ClassFile.read(compiledTest);
   4.161 +                if (cf == null) {
   4.162 +                    throw new Error("Classfile not found: " + filename);
   4.163 +                }
   4.164 +
   4.165 +                InnerClasses_attribute innerClasses = (InnerClasses_attribute)cf.getAttribute(Attribute.InnerClasses);
   4.166 +
   4.167 +                ArrayList<String> foundInnerSig = new ArrayList<>();
   4.168 +                if (innerClasses != null) {
   4.169 +                    for (InnerClasses_attribute.Info info : innerClasses.classes) {
   4.170 +                        String foundSig = info.getInnerClassInfo(cf.constant_pool).getName();
   4.171 +                        foundInnerSig.add(foundSig);
   4.172 +                    }
   4.173 +                }
   4.174 +
   4.175 +                ArrayList<String> expectedInnerSig = new ArrayList<>();
   4.176 +                //add inner class (if any)
   4.177 +                if (i < 2 && cks[i + 1] != ClassKind.NONE) {
   4.178 +                    expectedInnerSig.add(cks[i + 1].getClassfileName(cnames, cks, i + 1));
   4.179 +                }
   4.180 +                //add inner classes
   4.181 +                for (int j = 0 ; j != i + 1 && j < 3; j++) {
   4.182 +                    expectedInnerSig.add(cks[j].getClassfileName(cnames, cks, j));
   4.183 +                }
   4.184 +
   4.185 +                if (expectedInnerSig.size() != foundInnerSig.size()) {
   4.186 +                    throw new Error("InnerClasses attribute for " + cnames[i] + " has wrong size\n" +
   4.187 +                                    "expected " + expectedInnerSig.size() + "\n" +
   4.188 +                                    "found " + innerClasses.number_of_classes + "\n" +
   4.189 +                                    source);
   4.190 +                }
   4.191 +
   4.192 +                for (String foundSig : foundInnerSig) {
   4.193 +                    if (!expectedInnerSig.contains(foundSig)) {
   4.194 +                        throw new Error("InnerClasses attribute for " + cnames[i] + " has unexpected signature: " +
   4.195 +                                foundSig + "\n" + source + "\n" + expectedInnerSig);
   4.196 +                    }
   4.197 +                }
   4.198 +
   4.199 +                for (String expectedSig : expectedInnerSig) {
   4.200 +                    if (!foundInnerSig.contains(expectedSig)) {
   4.201 +                        throw new Error("InnerClasses attribute for " + cnames[i] + " does not contain expected signature: " +
   4.202 +                                    expectedSig + "\n" + source);
   4.203 +                    }
   4.204 +                }
   4.205 +            } catch (Exception e) {
   4.206 +                e.printStackTrace();
   4.207 +                throw new Error("error reading " + compiledTest +": " + e);
   4.208 +            }
   4.209 +        }
   4.210 +    }
   4.211 +
   4.212 +    class JavaSource extends SimpleJavaFileObject {
   4.213 +
   4.214 +        static final String source_template = "class Test { #C }";
   4.215 +
   4.216 +        String source;
   4.217 +
   4.218 +        public JavaSource() {
   4.219 +            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   4.220 +            String c3 = cks[2].getSource(cnames[2], cnames[1], "");
   4.221 +            String c2 = cks[1].getSource(cnames[1], cnames[0], c3);
   4.222 +            String c1 = cks[0].getSource(cnames[0], "Test", c2);
   4.223 +            source = source_template.replace("#C", c1);
   4.224 +        }
   4.225 +
   4.226 +        @Override
   4.227 +        public String toString() {
   4.228 +            return source;
   4.229 +        }
   4.230 +
   4.231 +        @Override
   4.232 +        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   4.233 +            return source;
   4.234 +        }
   4.235 +    }
   4.236 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/test/tools/javac/7003595/T7003595b.java	Tue Sep 13 14:15:22 2011 +0100
     5.3 @@ -0,0 +1,36 @@
     5.4 +/*
     5.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
     5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     5.7 + *
     5.8 + * This code is free software; you can redistribute it and/or modify it
     5.9 + * under the terms of the GNU General Public License version 2 only, as
    5.10 + * published by the Free Software Foundation.
    5.11 + *
    5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    5.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    5.15 + * version 2 for more details (a copy is included in the LICENSE file that
    5.16 + * accompanied this code).
    5.17 + *
    5.18 + * You should have received a copy of the GNU General Public License version
    5.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    5.21 + *
    5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    5.23 + * or visit www.oracle.com if you need additional information or have any
    5.24 + * questions.
    5.25 + */
    5.26 +
    5.27 +/*
    5.28 + * @test
    5.29 + * @bug 7003595
    5.30 + * @summary IncompatibleClassChangeError with unreferenced local class with subclass
    5.31 + */
    5.32 +
    5.33 +public class T7003595b {
    5.34 +    public static void main(String... args) throws Exception {
    5.35 +        class A {}
    5.36 +        class B extends A {}
    5.37 +        B.class.getSuperclass().getDeclaringClass();
    5.38 +    }
    5.39 +}

mercurial