test/tools/javac/7003595/T7003595.java

changeset 0
959103a6100f
equal deleted inserted replaced
-1:000000000000 0:959103a6100f
1 /*
2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /*
25 * @test
26 * @bug 7003595
27 * @summary IncompatibleClassChangeError with unreferenced local class with subclass
28 */
29
30 import com.sun.source.util.JavacTask;
31 import com.sun.tools.classfile.Attribute;
32 import com.sun.tools.classfile.ClassFile;
33 import com.sun.tools.classfile.InnerClasses_attribute;
34 import com.sun.tools.classfile.ConstantPool.*;
35 import com.sun.tools.javac.api.JavacTool;
36
37 import java.io.File;
38 import java.net.URI;
39 import java.util.Arrays;
40 import java.util.ArrayList;
41 import javax.tools.JavaCompiler;
42 import javax.tools.JavaFileObject;
43 import javax.tools.SimpleJavaFileObject;
44 import javax.tools.StandardJavaFileManager;
45 import javax.tools.ToolProvider;
46
47
48 public class T7003595 {
49
50 /** global decls ***/
51
52 // Create a single file manager and reuse it for each compile to save time.
53 static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
54
55 //statistics
56 static int checkCount = 0;
57
58 enum ClassKind {
59 NESTED("static class #N { #B }", "$", true),
60 INNER("class #N { #B }", "$", false),
61 LOCAL_REF("void test() { class #N { #B }; new #N(); }", "$1", false),
62 LOCAL_NOREF("void test() { class #N { #B }; }", "$1", false),
63 ANON("void test() { new Object() { #B }; }", "$1", false),
64 NONE("", "", false);
65
66 String memberInnerStr;
67 String sep;
68 boolean staticAllowed;
69
70 private ClassKind(String memberInnerStr, String sep, boolean staticAllowed) {
71 this.memberInnerStr = memberInnerStr;
72 this.sep = sep;
73 this.staticAllowed = staticAllowed;
74 }
75
76 String getSource(String className, String outerName, String nested) {
77 return memberInnerStr.replaceAll("#O", outerName).
78 replaceAll("#N", className).replaceAll("#B", nested);
79 }
80
81 static String getClassfileName(String[] names, ClassKind[] outerKinds, int pos) {
82 System.out.println(" pos = " + pos + " kind = " + outerKinds[pos] + " sep = " + outerKinds[pos].sep);
83 String name = outerKinds[pos] != ANON ?
84 names[pos] : "";
85 if (pos == 0) {
86 return "Test" + outerKinds[pos].sep + name;
87 } else {
88 String outerStr = getClassfileName(names, outerKinds, pos - 1);
89 return outerStr + outerKinds[pos].sep + name;
90 }
91 }
92
93 boolean isAllowed(ClassKind nestedKind) {
94 return nestedKind != NESTED ||
95 staticAllowed;
96 }
97 }
98
99 enum LocalInnerClass {
100 LOCAL_REF("class L {}; new L();", "Test$1L"),
101 LOCAL_NOREF("class L {};", "Test$1L"),
102 ANON("new Object() {};", "Test$1"),
103 NONE("", "");
104
105 String localInnerStr;
106 String canonicalInnerStr;
107
108 private LocalInnerClass(String localInnerStr, String canonicalInnerStr) {
109 this.localInnerStr = localInnerStr;
110 this.canonicalInnerStr = canonicalInnerStr;
111 }
112 }
113
114 public static void main(String... args) throws Exception {
115 for (ClassKind ck1 : ClassKind.values()) {
116 String cname1 = "C1";
117 for (ClassKind ck2 : ClassKind.values()) {
118 if (!ck1.isAllowed(ck2)) continue;
119 String cname2 = "C2";
120 for (ClassKind ck3 : ClassKind.values()) {
121 if (!ck2.isAllowed(ck3)) continue;
122 String cname3 = "C3";
123 new T7003595(new ClassKind[] {ck1, ck2, ck3}, new String[] { cname1, cname2, cname3 }).compileAndCheck();
124 }
125 }
126 }
127
128 System.out.println("Total checks made: " + checkCount);
129 }
130
131 /** instance decls **/
132
133 ClassKind[] cks;
134 String[] cnames;
135
136 T7003595(ClassKind[] cks, String[] cnames) {
137 this.cks = cks;
138 this.cnames = cnames;
139 }
140
141 void compileAndCheck() throws Exception {
142 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
143 JavaSource source = new JavaSource();
144 JavacTask ct = (JavacTask)tool.getTask(null, fm, null,
145 null, null, Arrays.asList(source));
146 ct.call();
147 verifyBytecode(source);
148 }
149
150 void verifyBytecode(JavaSource source) {
151 for (int i = 0; i < 3 ; i ++) {
152 if (cks[i] == ClassKind.NONE) break;
153 checkCount++;
154 String filename = cks[i].getClassfileName(cnames, cks, i);
155 File compiledTest = new File(filename + ".class");
156 try {
157 ClassFile cf = ClassFile.read(compiledTest);
158 if (cf == null) {
159 throw new Error("Classfile not found: " + filename);
160 }
161
162 InnerClasses_attribute innerClasses = (InnerClasses_attribute)cf.getAttribute(Attribute.InnerClasses);
163
164 ArrayList<String> foundInnerSig = new ArrayList<>();
165 if (innerClasses != null) {
166 for (InnerClasses_attribute.Info info : innerClasses.classes) {
167 String foundSig = info.getInnerClassInfo(cf.constant_pool).getName();
168 foundInnerSig.add(foundSig);
169 }
170 }
171
172 ArrayList<String> expectedInnerSig = new ArrayList<>();
173 //add inner class (if any)
174 if (i < 2 && cks[i + 1] != ClassKind.NONE) {
175 expectedInnerSig.add(cks[i + 1].getClassfileName(cnames, cks, i + 1));
176 }
177 //add inner classes
178 for (int j = 0 ; j != i + 1 && j < 3; j++) {
179 expectedInnerSig.add(cks[j].getClassfileName(cnames, cks, j));
180 }
181
182 if (expectedInnerSig.size() != foundInnerSig.size()) {
183 throw new Error("InnerClasses attribute for " + cnames[i] + " has wrong size\n" +
184 "expected " + expectedInnerSig.size() + "\n" +
185 "found " + innerClasses.number_of_classes + "\n" +
186 source);
187 }
188
189 for (String foundSig : foundInnerSig) {
190 if (!expectedInnerSig.contains(foundSig)) {
191 throw new Error("InnerClasses attribute for " + cnames[i] + " has unexpected signature: " +
192 foundSig + "\n" + source + "\n" + expectedInnerSig);
193 }
194 }
195
196 for (String expectedSig : expectedInnerSig) {
197 if (!foundInnerSig.contains(expectedSig)) {
198 throw new Error("InnerClasses attribute for " + cnames[i] + " does not contain expected signature: " +
199 expectedSig + "\n" + source);
200 }
201 }
202 } catch (Exception e) {
203 e.printStackTrace();
204 throw new Error("error reading " + compiledTest +": " + e);
205 }
206 }
207 }
208
209 class JavaSource extends SimpleJavaFileObject {
210
211 static final String source_template = "class Test { #C }";
212
213 String source;
214
215 public JavaSource() {
216 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
217 String c3 = cks[2].getSource(cnames[2], cnames[1], "");
218 String c2 = cks[1].getSource(cnames[1], cnames[0], c3);
219 String c1 = cks[0].getSource(cnames[0], "Test", c2);
220 source = source_template.replace("#C", c1);
221 }
222
223 @Override
224 public String toString() {
225 return source;
226 }
227
228 @Override
229 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
230 return source;
231 }
232 }
233 }

mercurial