2613b94f28
Reviewed-by: kvn, vlivanov
525 lines
22 KiB
Java
525 lines
22 KiB
Java
/*
|
|
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/**
|
|
* @test
|
|
* @bug 8025260 8016839 8046171
|
|
* @summary Ensure that correct exceptions are thrown, not NullPointerException
|
|
* @modules java.base/jdk.internal.org.objectweb.asm
|
|
* @library / .
|
|
*
|
|
* @build p.*
|
|
* @run main/othervm compiler.jsr292.methodHandleExceptions.TestAMEnotNPE
|
|
* @run main/othervm -Xint
|
|
* compiler.jsr292.methodHandleExceptions.TestAMEnotNPE
|
|
* @run main/othervm -Xcomp
|
|
* -XX:CompileCommand=compileonly,p.*::*
|
|
* -XX:CompileCommand=compileonly,q.*::*
|
|
* compiler.jsr292.methodHandleExceptions.TestAMEnotNPE
|
|
*/
|
|
|
|
// Since this test was written the specification for interface method selection has been
|
|
// revised (JEP 181 - Nestmates) so that private methods are never selected, as they never
|
|
// override any inherited method. So where a private method was previously selected
|
|
// and then resulted in IllegalAccessError, the private method is skipped and the invocation
|
|
// will either succeed or fail based on what other implementations are found in the inheritance
|
|
// hierarchy. This is explained for each test below.
|
|
|
|
package compiler.jsr292.methodHandleExceptions;
|
|
|
|
import p.Dok;
|
|
import jdk.internal.org.objectweb.asm.ClassWriter;
|
|
import jdk.internal.org.objectweb.asm.Handle;
|
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
|
|
import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
|
|
|
|
public class TestAMEnotNPE {
|
|
|
|
static boolean writeJarFiles = false;
|
|
static boolean readJarFiles = false;
|
|
|
|
/**
|
|
* Optional command line parameter (any case-insensitive prefix of)
|
|
* "writejarfiles" or "readjarfiles".
|
|
*
|
|
* "Writejarfiles" creates a jar file for each different set of tested classes.
|
|
* "Readjarfiles" causes the classloader to use the copies of the classes
|
|
* found in the corresponding jar files.
|
|
*
|
|
* Jarfilenames look something like pD_ext_pF (p.D extends p.F)
|
|
* and qD_m_pp_imp_pI (q.D with package-private m implements p.I)
|
|
*
|
|
*/
|
|
public static void main(String args[]) throws Throwable {
|
|
ArrayList<Throwable> lt = new ArrayList<Throwable>();
|
|
|
|
if (args.length > 0) {
|
|
String a0 = args[0].toLowerCase();
|
|
if (a0.length() > 0) {
|
|
writeJarFiles = ("writejarfiles").startsWith(a0);
|
|
readJarFiles = ("readjarfiles").startsWith(a0);
|
|
}
|
|
if (!(writeJarFiles || readJarFiles)) {
|
|
throw new Error("Command line parameter (if any) should be prefix of writeJarFiles or readJarFiles");
|
|
}
|
|
}
|
|
|
|
System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m, p.D extends p.F, p.F.m FINAL");
|
|
System.out.println(" - should invoke p.F.m as private p.D.m is skipped for selection");
|
|
tryAndCheckThrown(lt, bytesForDprivateSubWhat("p/F"),
|
|
"p.D extends p.F (p.F implements p.I, FINAL public m), private m",
|
|
null /* should succeed */, "pD_ext_pF");
|
|
System.out.println();
|
|
|
|
System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m, p.D extends p.E");
|
|
System.out.println(" - should invoke p.E.m as private p.D.m is skipped for selection");
|
|
tryAndCheckThrown(lt, bytesForDprivateSubWhat("p/E"),
|
|
"p.D extends p.E (p.E implements p.I, public m), private m",
|
|
null /* should succeed */, "pD_ext_pE");
|
|
|
|
System.out.println("TRYING p.D.m ABSTRACT interface-invoked as p.I.m");
|
|
tryAndCheckThrown(lt, bytesForD(),
|
|
"D extends abstract C, no m",
|
|
AbstractMethodError.class, "pD_ext_pC");
|
|
|
|
System.out.println("TRYING q.D.m PACKAGE interface-invoked as p.I.m");
|
|
tryAndCheckThrown(lt, "q.D", bytesForDsomeAccess("q/D", 0),
|
|
"q.D implements p.I, protected m",
|
|
IllegalAccessError.class, "qD_m_pp_imp_pI");
|
|
|
|
// Note jar file name is used in the plural-arg case.
|
|
System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m");
|
|
System.out.println(" - should invoke p.I.m as private p.D.m is skipped for selection");
|
|
tryAndCheckThrown(lt, bytesForDsomeAccess("p/D", ACC_PRIVATE),
|
|
"p.D implements p.I, private m",
|
|
null /* should succeed */, "pD_m_pri_imp_pI");
|
|
|
|
// Plural-arg test.
|
|
System.out.println("TRYING p.D.m PRIVATE MANY ARG interface-invoked as p.I.m");
|
|
System.out.println(" - should invoke p.I.m as private p.D.m is skipped for selection");
|
|
tryAndCheckThrownMany(lt, bytesForDsomeAccess("p/D", ACC_PRIVATE),
|
|
"p.D implements p.I, private m", null /* should succeed */);
|
|
|
|
if (lt.size() > 0) {
|
|
System.out.flush();
|
|
Thread.sleep(250); // This de-interleaves output and error in Netbeans, sigh.
|
|
for (Throwable th : lt)
|
|
System.err.println(th);
|
|
throw new Error("Test failed, there were " + lt.size() + " failures listed above");
|
|
} else {
|
|
System.out.println("ALL PASS, HOORAY!");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The bytes for D, a NOT abstract class extending abstract class C without
|
|
* supplying an implementation for abstract method m. There is a default
|
|
* method in the interface I, but it should lose to the abstract class.
|
|
*
|
|
* @return
|
|
* @throws Exception
|
|
*/
|
|
public static byte[] bytesForD() throws Exception {
|
|
|
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES
|
|
| ClassWriter.COMPUTE_MAXS);
|
|
MethodVisitor mv;
|
|
|
|
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/D", null, "p/C", null);
|
|
|
|
{
|
|
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
|
mv.visitCode();
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitMethodInsn(INVOKESPECIAL, "p/C", "<init>", "()V");
|
|
mv.visitInsn(RETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
cw.visitEnd();
|
|
|
|
return cw.toByteArray();
|
|
}
|
|
|
|
/**
|
|
* The bytes for D, implements I, does not extend C, declares m()I with
|
|
* access method_acc.
|
|
*
|
|
* @param d_name Name of class defined
|
|
* @param method_acc Accessibility of that class's method m.
|
|
* @return
|
|
* @throws Exception
|
|
*/
|
|
public static byte[] bytesForDsomeAccess(String d_name, int method_acc) throws Exception {
|
|
return bytesForSomeDsubSomethingSomeAccess(d_name, "java/lang/Object", method_acc);
|
|
}
|
|
|
|
/**
|
|
* The bytes for D implements I, extends some class, declares m()I as
|
|
* private.
|
|
*
|
|
* Invokeinterface of I.m applied to this D should throw IllegalAccessError
|
|
*
|
|
* @param sub_what The name of the class that D will extend.
|
|
* @return
|
|
* @throws Exception
|
|
*/
|
|
public static byte[] bytesForDprivateSubWhat(String sub_what) throws Exception {
|
|
return bytesForSomeDsubSomethingSomeAccess("p/D", sub_what, ACC_PRIVATE);
|
|
}
|
|
|
|
/**
|
|
* Returns the bytes for a class with name d_name (presumably "D" in some
|
|
* package), extending some class with name sub_what, implementing p.I,
|
|
* and defining two methods m() and m(11args) with access method_acc.
|
|
*
|
|
* @param d_name Name of class that is defined
|
|
* @param sub_what Name of class that it extends
|
|
* @param method_acc Accessibility of method(s) m in defined class.
|
|
* @return
|
|
* @throws Exception
|
|
*/
|
|
public static byte[] bytesForSomeDsubSomethingSomeAccess
|
|
(String d_name, String sub_what, int method_acc)
|
|
throws Exception {
|
|
|
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES
|
|
| ClassWriter.COMPUTE_MAXS);
|
|
MethodVisitor mv;
|
|
String[] interfaces = {"p/I"};
|
|
|
|
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, d_name, null, sub_what, interfaces);
|
|
{
|
|
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
|
mv.visitCode();
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitMethodInsn(INVOKESPECIAL, sub_what, "<init>", "()V");
|
|
mv.visitInsn(RETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
// int m() {return 3;}
|
|
{
|
|
mv = cw.visitMethod(method_acc, "m", "()I", null, null);
|
|
mv.visitCode();
|
|
mv.visitLdcInsn(new Integer(3));
|
|
mv.visitInsn(IRETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
// int m(11args) {return 3;}
|
|
{
|
|
mv = cw.visitMethod(method_acc, "m", "(BCSIJ"
|
|
+ "Ljava/lang/Object;"
|
|
+ "Ljava/lang/Object;"
|
|
+ "Ljava/lang/Object;"
|
|
+ "Ljava/lang/Object;"
|
|
+ "Ljava/lang/Object;"
|
|
+ "Ljava/lang/Object;"
|
|
+ ")I", null, null);
|
|
mv.visitCode();
|
|
mv.visitLdcInsn(new Integer(3));
|
|
mv.visitInsn(IRETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
cw.visitEnd();
|
|
return cw.toByteArray();
|
|
}
|
|
|
|
/**
|
|
* The bytecodes for a class p/T defining a methods test() and test(11args)
|
|
* that contain an invokeExact of a particular methodHandle, I.m.
|
|
*
|
|
* Test will be passed values that may imperfectly implement I,
|
|
* and thus may in turn throw exceptions.
|
|
*
|
|
* @return
|
|
* @throws Exception
|
|
*/
|
|
public static byte[] bytesForT() throws Exception {
|
|
|
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES
|
|
| ClassWriter.COMPUTE_MAXS);
|
|
MethodVisitor mv;
|
|
|
|
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/T", null, "java/lang/Object", null);
|
|
{
|
|
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
|
mv.visitCode();
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
|
|
mv.visitInsn(RETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
// static int test(I)
|
|
{
|
|
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "(Lp/I;)I", null, null);
|
|
mv.visitCode();
|
|
mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "p/I", "m", "()I"));
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
|
|
"invokeExact", "(Lp/I;)I");
|
|
mv.visitInsn(IRETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
// static int test(I,11args)
|
|
{
|
|
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);
|
|
mv.visitCode();
|
|
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"));
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitVarInsn(ILOAD, 1);
|
|
mv.visitVarInsn(ILOAD, 2);
|
|
mv.visitVarInsn(ILOAD, 3);
|
|
mv.visitVarInsn(ILOAD, 4);
|
|
mv.visitVarInsn(LLOAD, 5);
|
|
mv.visitVarInsn(ALOAD, 7);
|
|
mv.visitVarInsn(ALOAD, 8);
|
|
mv.visitVarInsn(ALOAD, 9);
|
|
mv.visitVarInsn(ALOAD, 10);
|
|
mv.visitVarInsn(ALOAD, 11);
|
|
mv.visitVarInsn(ALOAD, 12);
|
|
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
|
|
"invokeExact", "(Lp/I;BCSIJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I");
|
|
mv.visitInsn(IRETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
cw.visitEnd();
|
|
return cw.toByteArray();
|
|
}
|
|
|
|
private static void tryAndCheckThrown(
|
|
List<Throwable> lt, byte[] dBytes, String what, Class<?> expected, String jar_name)
|
|
throws Throwable {
|
|
tryAndCheckThrown(lt, "p.D", dBytes, what, expected, jar_name);
|
|
}
|
|
|
|
private static void tryAndCheckThrown(List<Throwable> lt, String d_name, byte[] dBytes, String what, Class<?> expected, String jar_name)
|
|
throws Throwable {
|
|
|
|
System.out.println("Methodhandle invokeExact I.m() for instance of " + what);
|
|
ByteClassLoader bcl1 = new ByteClassLoader(jar_name, readJarFiles, writeJarFiles);
|
|
try {
|
|
Class<?> d1 = bcl1.loadBytes(d_name, dBytes);
|
|
Class<?> t1 = bcl1.loadBytes("p.T", bytesForT());
|
|
invokeTest(t1, d1, expected, lt);
|
|
} finally {
|
|
// Not necessary for others -- all class files are written in this call.
|
|
// (unless the VM crashes first).
|
|
bcl1.close();
|
|
}
|
|
|
|
System.out.println("Reflection invoke I.m() for instance of " + what);
|
|
ByteClassLoader bcl3 = new ByteClassLoader(jar_name, readJarFiles, false);
|
|
Class<?> d3 = bcl3.loadBytes(d_name, dBytes);
|
|
Class<?> t3 = bcl3.loadClass("p.Treflect");
|
|
invokeTest(t3, d3, expected, lt);
|
|
|
|
System.out.println("Bytecode invokeInterface I.m() for instance of " + what);
|
|
ByteClassLoader bcl2 = new ByteClassLoader(jar_name, readJarFiles, false);
|
|
Class<?> d2 = bcl2.loadBytes(d_name, dBytes);
|
|
Class<?> t2 = bcl2.loadClass("p.Tdirect");
|
|
badGoodBadGood(t2, d2, expected, lt);
|
|
}
|
|
|
|
private static void invokeTest(Class<?> t, Class<?> d, Class<?> expected, List<Throwable> lt)
|
|
throws Throwable {
|
|
try {
|
|
Method m = t.getMethod("test", p.I.class);
|
|
Object o = d.newInstance();
|
|
Object result = m.invoke(null, o);
|
|
if (expected != null) {
|
|
System.out.println("FAIL, Expected " + expected.getName()
|
|
+ " wrapped in InvocationTargetException, but nothing was thrown");
|
|
lt.add(new Error("Exception " + expected.getName() + " was not thrown"));
|
|
} else {
|
|
System.out.println("PASS, saw expected return.");
|
|
}
|
|
} catch (InvocationTargetException e) {
|
|
Throwable th = e.getCause();
|
|
th.printStackTrace(System.out);
|
|
if (expected != null) {
|
|
if (expected.isInstance(th)) {
|
|
System.out.println("PASS, saw expected exception (" + expected.getName() + ").");
|
|
} else {
|
|
System.out.println("FAIL, Expected " + expected.getName()
|
|
+ " wrapped in InvocationTargetException, saw " + th);
|
|
lt.add(th);
|
|
}
|
|
} else {
|
|
System.out.println("FAIL, expected no exception, saw " + th);
|
|
lt.add(th);
|
|
}
|
|
}
|
|
System.out.println();
|
|
}
|
|
|
|
/* Many-arg versions of above */
|
|
private static void tryAndCheckThrownMany(List<Throwable> lt, byte[] dBytes, String what, Class<?> expected)
|
|
throws Throwable {
|
|
|
|
System.out.println("Methodhandle invokeExact I.m(11params) for instance of " + what);
|
|
ByteClassLoader bcl1 = new ByteClassLoader("p.D", readJarFiles, false);
|
|
try {
|
|
Class<?> d1 = bcl1.loadBytes("p.D", dBytes);
|
|
Class<?> t1 = bcl1.loadBytes("p.T", bytesForT());
|
|
invokeTestMany(t1, d1, expected, lt);
|
|
} finally {
|
|
bcl1.close(); // Not necessary for others -- all class files are written in this call.
|
|
}
|
|
|
|
{
|
|
System.out.println("Bytecode invokeInterface I.m(11params) for instance of " + what);
|
|
ByteClassLoader bcl2 = new ByteClassLoader("pD_m_pri_imp_pI", readJarFiles, false);
|
|
Class<?> d2 = bcl2.loadBytes("p.D", dBytes);
|
|
Class<?> t2 = bcl2.loadClass("p.Tdirect");
|
|
badGoodBadGoodMany(t2, d2, expected, lt);
|
|
|
|
}
|
|
{
|
|
System.out.println("Reflection invokeInterface I.m(11params) for instance of " + what);
|
|
ByteClassLoader bcl2 = new ByteClassLoader("pD_m_pri_imp_pI", readJarFiles, false);
|
|
Class<?> d2 = bcl2.loadBytes("p.D", dBytes);
|
|
Class<?> t2 = bcl2.loadClass("p.Treflect");
|
|
invokeTestMany(t2, d2, expected, lt);
|
|
}
|
|
}
|
|
|
|
private static void invokeTestMany(Class<?> t, Class<?> d, Class<?> expected, List<Throwable> lt)
|
|
throws Throwable {
|
|
try {
|
|
Method m = t.getMethod("test", p.I.class,
|
|
Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE,
|
|
Object.class, Object.class, Object.class,
|
|
Object.class, Object.class, Object.class);
|
|
Object o = d.newInstance();
|
|
Byte b = 1;
|
|
Character c = 2;
|
|
Short s = 3;
|
|
Integer i = 4;
|
|
Long j = 5L;
|
|
Object o1 = b;
|
|
Object o2 = c;
|
|
Object o3 = s;
|
|
Object o4 = i;
|
|
Object o5 = j;
|
|
Object o6 = "6";
|
|
|
|
Object result = m.invoke(null, o, b, c, s, i, j,
|
|
o1, o2, o3, o4, o5, o6);
|
|
if (expected != null) {
|
|
System.out.println("FAIL, Expected " + expected.getName()
|
|
+ " wrapped in InvocationTargetException, but nothing was thrown");
|
|
lt.add(new Error("Exception " + expected.getName()
|
|
+ " was not thrown"));
|
|
} else {
|
|
System.out.println("PASS, saw expected return.");
|
|
}
|
|
} catch (InvocationTargetException e) {
|
|
Throwable th = e.getCause();
|
|
th.printStackTrace(System.out);
|
|
if (expected != null) {
|
|
if (expected.isInstance(th)) {
|
|
System.out.println("PASS, saw expected exception ("
|
|
+ expected.getName() + ").");
|
|
} else {
|
|
System.out.println("FAIL, Expected " + expected.getName()
|
|
+ " wrapped in InvocationTargetException, saw " + th);
|
|
lt.add(th);
|
|
}
|
|
} else {
|
|
System.out.println("FAIL, expected no exception, saw " + th);
|
|
lt.add(th);
|
|
}
|
|
}
|
|
System.out.println();
|
|
}
|
|
|
|
/**
|
|
* This tests a peculiar idiom for tickling the bug on older VMs that lack
|
|
* methodhandles. The bug (if not fixed) acts in the following way:
|
|
*
|
|
* When a broken receiver is passed to the first execution of an invokeinterface
|
|
* bytecode, the illegal access is detected before the effects of resolution are
|
|
* cached for later use, and so repeated calls with a broken receiver will always
|
|
* throw the correct error.
|
|
*
|
|
* If, however, a good receiver is passed to the invokeinterface, the effects of
|
|
* resolution will be successfully cached. A subsequent execution with a broken
|
|
* receiver will reuse the cached information, skip the detailed resolution work,
|
|
* and instead encounter a null pointer. By convention, that is the encoding for a
|
|
* missing abstract method, and an AbstractMethodError is thrown -- not the expected
|
|
* IllegalAccessError.
|
|
*
|
|
* @param t2 Test invocation class
|
|
* @param d2 Test receiver class
|
|
* @param expected expected exception type
|
|
* @param lt list of unexpected throwables seen
|
|
*/
|
|
private static void badGoodBadGood(Class<?> t2, Class<?> d2, Class<?> expected, List<Throwable> lt)
|
|
throws Throwable {
|
|
System.out.println(" Error input 1st time");
|
|
invokeTest(t2, d2, expected, lt);
|
|
System.out.println(" Good input (instance of Dok)");
|
|
invokeTest(t2, Dok.class, null, lt);
|
|
System.out.println(" Error input 2nd time");
|
|
invokeTest(t2, d2, expected, lt);
|
|
System.out.println(" Good input (instance of Dok)");
|
|
invokeTest(t2, Dok.class, null, lt);
|
|
}
|
|
|
|
private static void badGoodBadGoodMany(Class<?> t2, Class<?> d2, Class<?> expected, List<Throwable> lt)
|
|
throws Throwable {
|
|
System.out.println(" Error input 1st time");
|
|
invokeTestMany(t2, d2, expected, lt);
|
|
System.out.println(" Good input (instance of Dok)");
|
|
invokeTestMany(t2, Dok.class, null, lt);
|
|
System.out.println(" Error input 2nd time");
|
|
invokeTestMany(t2, d2, expected, lt);
|
|
System.out.println(" Good input (instance of Dok)");
|
|
invokeTestMany(t2, Dok.class, null, lt);
|
|
}
|
|
}
|