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