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