1124N/A/*
1124N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
1124N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1124N/A *
1124N/A * This code is free software; you can redistribute it and/or modify it
1124N/A * under the terms of the GNU General Public License version 2 only, as
1124N/A * published by the Free Software Foundation.
1124N/A *
1124N/A * This code is distributed in the hope that it will be useful, but WITHOUT
1124N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1124N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1124N/A * version 2 for more details (a copy is included in the LICENSE file that
1124N/A * accompanied this code).
1124N/A *
1124N/A * You should have received a copy of the GNU General Public License version
1124N/A * 2 along with this work; if not, write to the Free Software Foundation,
1124N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1124N/A *
1124N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1124N/A * or visit www.oracle.com if you need additional information or have any
1124N/A * questions.
1124N/A */
1124N/A
1124N/A/*
1124N/A * @test
1124N/A * @bug 7003595
1124N/A * @summary IncompatibleClassChangeError with unreferenced local class with subclass
1124N/A */
1124N/A
1124N/Aimport com.sun.source.util.JavacTask;
1124N/Aimport com.sun.tools.classfile.Attribute;
1124N/Aimport com.sun.tools.classfile.ClassFile;
1124N/Aimport com.sun.tools.classfile.InnerClasses_attribute;
1124N/Aimport com.sun.tools.classfile.ConstantPool.*;
1124N/Aimport com.sun.tools.javac.api.JavacTool;
1124N/A
1124N/Aimport java.io.File;
1124N/Aimport java.net.URI;
1124N/Aimport java.util.Arrays;
1124N/Aimport java.util.ArrayList;
1124N/Aimport javax.tools.JavaCompiler;
1124N/Aimport javax.tools.JavaFileObject;
1124N/Aimport javax.tools.SimpleJavaFileObject;
1124N/Aimport javax.tools.StandardJavaFileManager;
1124N/Aimport javax.tools.ToolProvider;
1124N/A
1124N/A
1124N/Apublic class T7003595 {
1124N/A
1124N/A /** global decls ***/
1124N/A
1124N/A // Create a single file manager and reuse it for each compile to save time.
1124N/A static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
1124N/A
1124N/A //statistics
1124N/A static int checkCount = 0;
1124N/A
1124N/A enum ClassKind {
1124N/A NESTED("static class #N { #B }", "$", true),
1124N/A INNER("class #N { #B }", "$", false),
1124N/A LOCAL_REF("void test() { class #N { #B }; new #N(); }", "$1", false),
1124N/A LOCAL_NOREF("void test() { class #N { #B }; }", "$1", false),
1124N/A ANON("void test() { new Object() { #B }; }", "$1", false),
1124N/A NONE("", "", false);
1124N/A
1124N/A String memberInnerStr;
1124N/A String sep;
1124N/A boolean staticAllowed;
1124N/A
1124N/A private ClassKind(String memberInnerStr, String sep, boolean staticAllowed) {
1124N/A this.memberInnerStr = memberInnerStr;
1124N/A this.sep = sep;
1124N/A this.staticAllowed = staticAllowed;
1124N/A }
1124N/A
1124N/A String getSource(String className, String outerName, String nested) {
1124N/A return memberInnerStr.replaceAll("#O", outerName).
1124N/A replaceAll("#N", className).replaceAll("#B", nested);
1124N/A }
1124N/A
1124N/A static String getClassfileName(String[] names, ClassKind[] outerKinds, int pos) {
1124N/A System.out.println(" pos = " + pos + " kind = " + outerKinds[pos] + " sep = " + outerKinds[pos].sep);
1124N/A String name = outerKinds[pos] != ANON ?
1124N/A names[pos] : "";
1124N/A if (pos == 0) {
1124N/A return "Test" + outerKinds[pos].sep + name;
1124N/A } else {
1124N/A String outerStr = getClassfileName(names, outerKinds, pos - 1);
1124N/A return outerStr + outerKinds[pos].sep + name;
1124N/A }
1124N/A }
1124N/A
1124N/A boolean isAllowed(ClassKind nestedKind) {
1124N/A return nestedKind != NESTED ||
1124N/A staticAllowed;
1124N/A }
1124N/A }
1124N/A
1124N/A enum LocalInnerClass {
1124N/A LOCAL_REF("class L {}; new L();", "Test$1L"),
1124N/A LOCAL_NOREF("class L {};", "Test$1L"),
1124N/A ANON("new Object() {};", "Test$1"),
1124N/A NONE("", "");
1124N/A
1124N/A String localInnerStr;
1124N/A String canonicalInnerStr;
1124N/A
1124N/A private LocalInnerClass(String localInnerStr, String canonicalInnerStr) {
1124N/A this.localInnerStr = localInnerStr;
1124N/A this.canonicalInnerStr = canonicalInnerStr;
1124N/A }
1124N/A }
1124N/A
1124N/A public static void main(String... args) throws Exception {
1124N/A for (ClassKind ck1 : ClassKind.values()) {
1124N/A String cname1 = "C1";
1124N/A for (ClassKind ck2 : ClassKind.values()) {
1124N/A if (!ck1.isAllowed(ck2)) continue;
1124N/A String cname2 = "C2";
1124N/A for (ClassKind ck3 : ClassKind.values()) {
1124N/A if (!ck2.isAllowed(ck3)) continue;
1124N/A String cname3 = "C3";
1124N/A new T7003595(new ClassKind[] {ck1, ck2, ck3}, new String[] { cname1, cname2, cname3 }).compileAndCheck();
1124N/A }
1124N/A }
1124N/A }
1124N/A
1124N/A System.out.println("Total checks made: " + checkCount);
1124N/A }
1124N/A
1124N/A /** instance decls **/
1124N/A
1124N/A ClassKind[] cks;
1124N/A String[] cnames;
1124N/A
1124N/A T7003595(ClassKind[] cks, String[] cnames) {
1124N/A this.cks = cks;
1124N/A this.cnames = cnames;
1124N/A }
1124N/A
1124N/A void compileAndCheck() throws Exception {
1124N/A final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
1124N/A JavaSource source = new JavaSource();
1124N/A JavacTask ct = (JavacTask)tool.getTask(null, fm, null,
1124N/A null, null, Arrays.asList(source));
1124N/A ct.call();
1124N/A verifyBytecode(source);
1124N/A }
1124N/A
1124N/A void verifyBytecode(JavaSource source) {
1124N/A for (int i = 0; i < 3 ; i ++) {
1124N/A if (cks[i] == ClassKind.NONE) break;
1124N/A checkCount++;
1124N/A String filename = cks[i].getClassfileName(cnames, cks, i);
1124N/A File compiledTest = new File(filename + ".class");
1124N/A try {
1124N/A ClassFile cf = ClassFile.read(compiledTest);
1124N/A if (cf == null) {
1124N/A throw new Error("Classfile not found: " + filename);
1124N/A }
1124N/A
1124N/A InnerClasses_attribute innerClasses = (InnerClasses_attribute)cf.getAttribute(Attribute.InnerClasses);
1124N/A
1124N/A ArrayList<String> foundInnerSig = new ArrayList<>();
1124N/A if (innerClasses != null) {
1124N/A for (InnerClasses_attribute.Info info : innerClasses.classes) {
1124N/A String foundSig = info.getInnerClassInfo(cf.constant_pool).getName();
1124N/A foundInnerSig.add(foundSig);
1124N/A }
1124N/A }
1124N/A
1124N/A ArrayList<String> expectedInnerSig = new ArrayList<>();
1124N/A //add inner class (if any)
1124N/A if (i < 2 && cks[i + 1] != ClassKind.NONE) {
1124N/A expectedInnerSig.add(cks[i + 1].getClassfileName(cnames, cks, i + 1));
1124N/A }
1124N/A //add inner classes
1124N/A for (int j = 0 ; j != i + 1 && j < 3; j++) {
1124N/A expectedInnerSig.add(cks[j].getClassfileName(cnames, cks, j));
1124N/A }
1124N/A
1124N/A if (expectedInnerSig.size() != foundInnerSig.size()) {
1124N/A throw new Error("InnerClasses attribute for " + cnames[i] + " has wrong size\n" +
1124N/A "expected " + expectedInnerSig.size() + "\n" +
1124N/A "found " + innerClasses.number_of_classes + "\n" +
1124N/A source);
1124N/A }
1124N/A
1124N/A for (String foundSig : foundInnerSig) {
1124N/A if (!expectedInnerSig.contains(foundSig)) {
1124N/A throw new Error("InnerClasses attribute for " + cnames[i] + " has unexpected signature: " +
1124N/A foundSig + "\n" + source + "\n" + expectedInnerSig);
1124N/A }
1124N/A }
1124N/A
1124N/A for (String expectedSig : expectedInnerSig) {
1124N/A if (!foundInnerSig.contains(expectedSig)) {
1124N/A throw new Error("InnerClasses attribute for " + cnames[i] + " does not contain expected signature: " +
1124N/A expectedSig + "\n" + source);
1124N/A }
1124N/A }
1124N/A } catch (Exception e) {
1124N/A e.printStackTrace();
1124N/A throw new Error("error reading " + compiledTest +": " + e);
1124N/A }
1124N/A }
1124N/A }
1124N/A
1124N/A class JavaSource extends SimpleJavaFileObject {
1124N/A
1124N/A static final String source_template = "class Test { #C }";
1124N/A
1124N/A String source;
1124N/A
1124N/A public JavaSource() {
1124N/A super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
1124N/A String c3 = cks[2].getSource(cnames[2], cnames[1], "");
1124N/A String c2 = cks[1].getSource(cnames[1], cnames[0], c3);
1124N/A String c1 = cks[0].getSource(cnames[0], "Test", c2);
1124N/A source = source_template.replace("#C", c1);
1124N/A }
1124N/A
1124N/A @Override
1124N/A public String toString() {
1124N/A return source;
1124N/A }
1124N/A
1124N/A @Override
1124N/A public CharSequence getCharContent(boolean ignoreEncodingErrors) {
1124N/A return source;
1124N/A }
1124N/A }
1124N/A}