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