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