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