1/*
2 * Copyright (c) 2015, 2016, 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 8051045 8166974
27 * @summary Test exceptions from invokedynamic and the bootstrap method
28 * @modules java.base/jdk.internal.org.objectweb.asm
29 * @run main BootstrapMethodErrorTest
30 */
31
32import jdk.internal.org.objectweb.asm.ClassWriter;
33import jdk.internal.org.objectweb.asm.Handle;
34import jdk.internal.org.objectweb.asm.MethodVisitor;
35import jdk.internal.org.objectweb.asm.Opcodes;
36
37import java.lang.invoke.CallSite;
38import java.lang.invoke.ConstantCallSite;
39import java.lang.invoke.MethodHandle;
40import java.lang.invoke.MethodHandles;
41import java.lang.invoke.MethodType;
42import java.lang.invoke.WrongMethodTypeException;
43import java.lang.reflect.InvocationTargetException;
44import java.util.List;
45
46public class BootstrapMethodErrorTest {
47
48    static abstract class IndyClassloader extends ClassLoader implements Opcodes {
49
50        public IndyClassloader() {
51            super(BootstrapMethodErrorTest.class.getClassLoader());
52        }
53
54        @Override
55        public Class findClass(String name) throws ClassNotFoundException {
56            byte[] b;
57            try {
58                b = loadClassData(name);
59            }
60            catch (Throwable th) {
61                throw new ClassNotFoundException("Loading error", th);
62            }
63            return defineClass(name, b, 0, b.length);
64        }
65
66        static final String BOOTSTRAP_METHOD_CLASS_NAME = "C";
67
68        static final String BOOTSTRAP_METHOD_NAME = "bsm";
69
70        static final String INDY_CALLER_CLASS_NAME = "Exec";
71
72        static final String BOOTSTRAP_METHOD_DESC = MethodType.methodType(
73                Object.class, MethodHandles.Lookup.class, String.class, MethodType.class).
74                toMethodDescriptorString();
75
76        private byte[] loadClassData(String name) throws Exception {
77            ClassWriter cw = new ClassWriter(
78                    ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
79            if (name.equals(BOOTSTRAP_METHOD_CLASS_NAME)) {
80                defineIndyBootstrapMethodClass(cw);
81                return cw.toByteArray();
82            }
83            else if (name.equals("Exec")) {
84                defineIndyCallingClass(cw);
85                return cw.toByteArray();
86            }
87            return null;
88        }
89
90        void defineIndyCallingClass(ClassWriter cw) {
91            cw.visit(52, ACC_SUPER | ACC_PUBLIC, INDY_CALLER_CLASS_NAME, null, "java/lang/Object", null);
92            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invoke", "()V", null, null);
93            mv.visitCode();
94            Handle h = new Handle(H_INVOKESTATIC,
95                                  BOOTSTRAP_METHOD_CLASS_NAME, BOOTSTRAP_METHOD_NAME,
96                                  BOOTSTRAP_METHOD_DESC, false);
97            mv.visitInvokeDynamicInsn(BOOTSTRAP_METHOD_CLASS_NAME, "()V", h);
98            mv.visitInsn(RETURN);
99            mv.visitMaxs(0, 0);
100            mv.visitEnd();
101            cw.visitEnd();
102        }
103
104        void defineIndyBootstrapMethodClass(ClassWriter cw) {
105            cw.visit(52, ACC_SUPER | ACC_PUBLIC,
106                     BOOTSTRAP_METHOD_CLASS_NAME, null, "java/lang/Object", null);
107            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC,
108                                              BOOTSTRAP_METHOD_NAME, BOOTSTRAP_METHOD_DESC, null, null);
109            mv.visitCode();
110            defineIndyBootstrapMethodBody(mv);
111            mv.visitMaxs(0, 0);
112            mv.visitEnd();
113        }
114
115        void defineIndyBootstrapMethodBody(MethodVisitor mv) {
116            mv.visitInsn(ACONST_NULL);
117            mv.visitInsn(ARETURN);
118        }
119
120        void invoke() throws Exception {
121            Class.forName(BOOTSTRAP_METHOD_CLASS_NAME, true, this);
122            Class<?> exec = Class.forName(INDY_CALLER_CLASS_NAME, true, this);
123            exec.getMethod("invoke").invoke(null);
124        }
125
126        void test() throws Exception {
127            Class.forName(BOOTSTRAP_METHOD_CLASS_NAME, true, this);
128            Class<?> exec = Class.forName(INDY_CALLER_CLASS_NAME, true, this);
129            try {
130                exec.getMethod("invoke").invoke(null);
131                throw new RuntimeException("Expected InvocationTargetException but no exception at all was thrown");
132            } catch (InvocationTargetException e) {
133                Throwable t = e.getCause();
134                for (Class<? extends Throwable> etc : expectedThrowableClasses()) {
135                    if (!etc.isInstance(t)) {
136                        throw new RuntimeException(
137                                "Expected " + etc.getName() + " but got another exception: "
138                                + t.getClass().getName(),
139                                t);
140                    }
141                    t = t.getCause();
142                }
143            }
144        }
145
146        abstract List<Class<? extends Throwable>> expectedThrowableClasses();
147    }
148
149    // Methods called by a bootstrap method
150
151    public static CallSite getCallSite() {
152        try {
153            MethodHandle mh = MethodHandles.lookup().findStatic(
154                    BootstrapMethodErrorTest.class,
155                    "target",
156                    MethodType.methodType(Object.class, Object.class));
157            return new ConstantCallSite(mh);
158        } catch (Exception e) {
159            throw new RuntimeException(e);
160        }
161    }
162    public static Object target(Object o) {
163        return null;
164    }
165
166    static class TestThrowable extends Throwable {}
167    public static void throwsTestThrowable() throws Throwable {
168        throw new TestThrowable();
169    }
170
171    static class TestError extends Error {}
172    public static void throwsTestError() {
173        throw new TestError();
174    }
175
176    static class TestRuntimeException extends RuntimeException {}
177    public static void throwsTestRuntimeException() {
178        throw new TestRuntimeException();
179    }
180
181    static class TestCheckedException extends Exception {}
182    public static void throwsTestCheckedException() throws TestCheckedException {
183        throw new TestCheckedException();
184    }
185
186
187    // Test classes
188
189    static class InaccessibleBootstrapMethod extends IndyClassloader {
190
191        void defineIndyBootstrapMethodClass(ClassWriter cw) {
192            cw.visit(52, ACC_SUPER | ACC_PUBLIC,
193                     BOOTSTRAP_METHOD_CLASS_NAME, null, "java/lang/Object", null);
194            // Bootstrap method is declared to be private
195            MethodVisitor mv = cw.visitMethod(ACC_PRIVATE | ACC_STATIC,
196                                              BOOTSTRAP_METHOD_NAME, BOOTSTRAP_METHOD_DESC, null, null);
197            mv.visitCode();
198            defineIndyBootstrapMethodBody(mv);
199            mv.visitMaxs(0, 0);
200            mv.visitEnd();
201        }
202
203        @Override
204        List<Class<? extends Throwable>> expectedThrowableClasses() {
205            return List.of(IllegalAccessError.class);
206        }
207    }
208
209    static class BootstrapMethodDoesNotReturnCallSite extends IndyClassloader {
210
211        void defineIndyBootstrapMethodBody(MethodVisitor mv) {
212            // return null from the bootstrap method,
213            // which cannot be cast to CallSite
214            mv.visitInsn(ACONST_NULL);
215            mv.visitInsn(ARETURN);
216        }
217
218        @Override
219        List<Class<? extends Throwable>> expectedThrowableClasses() {
220            return List.of(BootstrapMethodError.class, ClassCastException.class);
221        }
222    }
223
224    static class BootstrapMethodCallSiteHasWrongTarget extends IndyClassloader {
225
226        @Override
227        void defineIndyBootstrapMethodBody(MethodVisitor mv) {
228            // Invoke the method BootstrapMethodErrorTest.getCallSite to obtain
229            // a CallSite instance whose target is different from that of
230            // the indy call site
231            mv.visitMethodInsn(INVOKESTATIC, "BootstrapMethodErrorTest",
232                               "getCallSite", "()Ljava/lang/invoke/CallSite;", false);
233            mv.visitInsn(ARETURN);
234        }
235
236        @Override
237        List<Class<? extends Throwable>> expectedThrowableClasses() {
238            return List.of(BootstrapMethodError.class, WrongMethodTypeException.class);
239        }
240    }
241
242    abstract static class BootstrapMethodThrows extends IndyClassloader {
243        final String methodName;
244
245        public BootstrapMethodThrows(Class<? extends Throwable> t) {
246            this.methodName = "throws" + t.getSimpleName();
247        }
248
249        @Override
250        void defineIndyBootstrapMethodBody(MethodVisitor mv) {
251            // Invoke the method whose name is methodName which will throw
252            // an exception
253            mv.visitMethodInsn(INVOKESTATIC, "BootstrapMethodErrorTest",
254                               methodName, "()V", false);
255            mv.visitInsn(ACONST_NULL);
256            mv.visitInsn(ARETURN);
257        }
258    }
259
260    static class BootstrapMethodThrowsThrowable extends BootstrapMethodThrows {
261
262        public BootstrapMethodThrowsThrowable() {
263            super(TestThrowable.class);
264        }
265
266        @Override
267        List<Class<? extends Throwable>> expectedThrowableClasses() {
268            return List.of(BootstrapMethodError.class, TestThrowable.class);
269        }
270    }
271
272    static class BootstrapMethodThrowsError extends BootstrapMethodThrows {
273
274        public BootstrapMethodThrowsError() {
275            super(TestError.class);
276        }
277
278        @Override
279        List<Class<? extends Throwable>> expectedThrowableClasses() {
280            return List.of(TestError.class);
281        }
282    }
283
284    static class BootstrapMethodThrowsRuntimeException extends BootstrapMethodThrows {
285
286        public BootstrapMethodThrowsRuntimeException() {
287            super(TestRuntimeException.class);
288        }
289
290        @Override
291        List<Class<? extends Throwable>> expectedThrowableClasses() {
292            return List.of(BootstrapMethodError.class, TestRuntimeException.class);
293        }
294    }
295
296    static class BootstrapMethodThrowsCheckedException extends BootstrapMethodThrows {
297
298        public BootstrapMethodThrowsCheckedException() {
299            super(TestCheckedException.class);
300        }
301
302        @Override
303        List<Class<? extends Throwable>> expectedThrowableClasses() {
304            return List.of(BootstrapMethodError.class, TestCheckedException.class);
305        }
306    }
307
308
309    public static void main(String[] args) throws Exception {
310        new InaccessibleBootstrapMethod().test();
311        new BootstrapMethodDoesNotReturnCallSite().test();
312        new BootstrapMethodCallSiteHasWrongTarget().test();
313        new BootstrapMethodThrowsThrowable().test();
314        new BootstrapMethodThrowsError().test();
315        new BootstrapMethodThrowsRuntimeException().test();
316        new BootstrapMethodThrowsCheckedException().test();
317    }
318}
319