test/compiler/jsr292/methodHandleExceptions/TestAMEnotNPE.java

Tue, 26 Nov 2013 18:16:04 -0500

author
drchase
date
Tue, 26 Nov 2013 18:16:04 -0500
changeset 6134
9d15b81d5d1b
parent 5800
dc261f466b6d
child 6876
710a3c8b516e
permissions
-rw-r--r--

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

     1 /*
     2  * Copyright (c) 2013, 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;
    34 /**
    35  * @test @bug 8025260 8016839
    36  * @summary Ensure that AbstractMethodError and IllegalAccessError are thrown appropriately, not NullPointerException
    37  *
    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
    39  *
    40  * @run main/othervm TestAMEnotNPE
    41  * @run main/othervm -Xint TestAMEnotNPE
    42  * @run main/othervm -Xcomp TestAMEnotNPE
    43  */
    44 public class TestAMEnotNPE implements Opcodes {
    46     static boolean writeJarFiles = false;
    47     static boolean readJarFiles = false;
    49     /**
    50      * Optional command line parameter (any case-insensitive prefix of)
    51      * "writejarfiles" or "readjarfiles".
    52      *
    53      * "Writejarfiles" creates a jar file for each different set of tested classes.
    54      * "Readjarfiles" causes the classloader to use the copies of the classes
    55      * found in the corresponding jar files.
    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>();
    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         }
    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();
    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");
    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");
    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");
   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");
   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);
   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     }
   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      *
   129      * @return
   130      * @throws Exception
   131      */
   132     public static byte[] bytesForD() throws Exception {
   134         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES
   135                 | ClassWriter.COMPUTE_MAXS);
   136         MethodVisitor mv;
   138         cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/D", null, "p/C", null);
   140         {
   141             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
   142             mv.visitCode();
   143             mv.visitVarInsn(ALOAD, 0);
   144             mv.visitMethodInsn(INVOKESPECIAL, "p/C", "<init>", "()V");
   145             mv.visitInsn(RETURN);
   146             mv.visitMaxs(0, 0);
   147             mv.visitEnd();
   148         }
   149         cw.visitEnd();
   151         return cw.toByteArray();
   152     }
   154     /**
   155      * The bytes for D, implements I, does not extend C, declares m()I with
   156      * access method_acc.
   157      *
   158      * @param d_name Name of class defined
   159      * @param method_acc Accessibility of that class's method m.
   160      * @return
   161      * @throws Exception
   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     }
   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     }
   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 {
   196         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES
   197                 | ClassWriter.COMPUTE_MAXS);
   198         MethodVisitor mv;
   199         String[] interfaces = {"p/I"};
   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     }
   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      */
   250     public static byte[] bytesForT() throws Exception {
   252         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES
   253                 | ClassWriter.COMPUTE_MAXS);
   254         MethodVisitor mv;
   256         cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/T", null, "java/lang/Object", null);
   257         {
   258             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
   259             mv.visitCode();
   260             mv.visitVarInsn(ALOAD, 0);
   261             mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
   262             mv.visitInsn(RETURN);
   263             mv.visitMaxs(0, 0);
   264             mv.visitEnd();
   265         }
   266         // static int test(I)
   267         {
   268             mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "(Lp/I;)I", null, null);
   269             mv.visitCode();
   270             mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "p/I", "m", "()I"));
   271             mv.visitVarInsn(ALOAD, 0);
   272             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
   273                     "invokeExact", "(Lp/I;)I");
   274             mv.visitInsn(IRETURN);
   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);
   299             mv.visitEnd();
   300         }
   301         cw.visitEnd();
   302         return cw.toByteArray();
   303     }
   305     private static void tryAndCheckThrown(
   306             List<Throwable> lt, byte[] dBytes, String what, Class<?> expected, String jar_name)
   307             throws Throwable {
   308         tryAndCheckThrown(lt, "p.D", dBytes, what, expected, jar_name);
   309     }
   311     private static void tryAndCheckThrown(List<Throwable> lt, String d_name, byte[] dBytes, String what, Class<?> expected, String jar_name)
   312             throws Throwable {
   314         System.out.println("Methodhandle invokeExact I.m() for instance of " + what);
   315         ByteClassLoader bcl1 = new ByteClassLoader(jar_name, readJarFiles, writeJarFiles);
   316         try {
   317             Class<?> d1 = bcl1.loadBytes(d_name, dBytes);
   318             Class<?> t1 = bcl1.loadBytes("p.T", bytesForT());
   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         }
   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);
   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     }
   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             }
   352         } catch (InvocationTargetException e) {
   353             Throwable th = e.getCause();
   354             th.printStackTrace(System.out);
   355             if (expected != null) {
   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                 }
   363             } else {
   364                 System.out.println("FAIL, expected no exception, saw " + th);
   365                 lt.add(th);
   366             }
   367         }
   368         System.out.println();
   369     }
   371     /* Many-arg versions of above */
   372     private static void tryAndCheckThrownMany(List<Throwable> lt, byte[] dBytes, String what, Class<?> expected)
   373             throws Throwable {
   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         }
   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);
   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     }
   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";
   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     }
   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     }
   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);
   495     }
   496 }

mercurial