Path: blob/master/test/hotspot/jtreg/compiler/jsr292/methodHandleExceptions/TestAMEnotNPE.java
41154 views
/*1* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24* @test25* @bug 8025260 8016839 804617126* @summary Ensure that correct exceptions are thrown, not NullPointerException27* @modules java.base/jdk.internal.org.objectweb.asm28* @library / .29*30* @build p.*31* @run main/othervm compiler.jsr292.methodHandleExceptions.TestAMEnotNPE32* @run main/othervm -Xint compiler.jsr292.methodHandleExceptions.TestAMEnotNPE33* @run main/othervm -Xcomp compiler.jsr292.methodHandleExceptions.TestAMEnotNPE34*/3536// Since this test was written the specification for interface method selection has been37// revised (JEP 181 - Nestmates) so that private methods are never selected, as they never38// override any inherited method. So where a private method was previously selected39// and then resulted in IllegalAccessError, the private method is skipped and the invocation40// will either succeed or fail based on what other implementations are found in the inheritance41// hierarchy. This is explained for each test below.4243package compiler.jsr292.methodHandleExceptions;4445import p.Dok;46import jdk.internal.org.objectweb.asm.ClassWriter;47import jdk.internal.org.objectweb.asm.Handle;48import jdk.internal.org.objectweb.asm.MethodVisitor;49import jdk.internal.org.objectweb.asm.Opcodes;5051import java.lang.reflect.InvocationTargetException;52import java.lang.reflect.Method;53import java.util.ArrayList;54import java.util.List;5556import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;57import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;58import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;59import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;60import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;61import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;62import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;63import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;64import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;65import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;66import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;67import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;6869public class TestAMEnotNPE {7071static boolean writeJarFiles = false;72static boolean readJarFiles = false;7374/**75* Optional command line parameter (any case-insensitive prefix of)76* "writejarfiles" or "readjarfiles".77*78* "Writejarfiles" creates a jar file for each different set of tested classes.79* "Readjarfiles" causes the classloader to use the copies of the classes80* found in the corresponding jar files.81*82* Jarfilenames look something like pD_ext_pF (p.D extends p.F)83* and qD_m_pp_imp_pI (q.D with package-private m implements p.I)84*85*/86public static void main(String args[]) throws Throwable {87ArrayList<Throwable> lt = new ArrayList<Throwable>();8889if (args.length > 0) {90String a0 = args[0].toLowerCase();91if (a0.length() > 0) {92writeJarFiles = ("writejarfiles").startsWith(a0);93readJarFiles = ("readjarfiles").startsWith(a0);94}95if (!(writeJarFiles || readJarFiles)) {96throw new Error("Command line parameter (if any) should be prefix of writeJarFiles or readJarFiles");97}98}99100System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m, p.D extends p.F, p.F.m FINAL");101System.out.println(" - should invoke p.F.m as private p.D.m is skipped for selection");102tryAndCheckThrown(lt, bytesForDprivateSubWhat("p/F"),103"p.D extends p.F (p.F implements p.I, FINAL public m), private m",104null /* should succeed */, "pD_ext_pF");105System.out.println();106107System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m, p.D extends p.E");108System.out.println(" - should invoke p.E.m as private p.D.m is skipped for selection");109tryAndCheckThrown(lt, bytesForDprivateSubWhat("p/E"),110"p.D extends p.E (p.E implements p.I, public m), private m",111null /* should succeed */, "pD_ext_pE");112113System.out.println("TRYING p.D.m ABSTRACT interface-invoked as p.I.m");114tryAndCheckThrown(lt, bytesForD(),115"D extends abstract C, no m",116AbstractMethodError.class, "pD_ext_pC");117118System.out.println("TRYING q.D.m PACKAGE interface-invoked as p.I.m");119tryAndCheckThrown(lt, "q.D", bytesForDsomeAccess("q/D", 0),120"q.D implements p.I, protected m",121IllegalAccessError.class, "qD_m_pp_imp_pI");122123// Note jar file name is used in the plural-arg case.124System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m");125System.out.println(" - should invoke p.I.m as private p.D.m is skipped for selection");126tryAndCheckThrown(lt, bytesForDsomeAccess("p/D", ACC_PRIVATE),127"p.D implements p.I, private m",128null /* should succeed */, "pD_m_pri_imp_pI");129130// Plural-arg test.131System.out.println("TRYING p.D.m PRIVATE MANY ARG interface-invoked as p.I.m");132System.out.println(" - should invoke p.I.m as private p.D.m is skipped for selection");133tryAndCheckThrownMany(lt, bytesForDsomeAccess("p/D", ACC_PRIVATE),134"p.D implements p.I, private m", null /* should succeed */);135136if (lt.size() > 0) {137System.out.flush();138Thread.sleep(250); // This de-interleaves output and error in Netbeans, sigh.139for (Throwable th : lt)140System.err.println(th);141throw new Error("Test failed, there were " + lt.size() + " failures listed above");142} else {143System.out.println("ALL PASS, HOORAY!");144}145}146147/**148* The bytes for D, a NOT abstract class extending abstract class C without149* supplying an implementation for abstract method m. There is a default150* method in the interface I, but it should lose to the abstract class.151*152* @return153* @throws Exception154*/155public static byte[] bytesForD() throws Exception {156157ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES158| ClassWriter.COMPUTE_MAXS);159MethodVisitor mv;160161cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/D", null, "p/C", null);162163{164mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);165mv.visitCode();166mv.visitVarInsn(ALOAD, 0);167mv.visitMethodInsn(INVOKESPECIAL, "p/C", "<init>", "()V");168mv.visitInsn(RETURN);169mv.visitMaxs(0, 0);170mv.visitEnd();171}172cw.visitEnd();173174return cw.toByteArray();175}176177/**178* The bytes for D, implements I, does not extend C, declares m()I with179* access method_acc.180*181* @param d_name Name of class defined182* @param method_acc Accessibility of that class's method m.183* @return184* @throws Exception185*/186public static byte[] bytesForDsomeAccess(String d_name, int method_acc) throws Exception {187return bytesForSomeDsubSomethingSomeAccess(d_name, "java/lang/Object", method_acc);188}189190/**191* The bytes for D implements I, extends some class, declares m()I as192* private.193*194* Invokeinterface of I.m applied to this D should throw IllegalAccessError195*196* @param sub_what The name of the class that D will extend.197* @return198* @throws Exception199*/200public static byte[] bytesForDprivateSubWhat(String sub_what) throws Exception {201return bytesForSomeDsubSomethingSomeAccess("p/D", sub_what, ACC_PRIVATE);202}203204/**205* Returns the bytes for a class with name d_name (presumably "D" in some206* package), extending some class with name sub_what, implementing p.I,207* and defining two methods m() and m(11args) with access method_acc.208*209* @param d_name Name of class that is defined210* @param sub_what Name of class that it extends211* @param method_acc Accessibility of method(s) m in defined class.212* @return213* @throws Exception214*/215public static byte[] bytesForSomeDsubSomethingSomeAccess216(String d_name, String sub_what, int method_acc)217throws Exception {218219ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES220| ClassWriter.COMPUTE_MAXS);221MethodVisitor mv;222String[] interfaces = {"p/I"};223224cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, d_name, null, sub_what, interfaces);225{226mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);227mv.visitCode();228mv.visitVarInsn(ALOAD, 0);229mv.visitMethodInsn(INVOKESPECIAL, sub_what, "<init>", "()V");230mv.visitInsn(RETURN);231mv.visitMaxs(0, 0);232mv.visitEnd();233}234// int m() {return 3;}235{236mv = cw.visitMethod(method_acc, "m", "()I", null, null);237mv.visitCode();238mv.visitLdcInsn(new Integer(3));239mv.visitInsn(IRETURN);240mv.visitMaxs(0, 0);241mv.visitEnd();242}243// int m(11args) {return 3;}244{245mv = cw.visitMethod(method_acc, "m", "(BCSIJ"246+ "Ljava/lang/Object;"247+ "Ljava/lang/Object;"248+ "Ljava/lang/Object;"249+ "Ljava/lang/Object;"250+ "Ljava/lang/Object;"251+ "Ljava/lang/Object;"252+ ")I", null, null);253mv.visitCode();254mv.visitLdcInsn(new Integer(3));255mv.visitInsn(IRETURN);256mv.visitMaxs(0, 0);257mv.visitEnd();258}259cw.visitEnd();260return cw.toByteArray();261}262263/**264* The bytecodes for a class p/T defining a methods test() and test(11args)265* that contain an invokeExact of a particular methodHandle, I.m.266*267* Test will be passed values that may imperfectly implement I,268* and thus may in turn throw exceptions.269*270* @return271* @throws Exception272*/273public static byte[] bytesForT() throws Exception {274275ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES276| ClassWriter.COMPUTE_MAXS);277MethodVisitor mv;278279cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/T", null, "java/lang/Object", null);280{281mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);282mv.visitCode();283mv.visitVarInsn(ALOAD, 0);284mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");285mv.visitInsn(RETURN);286mv.visitMaxs(0, 0);287mv.visitEnd();288}289// static int test(I)290{291mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "(Lp/I;)I", null, null);292mv.visitCode();293mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "p/I", "m", "()I"));294mv.visitVarInsn(ALOAD, 0);295mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",296"invokeExact", "(Lp/I;)I");297mv.visitInsn(IRETURN);298mv.visitMaxs(0, 0);299mv.visitEnd();300}301// static int test(I,11args)302{303mv = 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);304mv.visitCode();305mv.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"));306mv.visitVarInsn(ALOAD, 0);307mv.visitVarInsn(ILOAD, 1);308mv.visitVarInsn(ILOAD, 2);309mv.visitVarInsn(ILOAD, 3);310mv.visitVarInsn(ILOAD, 4);311mv.visitVarInsn(LLOAD, 5);312mv.visitVarInsn(ALOAD, 7);313mv.visitVarInsn(ALOAD, 8);314mv.visitVarInsn(ALOAD, 9);315mv.visitVarInsn(ALOAD, 10);316mv.visitVarInsn(ALOAD, 11);317mv.visitVarInsn(ALOAD, 12);318mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",319"invokeExact", "(Lp/I;BCSIJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I");320mv.visitInsn(IRETURN);321mv.visitMaxs(0, 0);322mv.visitEnd();323}324cw.visitEnd();325return cw.toByteArray();326}327328private static void tryAndCheckThrown(329List<Throwable> lt, byte[] dBytes, String what, Class<?> expected, String jar_name)330throws Throwable {331tryAndCheckThrown(lt, "p.D", dBytes, what, expected, jar_name);332}333334private static void tryAndCheckThrown(List<Throwable> lt, String d_name, byte[] dBytes, String what, Class<?> expected, String jar_name)335throws Throwable {336337System.out.println("Methodhandle invokeExact I.m() for instance of " + what);338ByteClassLoader bcl1 = new ByteClassLoader(jar_name, readJarFiles, writeJarFiles);339try {340Class<?> d1 = bcl1.loadBytes(d_name, dBytes);341Class<?> t1 = bcl1.loadBytes("p.T", bytesForT());342invokeTest(t1, d1, expected, lt);343} finally {344// Not necessary for others -- all class files are written in this call.345// (unless the VM crashes first).346bcl1.close();347}348349System.out.println("Reflection invoke I.m() for instance of " + what);350ByteClassLoader bcl3 = new ByteClassLoader(jar_name, readJarFiles, false);351Class<?> d3 = bcl3.loadBytes(d_name, dBytes);352Class<?> t3 = bcl3.loadClass("p.Treflect");353invokeTest(t3, d3, expected, lt);354355System.out.println("Bytecode invokeInterface I.m() for instance of " + what);356ByteClassLoader bcl2 = new ByteClassLoader(jar_name, readJarFiles, false);357Class<?> d2 = bcl2.loadBytes(d_name, dBytes);358Class<?> t2 = bcl2.loadClass("p.Tdirect");359badGoodBadGood(t2, d2, expected, lt);360}361362private static void invokeTest(Class<?> t, Class<?> d, Class<?> expected, List<Throwable> lt)363throws Throwable {364try {365Method m = t.getMethod("test", p.I.class);366Object o = d.newInstance();367Object result = m.invoke(null, o);368if (expected != null) {369System.out.println("FAIL, Expected " + expected.getName()370+ " wrapped in InvocationTargetException, but nothing was thrown");371lt.add(new Error("Exception " + expected.getName() + " was not thrown"));372} else {373System.out.println("PASS, saw expected return.");374}375} catch (InvocationTargetException e) {376Throwable th = e.getCause();377th.printStackTrace(System.out);378if (expected != null) {379if (expected.isInstance(th)) {380System.out.println("PASS, saw expected exception (" + expected.getName() + ").");381} else {382System.out.println("FAIL, Expected " + expected.getName()383+ " wrapped in InvocationTargetException, saw " + th);384lt.add(th);385}386} else {387System.out.println("FAIL, expected no exception, saw " + th);388lt.add(th);389}390}391System.out.println();392}393394/* Many-arg versions of above */395private static void tryAndCheckThrownMany(List<Throwable> lt, byte[] dBytes, String what, Class<?> expected)396throws Throwable {397398System.out.println("Methodhandle invokeExact I.m(11params) for instance of " + what);399ByteClassLoader bcl1 = new ByteClassLoader("p.D", readJarFiles, false);400try {401Class<?> d1 = bcl1.loadBytes("p.D", dBytes);402Class<?> t1 = bcl1.loadBytes("p.T", bytesForT());403invokeTestMany(t1, d1, expected, lt);404} finally {405bcl1.close(); // Not necessary for others -- all class files are written in this call.406}407408{409System.out.println("Bytecode invokeInterface I.m(11params) for instance of " + what);410ByteClassLoader bcl2 = new ByteClassLoader("pD_m_pri_imp_pI", readJarFiles, false);411Class<?> d2 = bcl2.loadBytes("p.D", dBytes);412Class<?> t2 = bcl2.loadClass("p.Tdirect");413badGoodBadGoodMany(t2, d2, expected, lt);414415}416{417System.out.println("Reflection invokeInterface I.m(11params) for instance of " + what);418ByteClassLoader bcl2 = new ByteClassLoader("pD_m_pri_imp_pI", readJarFiles, false);419Class<?> d2 = bcl2.loadBytes("p.D", dBytes);420Class<?> t2 = bcl2.loadClass("p.Treflect");421invokeTestMany(t2, d2, expected, lt);422}423}424425private static void invokeTestMany(Class<?> t, Class<?> d, Class<?> expected, List<Throwable> lt)426throws Throwable {427try {428Method m = t.getMethod("test", p.I.class,429Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE,430Object.class, Object.class, Object.class,431Object.class, Object.class, Object.class);432Object o = d.newInstance();433Byte b = 1;434Character c = 2;435Short s = 3;436Integer i = 4;437Long j = 5L;438Object o1 = b;439Object o2 = c;440Object o3 = s;441Object o4 = i;442Object o5 = j;443Object o6 = "6";444445Object result = m.invoke(null, o, b, c, s, i, j,446o1, o2, o3, o4, o5, o6);447if (expected != null) {448System.out.println("FAIL, Expected " + expected.getName()449+ " wrapped in InvocationTargetException, but nothing was thrown");450lt.add(new Error("Exception " + expected.getName()451+ " was not thrown"));452} else {453System.out.println("PASS, saw expected return.");454}455} catch (InvocationTargetException e) {456Throwable th = e.getCause();457th.printStackTrace(System.out);458if (expected != null) {459if (expected.isInstance(th)) {460System.out.println("PASS, saw expected exception ("461+ expected.getName() + ").");462} else {463System.out.println("FAIL, Expected " + expected.getName()464+ " wrapped in InvocationTargetException, saw " + th);465lt.add(th);466}467} else {468System.out.println("FAIL, expected no exception, saw " + th);469lt.add(th);470}471}472System.out.println();473}474475/**476* This tests a peculiar idiom for tickling the bug on older VMs that lack477* methodhandles. The bug (if not fixed) acts in the following way:478*479* When a broken receiver is passed to the first execution of an invokeinterface480* bytecode, the illegal access is detected before the effects of resolution are481* cached for later use, and so repeated calls with a broken receiver will always482* throw the correct error.483*484* If, however, a good receiver is passed to the invokeinterface, the effects of485* resolution will be successfully cached. A subsequent execution with a broken486* receiver will reuse the cached information, skip the detailed resolution work,487* and instead encounter a null pointer. By convention, that is the encoding for a488* missing abstract method, and an AbstractMethodError is thrown -- not the expected489* IllegalAccessError.490*491* @param t2 Test invocation class492* @param d2 Test receiver class493* @param expected expected exception type494* @param lt list of unexpected throwables seen495*/496private static void badGoodBadGood(Class<?> t2, Class<?> d2, Class<?> expected, List<Throwable> lt)497throws Throwable {498System.out.println(" Error input 1st time");499invokeTest(t2, d2, expected, lt);500System.out.println(" Good input (instance of Dok)");501invokeTest(t2, Dok.class, null, lt);502System.out.println(" Error input 2nd time");503invokeTest(t2, d2, expected, lt);504System.out.println(" Good input (instance of Dok)");505invokeTest(t2, Dok.class, null, lt);506}507508private static void badGoodBadGoodMany(Class<?> t2, Class<?> d2, Class<?> expected, List<Throwable> lt)509throws Throwable {510System.out.println(" Error input 1st time");511invokeTestMany(t2, d2, expected, lt);512System.out.println(" Good input (instance of Dok)");513invokeTestMany(t2, Dok.class, null, lt);514System.out.println(" Error input 2nd time");515invokeTestMany(t2, d2, expected, lt);516System.out.println(" Good input (instance of Dok)");517invokeTestMany(t2, Dok.class, null, lt);518}519}520521522