mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 11:04:34 +02:00
7003595: IncompatibleClassChangeError with unreferenced local class with subclass
Compiler omits unreferenced local inner classes from the InnerClasses attribute Reviewed-by: jjg
This commit is contained in:
parent
8db2f8de59
commit
0208d38b3c
5 changed files with 291 additions and 2 deletions
|
@ -726,6 +726,11 @@ public abstract class Symbol implements Element {
|
||||||
*/
|
*/
|
||||||
public JavaFileObject classfile;
|
public JavaFileObject classfile;
|
||||||
|
|
||||||
|
/** the list of translated local classes (used for generating
|
||||||
|
* InnerClasses attribute)
|
||||||
|
*/
|
||||||
|
public List<ClassSymbol> trans_local;
|
||||||
|
|
||||||
/** the constant pool of the class
|
/** the constant pool of the class
|
||||||
*/
|
*/
|
||||||
public Pool pool;
|
public Pool pool;
|
||||||
|
|
|
@ -2271,6 +2271,14 @@ public class Lower extends TreeTranslator {
|
||||||
tree.extending = translate(tree.extending);
|
tree.extending = translate(tree.extending);
|
||||||
tree.implementing = translate(tree.implementing);
|
tree.implementing = translate(tree.implementing);
|
||||||
|
|
||||||
|
if (currentClass.isLocal()) {
|
||||||
|
ClassSymbol encl = currentClass.owner.enclClass();
|
||||||
|
if (encl.trans_local == null) {
|
||||||
|
encl.trans_local = List.nil();
|
||||||
|
}
|
||||||
|
encl.trans_local = encl.trans_local.prepend(currentClass);
|
||||||
|
}
|
||||||
|
|
||||||
// Recursively translate members, taking into account that new members
|
// Recursively translate members, taking into account that new members
|
||||||
// might be created during the translation and prepended to the member
|
// might be created during the translation and prepended to the member
|
||||||
// list `tree.defs'.
|
// list `tree.defs'.
|
||||||
|
|
|
@ -863,10 +863,10 @@ public class ClassWriter extends ClassFile {
|
||||||
}
|
}
|
||||||
if (c.type.tag != CLASS) return; // arrays
|
if (c.type.tag != CLASS) return; // arrays
|
||||||
if (pool != null && // pool might be null if called from xClassName
|
if (pool != null && // pool might be null if called from xClassName
|
||||||
c.owner.kind != PCK &&
|
c.owner.enclClass() != null &&
|
||||||
(innerClasses == null || !innerClasses.contains(c))) {
|
(innerClasses == null || !innerClasses.contains(c))) {
|
||||||
// log.errWriter.println("enter inner " + c);//DEBUG
|
// log.errWriter.println("enter inner " + c);//DEBUG
|
||||||
if (c.owner.kind == TYP) enterInner((ClassSymbol)c.owner);
|
enterInner(c.owner.enclClass());
|
||||||
pool.put(c);
|
pool.put(c);
|
||||||
pool.put(c.name);
|
pool.put(c.name);
|
||||||
if (innerClasses == null) {
|
if (innerClasses == null) {
|
||||||
|
@ -1505,6 +1505,13 @@ public class ClassWriter extends ClassFile {
|
||||||
default : Assert.error();
|
default : Assert.error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c.trans_local != null) {
|
||||||
|
for (ClassSymbol local : c.trans_local) {
|
||||||
|
enterInner(local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
databuf.appendChar(fieldsCount);
|
databuf.appendChar(fieldsCount);
|
||||||
writeFields(c.members().elems);
|
writeFields(c.members().elems);
|
||||||
databuf.appendChar(methodsCount);
|
databuf.appendChar(methodsCount);
|
||||||
|
|
233
langtools/test/tools/javac/7003595/T7003595.java
Normal file
233
langtools/test/tools/javac/7003595/T7003595.java
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, 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 7003595
|
||||||
|
* @summary IncompatibleClassChangeError with unreferenced local class with subclass
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.sun.source.util.JavacTask;
|
||||||
|
import com.sun.tools.classfile.Attribute;
|
||||||
|
import com.sun.tools.classfile.ClassFile;
|
||||||
|
import com.sun.tools.classfile.InnerClasses_attribute;
|
||||||
|
import com.sun.tools.classfile.ConstantPool.*;
|
||||||
|
import com.sun.tools.javac.api.JavacTool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
|
import javax.tools.JavaFileObject;
|
||||||
|
import javax.tools.SimpleJavaFileObject;
|
||||||
|
import javax.tools.StandardJavaFileManager;
|
||||||
|
import javax.tools.ToolProvider;
|
||||||
|
|
||||||
|
|
||||||
|
public class T7003595 {
|
||||||
|
|
||||||
|
/** global decls ***/
|
||||||
|
|
||||||
|
// Create a single file manager and reuse it for each compile to save time.
|
||||||
|
static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
|
||||||
|
|
||||||
|
//statistics
|
||||||
|
static int checkCount = 0;
|
||||||
|
|
||||||
|
enum ClassKind {
|
||||||
|
NESTED("static class #N { #B }", "$", true),
|
||||||
|
INNER("class #N { #B }", "$", false),
|
||||||
|
LOCAL_REF("void test() { class #N { #B }; new #N(); }", "$1", false),
|
||||||
|
LOCAL_NOREF("void test() { class #N { #B }; }", "$1", false),
|
||||||
|
ANON("void test() { new Object() { #B }; }", "$1", false),
|
||||||
|
NONE("", "", false);
|
||||||
|
|
||||||
|
String memberInnerStr;
|
||||||
|
String sep;
|
||||||
|
boolean staticAllowed;
|
||||||
|
|
||||||
|
private ClassKind(String memberInnerStr, String sep, boolean staticAllowed) {
|
||||||
|
this.memberInnerStr = memberInnerStr;
|
||||||
|
this.sep = sep;
|
||||||
|
this.staticAllowed = staticAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSource(String className, String outerName, String nested) {
|
||||||
|
return memberInnerStr.replaceAll("#O", outerName).
|
||||||
|
replaceAll("#N", className).replaceAll("#B", nested);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getClassfileName(String[] names, ClassKind[] outerKinds, int pos) {
|
||||||
|
System.out.println(" pos = " + pos + " kind = " + outerKinds[pos] + " sep = " + outerKinds[pos].sep);
|
||||||
|
String name = outerKinds[pos] != ANON ?
|
||||||
|
names[pos] : "";
|
||||||
|
if (pos == 0) {
|
||||||
|
return "Test" + outerKinds[pos].sep + name;
|
||||||
|
} else {
|
||||||
|
String outerStr = getClassfileName(names, outerKinds, pos - 1);
|
||||||
|
return outerStr + outerKinds[pos].sep + name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAllowed(ClassKind nestedKind) {
|
||||||
|
return nestedKind != NESTED ||
|
||||||
|
staticAllowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LocalInnerClass {
|
||||||
|
LOCAL_REF("class L {}; new L();", "Test$1L"),
|
||||||
|
LOCAL_NOREF("class L {};", "Test$1L"),
|
||||||
|
ANON("new Object() {};", "Test$1"),
|
||||||
|
NONE("", "");
|
||||||
|
|
||||||
|
String localInnerStr;
|
||||||
|
String canonicalInnerStr;
|
||||||
|
|
||||||
|
private LocalInnerClass(String localInnerStr, String canonicalInnerStr) {
|
||||||
|
this.localInnerStr = localInnerStr;
|
||||||
|
this.canonicalInnerStr = canonicalInnerStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String... args) throws Exception {
|
||||||
|
for (ClassKind ck1 : ClassKind.values()) {
|
||||||
|
String cname1 = "C1";
|
||||||
|
for (ClassKind ck2 : ClassKind.values()) {
|
||||||
|
if (!ck1.isAllowed(ck2)) continue;
|
||||||
|
String cname2 = "C2";
|
||||||
|
for (ClassKind ck3 : ClassKind.values()) {
|
||||||
|
if (!ck2.isAllowed(ck3)) continue;
|
||||||
|
String cname3 = "C3";
|
||||||
|
new T7003595(new ClassKind[] {ck1, ck2, ck3}, new String[] { cname1, cname2, cname3 }).compileAndCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Total checks made: " + checkCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** instance decls **/
|
||||||
|
|
||||||
|
ClassKind[] cks;
|
||||||
|
String[] cnames;
|
||||||
|
|
||||||
|
T7003595(ClassKind[] cks, String[] cnames) {
|
||||||
|
this.cks = cks;
|
||||||
|
this.cnames = cnames;
|
||||||
|
}
|
||||||
|
|
||||||
|
void compileAndCheck() throws Exception {
|
||||||
|
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
|
||||||
|
JavaSource source = new JavaSource();
|
||||||
|
JavacTask ct = (JavacTask)tool.getTask(null, fm, null,
|
||||||
|
null, null, Arrays.asList(source));
|
||||||
|
ct.call();
|
||||||
|
verifyBytecode(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifyBytecode(JavaSource source) {
|
||||||
|
for (int i = 0; i < 3 ; i ++) {
|
||||||
|
if (cks[i] == ClassKind.NONE) break;
|
||||||
|
checkCount++;
|
||||||
|
String filename = cks[i].getClassfileName(cnames, cks, i);
|
||||||
|
File compiledTest = new File(filename + ".class");
|
||||||
|
try {
|
||||||
|
ClassFile cf = ClassFile.read(compiledTest);
|
||||||
|
if (cf == null) {
|
||||||
|
throw new Error("Classfile not found: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
InnerClasses_attribute innerClasses = (InnerClasses_attribute)cf.getAttribute(Attribute.InnerClasses);
|
||||||
|
|
||||||
|
ArrayList<String> foundInnerSig = new ArrayList<>();
|
||||||
|
if (innerClasses != null) {
|
||||||
|
for (InnerClasses_attribute.Info info : innerClasses.classes) {
|
||||||
|
String foundSig = info.getInnerClassInfo(cf.constant_pool).getName();
|
||||||
|
foundInnerSig.add(foundSig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<String> expectedInnerSig = new ArrayList<>();
|
||||||
|
//add inner class (if any)
|
||||||
|
if (i < 2 && cks[i + 1] != ClassKind.NONE) {
|
||||||
|
expectedInnerSig.add(cks[i + 1].getClassfileName(cnames, cks, i + 1));
|
||||||
|
}
|
||||||
|
//add inner classes
|
||||||
|
for (int j = 0 ; j != i + 1 && j < 3; j++) {
|
||||||
|
expectedInnerSig.add(cks[j].getClassfileName(cnames, cks, j));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedInnerSig.size() != foundInnerSig.size()) {
|
||||||
|
throw new Error("InnerClasses attribute for " + cnames[i] + " has wrong size\n" +
|
||||||
|
"expected " + expectedInnerSig.size() + "\n" +
|
||||||
|
"found " + innerClasses.number_of_classes + "\n" +
|
||||||
|
source);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String foundSig : foundInnerSig) {
|
||||||
|
if (!expectedInnerSig.contains(foundSig)) {
|
||||||
|
throw new Error("InnerClasses attribute for " + cnames[i] + " has unexpected signature: " +
|
||||||
|
foundSig + "\n" + source + "\n" + expectedInnerSig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String expectedSig : expectedInnerSig) {
|
||||||
|
if (!foundInnerSig.contains(expectedSig)) {
|
||||||
|
throw new Error("InnerClasses attribute for " + cnames[i] + " does not contain expected signature: " +
|
||||||
|
expectedSig + "\n" + source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new Error("error reading " + compiledTest +": " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JavaSource extends SimpleJavaFileObject {
|
||||||
|
|
||||||
|
static final String source_template = "class Test { #C }";
|
||||||
|
|
||||||
|
String source;
|
||||||
|
|
||||||
|
public JavaSource() {
|
||||||
|
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
|
||||||
|
String c3 = cks[2].getSource(cnames[2], cnames[1], "");
|
||||||
|
String c2 = cks[1].getSource(cnames[1], cnames[0], c3);
|
||||||
|
String c1 = cks[0].getSource(cnames[0], "Test", c2);
|
||||||
|
source = source_template.replace("#C", c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
langtools/test/tools/javac/7003595/T7003595b.java
Normal file
36
langtools/test/tools/javac/7003595/T7003595b.java
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, 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 7003595
|
||||||
|
* @summary IncompatibleClassChangeError with unreferenced local class with subclass
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class T7003595b {
|
||||||
|
public static void main(String... args) throws Exception {
|
||||||
|
class A {}
|
||||||
|
class B extends A {}
|
||||||
|
B.class.getSuperclass().getDeclaringClass();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue