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