|
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 } |