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