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 package org.graalvm.compiler.core.test; 24 25 import java.lang.invoke.MethodHandle; 26 import java.lang.invoke.MethodHandles; 27 import java.lang.invoke.MethodType; 28 29 import org.graalvm.compiler.code.CompilationResult; 30 import org.graalvm.compiler.test.ExportingClassLoader; 31 import org.junit.Test; 32 import org.objectweb.asm.ClassWriter; 33 import org.objectweb.asm.Label; 34 import org.objectweb.asm.MethodVisitor; 35 import org.objectweb.asm.Opcodes; 36 37 import jdk.vm.ci.code.InstalledCode; 38 import jdk.vm.ci.meta.ResolvedJavaMethod; 39 40 public 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 }