/*
* Copyright ( c ) 2011 , 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 7003595
* @ summary IncompatibleClassChangeError with unreferenced local class with subclass
* @ modules jdk . jdeps / com . sun . tools . classfile
* jdk . compiler / com . sun . tools . javac . api
* jdk . compiler / com . sun . tools . javac . file
*/
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 ***/
//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 {
// Create a single file manager and reuse it for each compile to save time.
try (StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null , null , null )) {
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(fm, new ClassKind[] {ck1, ck2, ck3}, new String[] { cname1, cname2, cname3 }).compileAndCheck();
}
}
}
}
System.out.println("Total checks made: " + checkCount);
}
/** instance decls **/
ClassKind[] cks;
String[] cnames;
StandardJavaFileManager fm;
T7003595(StandardJavaFileManager fm, ClassKind[] cks, String[] cnames) {
this .fm = fm;
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;
}
}
}
Messung V0.5 in Prozent C=91 H=91 G=90
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland