1 /* 2 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. 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 /** 26 * @test 27 * @bug 8025260 8016839 28 * @summary Ensure that AbstractMethodError and IllegalAccessError are thrown appropriately, not NullPointerException 29 * @modules java.base/jdk.internal.org.objectweb.asm 30 * @library / . 31 * 32 * @build p.* 33 * @run main/othervm compiler.jsr292.methodHandleExceptions.TestAMEnotNPE 34 * @run main/othervm -Xint compiler.jsr292.methodHandleExceptions.TestAMEnotNPE 35 * @run main/othervm -Xcomp compiler.jsr292.methodHandleExceptions.TestAMEnotNPE 36 */ 37 38 package compiler.jsr292.methodHandleExceptions; 39 40 import p.Dok; 41 import jdk.internal.org.objectweb.asm.ClassWriter; 42 import jdk.internal.org.objectweb.asm.Handle; 43 import jdk.internal.org.objectweb.asm.MethodVisitor; 44 import jdk.internal.org.objectweb.asm.Opcodes; 45 46 import java.lang.reflect.InvocationTargetException; 47 import java.lang.reflect.Method; 48 import java.util.ArrayList; 49 import java.util.List; 50 51 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 52 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 53 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 54 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 55 import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 56 import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; 57 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; 58 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 59 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN; 60 import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD; 61 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 62 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; 63 64 public class TestAMEnotNPE { 65 66 static boolean writeJarFiles = false; 67 static boolean readJarFiles = false; 68 69 /** 70 * Optional command line parameter (any case-insensitive prefix of) 71 * "writejarfiles" or "readjarfiles". 72 * 73 * "Writejarfiles" creates a jar file for each different set of tested classes. 74 * "Readjarfiles" causes the classloader to use the copies of the classes 75 * found in the corresponding jar files. 76 * 77 * Jarfilenames look something like pD_ext_pF (p.D extends p.F) 78 * and qD_m_pp_imp_pI (q.D with package-private m implements p.I) 79 * 80 */ 81 public static void main(String args[]) throws Throwable { 82 ArrayList<Throwable> lt = new ArrayList<Throwable>(); 83 84 if (args.length > 0) { 85 String a0 = args[0].toLowerCase(); 86 if (a0.length() > 0) { 87 writeJarFiles = ("writejarfiles").startsWith(a0); 88 readJarFiles = ("readjarfiles").startsWith(a0); 89 } 90 if (!(writeJarFiles || readJarFiles)) { 91 throw new Error("Command line parameter (if any) should be prefix of writeJarFiles or readJarFiles"); 92 } 93 } 94 95 try { 96 System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m, p.D extends p.F, p.F.m FINAL"); 97 tryAndCheckThrown(lt, bytesForDprivateSubWhat("p/F"), 98 "p.D extends p.F (p.F implements p.I, FINAL public m), private m", 99 IllegalAccessError.class, "pD_ext_pF"); 100 // We'll take either a VerifyError (pre 2013-11-30) 101 // or an IllegalAccessError (post 2013-11-22) 102 } catch (VerifyError ve) { 103 System.out.println("Saw expected VerifyError " + ve); 104 } 105 System.out.println(); 106 107 System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m, p.D extends p.E"); 108 tryAndCheckThrown(lt, bytesForDprivateSubWhat("p/E"), 109 "p.D extends p.E (p.E implements p.I, public m), private m", 110 IllegalAccessError.class, "pD_ext_pE"); 111 112 System.out.println("TRYING p.D.m ABSTRACT interface-invoked as p.I.m"); 113 tryAndCheckThrown(lt, bytesForD(), 114 "D extends abstract C, no m", 115 AbstractMethodError.class, "pD_ext_pC"); 116 117 System.out.println("TRYING q.D.m PACKAGE interface-invoked as p.I.m"); 118 tryAndCheckThrown(lt, "q.D", bytesForDsomeAccess("q/D", 0), 119 "q.D implements p.I, protected m", IllegalAccessError.class, 120 "qD_m_pp_imp_pI"); 121 122 // Note jar file name is used in the plural-arg case. 123 System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m"); 124 tryAndCheckThrown(lt, bytesForDsomeAccess("p/D", ACC_PRIVATE), 125 "p.D implements p.I, private m", 126 IllegalAccessError.class, "pD_m_pri_imp_pI"); 127 128 // Plural-arg test. 129 System.out.println("TRYING p.D.m PRIVATE MANY ARG interface-invoked as p.I.m"); 130 tryAndCheckThrownMany(lt, bytesForDsomeAccess("p/D", ACC_PRIVATE), 131 "p.D implements p.I, private m", IllegalAccessError.class); 132 133 if (lt.size() > 0) { 134 System.out.flush(); 135 Thread.sleep(250); // This de-interleaves output and error in Netbeans, sigh. 136 for (Throwable th : lt) 137 System.err.println(th); 138 throw new Error("Test failed, there were " + lt.size() + " failures listed above"); 139 } else { 140 System.out.println("ALL PASS, HOORAY!"); 141 } 142 } 143 144 /** 145 * The bytes for D, a NOT abstract class extending abstract class C without 146 * supplying an implementation for abstract method m. There is a default 147 * method in the interface I, but it should lose to the abstract class. 148 * 149 * @return 150 * @throws Exception 151 */ 152 public static byte[] bytesForD() throws Exception { 153 154 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES 155 | ClassWriter.COMPUTE_MAXS); 156 MethodVisitor mv; 157 158 cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/D", null, "p/C", null); 159 160 { 161 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 162 mv.visitCode(); 163 mv.visitVarInsn(ALOAD, 0); 164 mv.visitMethodInsn(INVOKESPECIAL, "p/C", "<init>", "()V"); 165 mv.visitInsn(RETURN); 166 mv.visitMaxs(0, 0); 167 mv.visitEnd(); 168 } 169 cw.visitEnd(); 170 171 return cw.toByteArray(); 172 } 173 174 /** 175 * The bytes for D, implements I, does not extend C, declares m()I with 176 * access method_acc. 177 * 178 * @param d_name Name of class defined 179 * @param method_acc Accessibility of that class's method m. 180 * @return 181 * @throws Exception 182 */ 183 public static byte[] bytesForDsomeAccess(String d_name, int method_acc) throws Exception { 184 return bytesForSomeDsubSomethingSomeAccess(d_name, "java/lang/Object", method_acc); 185 } 186 187 /** 188 * The bytes for D implements I, extends some class, declares m()I as 189 * private. 190 * 191 * Invokeinterface of I.m applied to this D should throw IllegalAccessError 192 * 193 * @param sub_what The name of the class that D will extend. 194 * @return 195 * @throws Exception 196 */ 197 public static byte[] bytesForDprivateSubWhat(String sub_what) throws Exception { 198 return bytesForSomeDsubSomethingSomeAccess("p/D", sub_what, ACC_PRIVATE); 199 } 200 201 /** 202 * Returns the bytes for a class with name d_name (presumably "D" in some 203 * package), extending some class with name sub_what, implementing p.I, 204 * and defining two methods m() and m(11args) with access method_acc. 205 * 206 * @param d_name Name of class that is defined 207 * @param sub_what Name of class that it extends 208 * @param method_acc Accessibility of method(s) m in defined class. 209 * @return 210 * @throws Exception 211 */ 212 public static byte[] bytesForSomeDsubSomethingSomeAccess 213 (String d_name, String sub_what, int method_acc) 214 throws Exception { 215 216 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES 217 | ClassWriter.COMPUTE_MAXS); 218 MethodVisitor mv; 219 String[] interfaces = {"p/I"}; 220 221 cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, d_name, null, sub_what, interfaces); 222 { 223 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 224 mv.visitCode(); 225 mv.visitVarInsn(ALOAD, 0); 226 mv.visitMethodInsn(INVOKESPECIAL, sub_what, "<init>", "()V"); 227 mv.visitInsn(RETURN); 228 mv.visitMaxs(0, 0); 229 mv.visitEnd(); 230 } 231 // int m() {return 3;} 232 { 233 mv = cw.visitMethod(method_acc, "m", "()I", null, null); 234 mv.visitCode(); 235 mv.visitLdcInsn(new Integer(3)); 236 mv.visitInsn(IRETURN); 237 mv.visitMaxs(0, 0); 238 mv.visitEnd(); 239 } 240 // int m(11args) {return 3;} 241 { 242 mv = cw.visitMethod(method_acc, "m", "(BCSIJ" 243 + "Ljava/lang/Object;" 244 + "Ljava/lang/Object;" 245 + "Ljava/lang/Object;" 246 + "Ljava/lang/Object;" 247 + "Ljava/lang/Object;" 248 + "Ljava/lang/Object;" 249 + ")I", null, null); 250 mv.visitCode(); 251 mv.visitLdcInsn(new Integer(3)); 252 mv.visitInsn(IRETURN); 253 mv.visitMaxs(0, 0); 254 mv.visitEnd(); 255 } 256 cw.visitEnd(); 257 return cw.toByteArray(); 258 } 259 260 /** 261 * The bytecodes for a class p/T defining a methods test() and test(11args) 262 * that contain an invokeExact of a particular methodHandle, I.m. 263 * 264 * Test will be passed values that may imperfectly implement I, 265 * and thus may in turn throw exceptions. 266 * 267 * @return 268 * @throws Exception 269 */ 270 public static byte[] bytesForT() throws Exception { 271 272 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES 273 | ClassWriter.COMPUTE_MAXS); 274 MethodVisitor mv; 275 276 cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/T", null, "java/lang/Object", null); 277 { 278 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 279 mv.visitCode(); 280 mv.visitVarInsn(ALOAD, 0); 281 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); 282 mv.visitInsn(RETURN); 283 mv.visitMaxs(0, 0); 284 mv.visitEnd(); 285 } 286 // static int test(I) 287 { 288 mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "(Lp/I;)I", null, null); 289 mv.visitCode(); 290 mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "p/I", "m", "()I")); 291 mv.visitVarInsn(ALOAD, 0); 292 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", 293 "invokeExact", "(Lp/I;)I"); 294 mv.visitInsn(IRETURN); 295 mv.visitMaxs(0, 0); 296 mv.visitEnd(); 297 } 298 // static int test(I,11args) 299 { 300 mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "(Lp/I;BCSIJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I", null, null); 301 mv.visitCode(); 302 mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "p/I", "m", "(BCSIJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I")); 303 mv.visitVarInsn(ALOAD, 0); 304 mv.visitVarInsn(ILOAD, 1); 305 mv.visitVarInsn(ILOAD, 2); 306 mv.visitVarInsn(ILOAD, 3); 307 mv.visitVarInsn(ILOAD, 4); 308 mv.visitVarInsn(LLOAD, 5); 309 mv.visitVarInsn(ALOAD, 7); 310 mv.visitVarInsn(ALOAD, 8); 311 mv.visitVarInsn(ALOAD, 9); 312 mv.visitVarInsn(ALOAD, 10); 313 mv.visitVarInsn(ALOAD, 11); 314 mv.visitVarInsn(ALOAD, 12); 315 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", 316 "invokeExact", "(Lp/I;BCSIJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I"); 317 mv.visitInsn(IRETURN); 318 mv.visitMaxs(0, 0); 319 mv.visitEnd(); 320 } 321 cw.visitEnd(); 322 return cw.toByteArray(); 323 } 324 325 private static void tryAndCheckThrown( 326 List<Throwable> lt, byte[] dBytes, String what, Class<?> expected, String jar_name) 327 throws Throwable { 328 tryAndCheckThrown(lt, "p.D", dBytes, what, expected, jar_name); 329 } 330 331 private static void tryAndCheckThrown(List<Throwable> lt, String d_name, byte[] dBytes, String what, Class<?> expected, String jar_name) 332 throws Throwable { 333 334 System.out.println("Methodhandle invokeExact I.m() for instance of " + what); 335 ByteClassLoader bcl1 = new ByteClassLoader(jar_name, readJarFiles, writeJarFiles); 336 try { 337 Class<?> d1 = bcl1.loadBytes(d_name, dBytes); 338 Class<?> t1 = bcl1.loadBytes("p.T", bytesForT()); 339 invokeTest(t1, d1, expected, lt); 340 } finally { 341 // Not necessary for others -- all class files are written in this call. 342 // (unless the VM crashes first). 343 bcl1.close(); 344 } 345 346 System.out.println("Reflection invoke I.m() for instance of " + what); 347 ByteClassLoader bcl3 = new ByteClassLoader(jar_name, readJarFiles, false); 348 Class<?> d3 = bcl3.loadBytes(d_name, dBytes); 349 Class<?> t3 = bcl3.loadClass("p.Treflect"); 350 invokeTest(t3, d3, expected, lt); 351 352 System.out.println("Bytecode invokeInterface I.m() for instance of " + what); 353 ByteClassLoader bcl2 = new ByteClassLoader(jar_name, readJarFiles, false); 354 Class<?> d2 = bcl2.loadBytes(d_name, dBytes); 355 Class<?> t2 = bcl2.loadClass("p.Tdirect"); 356 badGoodBadGood(t2, d2, expected, lt); 357 } 358 359 private static void invokeTest(Class<?> t, Class<?> d, Class<?> expected, List<Throwable> lt) 360 throws Throwable { 361 try { 362 Method m = t.getMethod("test", p.I.class); 363 Object o = d.newInstance(); 364 Object result = m.invoke(null, o); 365 if (expected != null) { 366 System.out.println("FAIL, Expected " + expected.getName() 367 + " wrapped in InvocationTargetException, but nothing was thrown"); 368 lt.add(new Error("Exception " + expected.getName() + " was not thrown")); 369 } else { 370 System.out.println("PASS, saw expected return."); 371 } 372 } catch (InvocationTargetException e) { 373 Throwable th = e.getCause(); 374 th.printStackTrace(System.out); 375 if (expected != null) { 376 if (expected.isInstance(th)) { 377 System.out.println("PASS, saw expected exception (" + expected.getName() + ")."); 378 } else { 379 System.out.println("FAIL, Expected " + expected.getName() 380 + " wrapped in InvocationTargetException, saw " + th); 381 lt.add(th); 382 } 383 } else { 384 System.out.println("FAIL, expected no exception, saw " + th); 385 lt.add(th); 386 } 387 } 388 System.out.println(); 389 } 390 391 /* Many-arg versions of above */ 392 private static void tryAndCheckThrownMany(List<Throwable> lt, byte[] dBytes, String what, Class<?> expected) 393 throws Throwable { 394 395 System.out.println("Methodhandle invokeExact I.m(11params) for instance of " + what); 396 ByteClassLoader bcl1 = new ByteClassLoader("p.D", readJarFiles, false); 397 try { 398 Class<?> d1 = bcl1.loadBytes("p.D", dBytes); 399 Class<?> t1 = bcl1.loadBytes("p.T", bytesForT()); 400 invokeTestMany(t1, d1, expected, lt); 401 } finally { 402 bcl1.close(); // Not necessary for others -- all class files are written in this call. 403 } 404 405 { 406 System.out.println("Bytecode invokeInterface I.m(11params) for instance of " + what); 407 ByteClassLoader bcl2 = new ByteClassLoader("pD_m_pri_imp_pI", readJarFiles, false); 408 Class<?> d2 = bcl2.loadBytes("p.D", dBytes); 409 Class<?> t2 = bcl2.loadClass("p.Tdirect"); 410 badGoodBadGoodMany(t2, d2, expected, lt); 411 412 } 413 { 414 System.out.println("Reflection invokeInterface I.m(11params) for instance of " + what); 415 ByteClassLoader bcl2 = new ByteClassLoader("pD_m_pri_imp_pI", readJarFiles, false); 416 Class<?> d2 = bcl2.loadBytes("p.D", dBytes); 417 Class<?> t2 = bcl2.loadClass("p.Treflect"); 418 invokeTestMany(t2, d2, expected, lt); 419 } 420 } 421 422 private static void invokeTestMany(Class<?> t, Class<?> d, Class<?> expected, List<Throwable> lt) 423 throws Throwable { 424 try { 425 Method m = t.getMethod("test", p.I.class, 426 Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, 427 Object.class, Object.class, Object.class, 428 Object.class, Object.class, Object.class); 429 Object o = d.newInstance(); 430 Byte b = 1; 431 Character c = 2; 432 Short s = 3; 433 Integer i = 4; 434 Long j = 5L; 435 Object o1 = b; 436 Object o2 = c; 437 Object o3 = s; 438 Object o4 = i; 439 Object o5 = j; 440 Object o6 = "6"; 441 442 Object result = m.invoke(null, o, b, c, s, i, j, 443 o1, o2, o3, o4, o5, o6); 444 if (expected != null) { 445 System.out.println("FAIL, Expected " + expected.getName() 446 + " wrapped in InvocationTargetException, but nothing was thrown"); 447 lt.add(new Error("Exception " + expected.getName() 448 + " was not thrown")); 449 } else { 450 System.out.println("PASS, saw expected return."); 451 } 452 } catch (InvocationTargetException e) { 453 Throwable th = e.getCause(); 454 th.printStackTrace(System.out); 455 if (expected != null) { 456 if (expected.isInstance(th)) { 457 System.out.println("PASS, saw expected exception (" 458 + expected.getName() + ")."); 459 } else { 460 System.out.println("FAIL, Expected " + expected.getName() 461 + " wrapped in InvocationTargetException, saw " + th); 462 lt.add(th); 463 } 464 } else { 465 System.out.println("FAIL, expected no exception, saw " + th); 466 lt.add(th); 467 } 468 } 469 System.out.println(); 470 } 471 472 /** 473 * This tests a peculiar idiom for tickling the bug on older VMs that lack 474 * methodhandles. The bug (if not fixed) acts in the following way: 475 * 476 * When a broken receiver is passed to the first execution of an invokeinterface 477 * bytecode, the illegal access is detected before the effects of resolution are 478 * cached for later use, and so repeated calls with a broken receiver will always 479 * throw the correct error. 480 * 481 * If, however, a good receiver is passed to the invokeinterface, the effects of 482 * resolution will be successfully cached. A subsequent execution with a broken 483 * receiver will reuse the cached information, skip the detailed resolution work, 484 * and instead encounter a null pointer. By convention, that is the encoding for a 485 * missing abstract method, and an AbstractMethodError is thrown -- not the expected 486 * IllegalAccessError. 487 * 488 * @param t2 Test invocation class 489 * @param d2 Test receiver class 490 * @param expected expected exception type 491 * @param lt list of unexpected throwables seen 492 */ 493 private static void badGoodBadGood(Class<?> t2, Class<?> d2, Class<?> expected, List<Throwable> lt) 494 throws Throwable { 495 System.out.println(" Error input 1st time"); 496 invokeTest(t2, d2, expected, lt); 497 System.out.println(" Good input (instance of Dok)"); 498 invokeTest(t2, Dok.class, null, lt); 499 System.out.println(" Error input 2nd time"); 500 invokeTest(t2, d2, expected, lt); 501 System.out.println(" Good input (instance of Dok)"); 502 invokeTest(t2, Dok.class, null, lt); 503 } 504 505 private static void badGoodBadGoodMany(Class<?> t2, Class<?> d2, Class<?> expected, List<Throwable> lt) 506 throws Throwable { 507 System.out.println(" Error input 1st time"); 508 invokeTestMany(t2, d2, expected, lt); 509 System.out.println(" Good input (instance of Dok)"); 510 invokeTestMany(t2, Dok.class, null, lt); 511 System.out.println(" Error input 2nd time"); 512 invokeTestMany(t2, d2, expected, lt); 513 System.out.println(" Good input (instance of Dok)"); 514 invokeTestMany(t2, Dok.class, null, lt); 515 } 516 }