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