InterfaceMethodHandleTest.java revision 12772:fbb9c8026495
1264741Sjmmv/*
2264741Sjmmv * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3289172Sngie * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4264741Sjmmv *
5264741Sjmmv * This code is free software; you can redistribute it and/or modify it
6264741Sjmmv * 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 */
23package org.graalvm.compiler.core.test;
24
25import java.lang.invoke.MethodHandle;
26import java.lang.invoke.MethodHandles;
27import java.lang.invoke.MethodType;
28
29import org.graalvm.compiler.code.CompilationResult;
30import org.graalvm.compiler.test.ExportingClassLoader;
31import org.junit.Test;
32import org.objectweb.asm.ClassWriter;
33import org.objectweb.asm.Label;
34import org.objectweb.asm.MethodVisitor;
35import org.objectweb.asm.Opcodes;
36
37import jdk.vm.ci.code.InstalledCode;
38import jdk.vm.ci.meta.ResolvedJavaMethod;
39
40public final class InterfaceMethodHandleTest extends GraalCompilerTest implements Opcodes {
41    private static final MethodHandle INTERFACE_HANDLE_M;
42    private static final MethodHandle INTERFACE_HANDLE_M2;
43
44    public interface I {
45        int m();
46
47        int m2(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j);
48    }
49
50    static class A implements I {
51        @Override
52        public int m() {
53            return 0;
54        }
55
56        @Override
57        public int m2(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
58            return 1;
59        }
60
61    }
62
63    static class M2Thrower implements I {
64        @Override
65        public int m() {
66            return 0;
67        }
68
69        @Override
70        public int m2(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
71            throw new InternalError();
72        }
73
74    }
75
76    static {
77        try {
78            MethodType type = MethodType.fromMethodDescriptorString("()I", I.class.getClassLoader());
79            INTERFACE_HANDLE_M = MethodHandles.lookup().findVirtual(I.class, "m", type);
80            MethodType type2 = MethodType.fromMethodDescriptorString("(IIIIIIIIII)I", I.class.getClassLoader());
81            INTERFACE_HANDLE_M2 = MethodHandles.lookup().findVirtual(I.class, "m2", type2);
82        } catch (IllegalAccessException | NoSuchMethodException e) {
83            throw new RuntimeException("unable to initialize method handle", e);
84        }
85    }
86
87    public static Object invokeInterfaceHandle(I o) throws Throwable {
88        return (int) INTERFACE_HANDLE_M.invokeExact(o);
89    }
90
91    @Test
92    public void testInvokeInterface01() {
93        test("invokeInterfaceHandle", new A());
94
95    }
96
97    @Test
98    public void testInvokeInterface02() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
99        test("invokeInterfaceHandle", loader.findClass(NAME).newInstance());
100    }
101
102    public static Object invokeInterfaceHandle2(I o, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) throws Throwable {
103        return (int) INTERFACE_HANDLE_M2.invokeExact(o, a, b, c, d, e, f, g, h, i, j);
104    }
105
106    @Override
107    protected InstalledCode addMethod(ResolvedJavaMethod method, CompilationResult compResult) {
108        if (method.getDeclaringClass().equals(getMetaAccess().lookupJavaType(M2Thrower.class))) {
109            // Make sure M2Thrower.m2 is invoked from normal code
110            return getBackend().createDefaultInstalledCode(method, compResult);
111        }
112        return super.addMethod(method, compResult);
113    }
114
115    /**
116     * Try to exercise a mixed calling sequence with regular JIT code calling a method handle that
117     * can't be inlined with an implementation compiled by Graal that throws an exception.
118     */
119    @Test
120    public void testInvokeInterface03() throws Throwable {
121        A goodInstance = new A();
122        I badInstance = new M2Thrower();
123        getCode(getMetaAccess().lookupJavaMethod(getMethod(M2Thrower.class, "m2")));
124        for (int x = 0; x < 1000; x++) {
125            final int limit = 20000;
126            for (int i = 0; i <= limit; i++) {
127                try {
128                    invokeInterfaceHandle2(i < limit - 1 ? goodInstance : badInstance, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
129                } catch (InternalError e) {
130
131                }
132            }
133        }
134    }
135
136    private static final String BASENAME = InterfaceMethodHandleTest.class.getName();
137    private static final String NAME = BASENAME + "_B";
138    private AsmLoader loader = new AsmLoader(UnbalancedMonitorsTest.class.getClassLoader());
139
140    /**
141     * Construct a type which claims to implement {@link I} but with incorrect access on {@link I#m}
142     * so that an exception must be thrown.
143     */
144    public static byte[] bytesForB() {
145
146        ClassWriter cw = new ClassWriter(0);
147        MethodVisitor mv;
148        String jvmName = NAME.replace('.', '/');
149        cw.visit(52, ACC_SUPER | ACC_PUBLIC, jvmName, null, "java/lang/Object", new String[]{BASENAME.replace('.', '/') + "$I"});
150
151        mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
152        mv.visitCode();
153        Label l0 = new Label();
154        mv.visitLabel(l0);
155        mv.visitVarInsn(ALOAD, 0);
156        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
157        mv.visitInsn(RETURN);
158        Label l1 = new Label();
159        mv.visitLabel(l1);
160        mv.visitMaxs(1, 1);
161        mv.visitEnd();
162
163        mv = cw.visitMethod(ACC_PRIVATE, "m", "()I", null, null);
164        mv.visitCode();
165        l0 = new Label();
166        mv.visitLabel(l0);
167        mv.visitInsn(ICONST_0);
168        mv.visitInsn(IRETURN);
169        l1 = new Label();
170        mv.visitLabel(l1);
171        mv.visitMaxs(1, 1);
172        mv.visitEnd();
173
174        cw.visitEnd();
175
176        mv = cw.visitMethod(ACC_PRIVATE, "m2", "(IIIIIIIIII)I", null, null);
177        mv.visitCode();
178        l0 = new Label();
179        mv.visitLabel(l0);
180        mv.visitInsn(ICONST_0);
181        mv.visitInsn(IRETURN);
182        l1 = new Label();
183        mv.visitLabel(l1);
184        mv.visitMaxs(1, 11);
185        mv.visitEnd();
186
187        cw.visitEnd();
188
189        return cw.toByteArray();
190    }
191
192    public static class AsmLoader extends ExportingClassLoader {
193        Class<?> loaded;
194
195        public AsmLoader(ClassLoader parent) {
196            super(parent);
197        }
198
199        @Override
200        protected Class<?> findClass(String name) throws ClassNotFoundException {
201            if (name.equals(NAME)) {
202                if (loaded != null) {
203                    return loaded;
204                }
205                byte[] bytes = bytesForB();
206                return (loaded = defineClass(name, bytes, 0, bytes.length));
207            } else {
208                return super.findClass(name);
209            }
210        }
211    }
212}
213