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