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