comparison test/compiler/jsr292/methodHandleExceptions/TestAMEnotNPE.java @ 13403:9d15b81d5d1b

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