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