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