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