1 /*
2 * Copyright (c) 2018 SAP SE. 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 8141551
27 * @summary C2 can not handle returns with inccompatible interface arrays
28 * @requires vm.opt.final.TieredCompilation
29 * @requires vm.compMode == "Xmixed" & vm.flavor == "server"
30 * @modules java.base/jdk.internal.misc
31 * @library /testlibrary/asm
32 * @library /test/lib /
33 *
34 * @build jdk.test.whitebox.WhiteBox
35 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
36 * @run main/othervm
37 * -Xbootclasspath/a:.
38 * -XX:+UnlockDiagnosticVMOptions
39 * -XX:+WhiteBoxAPI
40 * -Xbatch
41 * -XX:-TieredCompilation
42 * -XX:TieredStopAtLevel=4
43 * -XX:CICompilerCount=1
44 * -XX:+PrintCompilation
45 * -XX:+PrintInlining
46 * -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*::run
47 * -XX:CompileCommand=dontinline,compiler.types.TestMeetIncompatibleInterfaceArrays$Helper::createI2*
48 * -XX:CompileCommand=quiet
49 * compiler.types.TestMeetIncompatibleInterfaceArrays 0
50 * @run main/othervm
51 * -Xbootclasspath/a:.
52 * -XX:+UnlockDiagnosticVMOptions
53 * -XX:+WhiteBoxAPI
54 * -Xbatch
55 * -XX:-TieredCompilation
56 * -XX:TieredStopAtLevel=4
57 * -XX:CICompilerCount=1
58 * -XX:+PrintCompilation
59 * -XX:+PrintInlining
60 * -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*::run
61 * -XX:CompileCommand=inline,compiler.types.TestMeetIncompatibleInterfaceArrays$Helper::createI2*
62 * -XX:CompileCommand=quiet
63 * compiler.types.TestMeetIncompatibleInterfaceArrays 1
64 * @run main/othervm
65 * -Xbootclasspath/a:.
66 * -XX:+UnlockDiagnosticVMOptions
67 * -XX:+WhiteBoxAPI
68 * -Xbatch
69 * -XX:+TieredCompilation
70 * -XX:TieredStopAtLevel=4
71 * -XX:CICompilerCount=2
72 * -XX:+PrintCompilation
73 * -XX:+PrintInlining
74 * -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*::run
75 * -XX:CompileCommand=compileonly,compiler.types.TestMeetIncompatibleInterfaceArrays$Helper::createI2*
76 * -XX:CompileCommand=inline,compiler.types.TestMeetIncompatibleInterfaceArrays$Helper::createI2*
77 * -XX:CompileCommand=quiet
78 * compiler.types.TestMeetIncompatibleInterfaceArrays 2
79 *
80 * @author volker.simonis@gmail.com
81 */
82
83 package compiler.types;
84
85 import compiler.whitebox.CompilerWhiteBoxTest;
86 import org.objectweb.asm.ClassWriter;
87 import org.objectweb.asm.MethodVisitor;
88 import jdk.test.whitebox.WhiteBox;
89
90 import java.io.FileOutputStream;
91 import java.lang.reflect.InvocationTargetException;
92 import java.lang.reflect.Method;
93
94 import static org.objectweb.asm.Opcodes.AALOAD;
95 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
96 import static org.objectweb.asm.Opcodes.ACC_STATIC;
97 import static org.objectweb.asm.Opcodes.ALOAD;
98 import static org.objectweb.asm.Opcodes.ARETURN;
99 import static org.objectweb.asm.Opcodes.ASTORE;
100 import static org.objectweb.asm.Opcodes.GETSTATIC;
101 import static org.objectweb.asm.Opcodes.ICONST_0;
102 import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
103 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
104 import static org.objectweb.asm.Opcodes.INVOKESTATIC;
105 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
106 import static org.objectweb.asm.Opcodes.RETURN;
107 import static org.objectweb.asm.Opcodes.V1_8;
108
109 public class TestMeetIncompatibleInterfaceArrays extends ClassLoader {
110
111 private static final WhiteBox WB = WhiteBox.getWhiteBox();
112
113 public static interface I1 { public String getName(); }
114 public static interface I2 { public String getName(); }
115 public static class I2C implements I2 { public String getName() { return "I2";} }
116 public static class I21C implements I2, I1 { public String getName() { return "I2 and I1";} }
117
118 public static class Helper {
119 public static I2 createI2Array0() {
120 return new I2C();
121 }
122 public static I2[] createI2Array1() {
123 return new I2C[] { new I2C() };
124 }
125 public static I2[][] createI2Array2() {
126 return new I2C[][] { new I2C[] { new I2C() } };
127 }
128 public static I2[][][] createI2Array3() {
129 return new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } };
130 }
131 public static I2[][][][] createI2Array4() {
132 return new I2C[][][][] { new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } } };
133 }
134 public static I2[][][][][] createI2Array5() {
135 return new I2C[][][][][] { new I2C[][][][] { new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } } } };
136 }
137 public static I2 createI21Array0() {
138 return new I21C();
139 }
140 public static I2[] createI21Array1() {
141 return new I21C[] { new I21C() };
142 }
143 public static I2[][] createI21Array2() {
144 return new I21C[][] { new I21C[] { new I21C() } };
145 }
146 public static I2[][][] createI21Array3() {
147 return new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } };
148 }
149 public static I2[][][][] createI21Array4() {
150 return new I21C[][][][] { new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } } };
151 }
152 public static I2[][][][][] createI21Array5() {
153 return new I21C[][][][][] { new I21C[][][][] { new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } } } };
154 }
155 }
156
157 // Location for the generated class files
158 public static final String PATH = System.getProperty("test.classes", ".") + java.io.File.separator;
159
160 /*
161 * With 'good == false' this helper method creates the following classes
162 * (using the nested 'Helper' class and the nested interfaces 'I1' and 'I2').
163 * For brevity I omit the enclosing class 'TestMeetIncompatibleInterfaceArrays' in the
164 * following examples:
165 *
166 * public class MeetIncompatibleInterfaceArrays0ASM {
167 * public static I1 run() {
168 * return Helper.createI2Array0(); // returns I2
169 * }
170 * public static void test() {
171 * I1 i1 = run();
172 * System.out.println(i1.getName());
173 * }
174 * }
175 * public class MeetIncompatibleInterfaceArrays1ASM {
176 * public static I1[] run() {
177 * return Helper.createI2Array1(); // returns I2[]
178 * }
179 * public static void test() {
180 * I1[] i1 = run();
181 * System.out.println(i1[0].getName());
182 * }
183 * }
184 * ...
185 * // MeetIncompatibleInterfaceArrays4ASM is special because it creates
186 * // an illegal class which will be rejected by the verifier.
187 * public class MeetIncompatibleInterfaceArrays4ASM {
188 * public static I1[][][][] run() {
189 * return Helper.createI2Array3(); // returns I1[][][] which gives a verifier error because return expects I1[][][][]
190 * }
191 * public static void test() {
192 * I1[][][][] i1 = run();
193 * System.out.println(i1[0][0][0][0].getName());
194 * }
195 * ...
196 * public class MeetIncompatibleInterfaceArrays5ASM {
197 * public static I1[][][][][] run() {
198 * return Helper.createI2Array5(); // returns I2[][][][][]
199 * }
200 * public static void test() {
201 * I1[][][][][] i1 = run();
202 * System.out.println(i1[0][0][0][0][0].getName());
203 * }
204 * }
205 *
206 * Notice that this is not legal Java code. We would have to use a cast in "run()" to make it legal:
207 *
208 * public static I1[] run() {
209 * return (I1[])Helper.createI2Array1(); // returns I2[]
210 * }
211 *
212 * But in pure bytecode, the "run()" methods are perfectly legal:
213 *
214 * public static I1[] run();
215 * Code:
216 * 0: invokestatic #16 // Method Helper.createI2Array1:()[LI2;
217 * 3: areturn
218 *
219 * The "test()" method calls the "getName()" function from I1 on the objects returned by "run()".
220 * This will expectedly fail with an "IncompatibleClassChangeError" because the objects returned
221 * by "run()" (and by createI2Array()) are actually of type "I2C" and only implement "I2" but not "I1".
222 *
223 *
224 * With 'good == true' this helper method will create the following classes:
225 *
226 * public class MeetIncompatibleInterfaceArraysGood0ASM {
227 * public static I1 run() {
228 * return Helper.createI21Array0(); // returns I2
229 * }
230 * public static void test() {
231 * I1 i1 = run();
232 * System.out.println(i1.getName());
233 * }
234 * }
235 *
236 * Calling "test()" on these objects will succeed and output "I2 and I1" because now the "run()"
237 * method calls "createI21Array()" which actually return an object (or an array of objects) of
238 * type "I21C" which implements both "I2" and "I1".
239 *
240 * Notice that at the bytecode level, the code for the "run()" and "test()" methods in
241 * "MeetIncompatibleInterfaceArraysASM" and "MeetIncompatibleInterfaceArraysGoodASM" look exactly
242 * the same. I.e. the verifier has no chance to verify if the I2 object returned by "createI1Array()"
243 * or "createI21Array()" implements "I1" or not. That's actually the reason why both versions of
244 * generated classes are legal from a verifier point of view.
245 *
246 */
247 static void generateTestClass(int dim, boolean good) throws Exception {
248 String baseClassName = "MeetIncompatibleInterfaceArrays";
249 if (good)
250 baseClassName += "Good";
251 String createName = "createI2" + (good ? "1" : "") + "Array";
252 String a = "";
253 for (int i = 0; i < dim; i++)
254 a += "[";
255 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
256 cw.visit(V1_8, ACC_PUBLIC, baseClassName + dim + "ASM", null, "java/lang/Object", null);
257 MethodVisitor constr = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
258 constr.visitCode();
259 constr.visitVarInsn(ALOAD, 0);
260 constr.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
261 constr.visitInsn(RETURN);
262 constr.visitMaxs(0, 0);
263 constr.visitEnd();
264 MethodVisitor run = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "run",
265 "()" + a + "Lcompiler/types/TestMeetIncompatibleInterfaceArrays$I1;", null, null);
266 run.visitCode();
267 if (dim == 4) {
268 run.visitMethodInsn(INVOKESTATIC, "compiler/types/TestMeetIncompatibleInterfaceArrays$Helper", createName + 3,
269 "()" + "[[[" + "Lcompiler/types/TestMeetIncompatibleInterfaceArrays$I2;", false);
270 } else {
271 run.visitMethodInsn(INVOKESTATIC, "compiler/types/TestMeetIncompatibleInterfaceArrays$Helper", createName + dim,
272 "()" + a + "Lcompiler/types/TestMeetIncompatibleInterfaceArrays$I2;", false);
273 }
274 run.visitInsn(ARETURN);
275 run.visitMaxs(0, 0);
276 run.visitEnd();
277 MethodVisitor test = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "()V", null, null);
278 test.visitCode();
279 test.visitMethodInsn(INVOKESTATIC, baseClassName + dim + "ASM", "run",
280 "()" + a + "Lcompiler/types/TestMeetIncompatibleInterfaceArrays$I1;", false);
281 test.visitVarInsn(ASTORE, 0);
282 if (dim > 0) {
283 test.visitVarInsn(ALOAD, 0);
284 for (int i = 1; i <= dim; i++) {
285 test.visitInsn(ICONST_0);
286 test.visitInsn(AALOAD);
287 }
288 test.visitVarInsn(ASTORE, 1);
289 }
290 test.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
291 test.visitVarInsn(ALOAD, dim > 0 ? 1 : 0);
292 test.visitMethodInsn(INVOKEINTERFACE, "compiler/types/TestMeetIncompatibleInterfaceArrays$I1", "getName",
293 "()Ljava/lang/String;", true);
294 test.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
295 test.visitInsn(RETURN);
296 test.visitMaxs(0, 0);
297 test.visitEnd();
298
299 // Get the bytes of the class..
300 byte[] b = cw.toByteArray();
301 // ..and write them into a class file (for debugging)
302 FileOutputStream fos = new FileOutputStream(PATH + baseClassName + dim + "ASM.class");
303 fos.write(b);
304 fos.close();
305
306 }
307
308 public static String[][] tier = { { "interpreted (tier 0)",
309 "C2 (tier 4) without inlining",
310 "C2 (tier 4) without inlining" },
311 { "interpreted (tier 0)",
312 "C2 (tier 4) with inlining",
313 "C2 (tier 4) with inlining" },
314 { "interpreted (tier 0)",
315 "C1 (tier 3) with inlining",
316 "C2 (tier 4) with inlining" } };
317
318 public static int[][] level = { { CompilerWhiteBoxTest.COMP_LEVEL_NONE,
319 CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION,
320 CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION },
321 { CompilerWhiteBoxTest.COMP_LEVEL_NONE,
322 CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION,
323 CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION },
324 { CompilerWhiteBoxTest.COMP_LEVEL_NONE,
325 CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE,
326 CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION } };
327
328 public static void main(String[] args) throws Exception {
329 final int pass = Integer.parseInt(args.length > 0 ? args[0] : "0");
330
331 // Load and initialize some classes required for compilation
332 Class.forName("compiler.types.TestMeetIncompatibleInterfaceArrays$I1");
333 Class.forName("compiler.types.TestMeetIncompatibleInterfaceArrays$I2");
334 Class.forName("compiler.types.TestMeetIncompatibleInterfaceArrays$Helper");
335
336 for (int g = 0; g < 2; g++) {
337 String baseClassName = "MeetIncompatibleInterfaceArrays";
338 boolean good = (g == 0) ? false : true;
339 if (good)
340 baseClassName += "Good";
341 for (int i = 0; i < 6; i++) {
342 System.out.println();
343 System.out.println("Creating " + baseClassName + i + "ASM.class");
344 System.out.println("========================================" + "=" + "=========");
345 // Create the "MeetIncompatibleInterfaceArrays<i>ASM" class
346 generateTestClass(i, good);
347 Class<?> c = null;
348 try {
349 c = Class.forName(baseClassName + i + "ASM");
350 } catch (VerifyError ve) {
351 if (i == 4) {
352 System.out.println("OK - must be (" + ve.getMessage() + ").");
353 } else {
354 throw ve;
355 }
356 continue;
357 }
358 // Call MeetIncompatibleInterfaceArrays<i>ASM.test()
359 Method m = c.getMethod("test");
360 Method r = c.getMethod("run");
361 for (int j = 0; j < 3; j++) {
362 System.out.println((j + 1) + ". invokation of " + baseClassName + i + "ASM.test() [::" +
363 r.getName() + "() should be '" + tier[pass][j] + "' compiled]");
364
365 // Skip Profiling compilation (C1) when Tiered is disabled.
366 boolean profile = (level[pass][j] == CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE);
367 if (profile && CompilerWhiteBoxTest.skipOnTieredCompilation(false)) {
368 continue;
369 }
370
371 WB.enqueueMethodForCompilation(r, level[pass][j]);
372
373 try {
374 m.invoke(null);
375 } catch (InvocationTargetException ite) {
376 if (good) {
377 throw ite;
378 } else {
379 if (ite.getCause() instanceof IncompatibleClassChangeError) {
380 System.out.println(" OK - catched InvocationTargetException("
381 + ite.getCause().getMessage() + ").");
382 } else {
383 throw ite;
384 }
385 }
386 }
387
388 int r_comp_level = WB.getMethodCompilationLevel(r);
389 System.out.println(" invokation of " + baseClassName + i + "ASM.test() [::" +
390 r.getName() + "() was compiled at tier " + r_comp_level + "]");
391
392 if (r_comp_level != level[pass][j]) {
393 throw new Exception("Method " + r + " must be compiled at tier " + level[pass][j] +
394 " but was compiled at " + r_comp_level + " instead!");
395 }
396
397 WB.deoptimizeMethod(r);
398 }
399 }
400 }
401 }
402 }
--- EOF ---