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.org.objectweb.asm 31 * java.base/jdk.internal.misc 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 jdk.internal.org.objectweb.asm.ClassWriter; 87 import jdk.internal.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 jdk.internal.org.objectweb.asm.Opcodes.AALOAD; 95 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 96 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 97 import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 98 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; 99 import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; 100 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; 101 import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0; 102 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE; 103 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; 104 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; 105 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 106 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 107 import static jdk.internal.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 }