1 /*
   2  * Copyright (c) 2015, 2018, 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 jdk.internal.nicl;
  24 
  25 import jdk.internal.nicl.types.DescriptorParser;
  26 import jdk.internal.org.objectweb.asm.MethodVisitor;
  27 import jdk.internal.org.objectweb.asm.Type;
  28 
  29 import java.lang.invoke.MethodHandle;
  30 import java.lang.invoke.MethodHandles;
  31 import java.lang.invoke.MethodType;
  32 import java.lang.reflect.Method;
  33 import java.nicl.layout.Function;
  34 import java.nicl.layout.Layout;
  35 import java.nicl.metadata.*;
  36 import java.nicl.types.LayoutType;
  37 import java.nicl.types.Pointer;
  38 import java.nicl.types.Struct;
  39 
  40 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  41 
  42 class HeaderImplGenerator extends BinderClassGenerator {
  43 
  44     // lookup helper to use for looking up symbols
  45     private final SymbolLookup lookup;
  46 
  47     HeaderImplGenerator(Class<?> hostClass, String implClassName, Class<?> c, SymbolLookup lookup) {
  48         super(hostClass, implClassName, new Class<?>[] { c });
  49         this.lookup = lookup;
  50     }
  51 
  52     enum AccessorMethodType {
  53         get, set, ptr;
  54 
  55         MethodType getMethodType(Class<?> c) {
  56             switch (this) {
  57                 case get: return MethodType.methodType(c);
  58                 case set: return MethodType.methodType(void.class, c);
  59                 case ptr: return MethodType.methodType(Pointer.class);
  60             }
  61 
  62             throw new IllegalArgumentException("Unhandled type: " + this);
  63         }
  64     }
  65 
  66     @Override
  67     protected void generateDefaultConstructor(BinderClassWriter cw) {
  68         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
  69         mv.visitCode();
  70         mv.visitVarInsn(ALOAD, 0);
  71         mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
  72         mv.visitInsn(RETURN);
  73         mv.visitMaxs(1,1);
  74         mv.visitEnd();
  75     }
  76 
  77     @Override
  78     protected void generateMethodImplementation(BinderClassWriter cw, Method method) {
  79         if (method.isAnnotationPresent(NativeType.class)) {
  80             if (Util.isFunction(method)) {

  81                 MethodType methodType = Util.methodTypeFor(method);
  82                 Function function = Util.functionof(method);
  83                 NativeInvoker invoker;
  84                 try {
  85                     invoker = new NativeInvoker(function, methodType, method.isVarArgs(), lookup, getSymbolName(method), method.toString(), method.getGenericReturnType());
  86                 } catch (NoSuchMethodException | IllegalAccessException e) {
  87                     throw new IllegalStateException(e);
  88                 }
  89                 addMethodFromHandle(cw, method.getName(), methodType, method.isVarArgs(), invoker.getBoundMethodHandle());
  90             } else {
  91                 generateGlobalVariableMethods(cw, method, getSymbolName(method, getGetterBaseName(method)));
  92             }
  93         } else {
  94             super.generateMethodImplementation(cw, method);
  95         }
  96     }
  97 
  98     private void generateGlobalVariableMethods(BinderClassWriter cw, Method method, String symbolName) {
  99         Class<?> c = method.getReturnType();
 100         java.lang.reflect.Type type = method.getGenericReturnType();
 101         Layout l = new DescriptorParser(method.getAnnotation(NativeType.class).layout()).parseLayout().findFirst().get();
 102         LayoutType<?> lt = Util.makeType(type, l);
 103 
 104         int dollarIndex = method.getName().indexOf("$");
 105         String methodBaseName = method.getName().substring(0, dollarIndex);
 106         Pointer<?> p;
 107 
 108         try {
 109             p = lookup.lookup(symbolName).getAddress().cast(lt);
 110         } catch (NoSuchMethodException e) {
 111             throw new IllegalStateException(e);
 112         }
 113 
 114         for (AccessorMethodType t : AccessorMethodType.values()) {
 115             String methodName = methodBaseName + "$" + t.name();
 116             MethodHandle target;
 117             switch (t) {
 118                 case get:
 119                     target = lt.getter();
 120                     target = target.bindTo(p).asType(MethodType.methodType(c));
 121                     break;
 122 
 123                 case set:
 124                     target = lt.setter();
 125                     target = target.bindTo(p).asType(MethodType.methodType(void.class, c));
 126                     break;
 127 
 128                 case ptr:
 129                     target = MethodHandles.constant(Pointer.class, p);
 130                     break;
 131 
 132                 default:
 133                     throw new InternalError("Unexpected access method type: " + t);
 134             }
 135 
 136             addMethodFromHandle(cw, methodName, t.getMethodType(c), false, target);
 137         }
 138     }
 139 
 140     private String getGetterBaseName(Method method) {
 141         String name = method.getName();
 142 
 143         if (!name.endsWith("$get")) {
 144             throw new IllegalArgumentException("Unexpected method name " + method.getName());
 145         }
 146 
 147         return name.substring(0, name.lastIndexOf("$"));
 148     }
 149 
 150     private String getSymbolName(Method method, String defaultName) {
 151         String name = method.getAnnotation(NativeType.class).name();
 152         if (NativeType.NO_NAME.equals(name)) {
 153             // FIXME: Make this an error (require name to be set)?
 154             return defaultName;
 155         } else {
 156             return name;
 157         }
 158     }
 159 
 160     private String getSymbolName(Method method) {
 161         return getSymbolName(method, method.getName());
 162     }
 163 
 164     // code generation helpers
 165 
 166     private void addMethodFromHandle(BinderClassWriter cw, String methodName, MethodType methodType, boolean isVarArgs, MethodHandle targetMethodHandle) {
 167         String descriptor = methodType.toMethodDescriptorString();
 168 
 169         int flags = ACC_PUBLIC;
 170         if (isVarArgs) {
 171             flags |= ACC_VARARGS;
 172         }
 173 
 174         MethodVisitor mv = cw.visitMethod(flags, methodName, descriptor, null, null);
 175 
 176         mv.visitCode();
 177 
 178         // push the method handle
 179         mv.visitLdcInsn(cw.makeConstantPoolPatch(targetMethodHandle));
 180         mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class));
 181 
 182         //copy arguments
 183         for (int i = 0, curSlot = 1; i < methodType.parameterCount(); i++) {
 184             Class<?> c = methodType.parameterType(i);
 185             mv.visitVarInsn(loadInsn(c), curSlot);
 186             curSlot += getSlotsForType(c);
 187         }
 188 
 189         //call MH
 190         mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invoke",
 191                 targetMethodHandle.type().toMethodDescriptorString(), false);
 192 
 193         mv.visitInsn(returnInsn(methodType.returnType()));
 194 
 195         mv.visitMaxs(0, 0);
 196         mv.visitEnd();
 197     }
 198 
 199     //where
 200     private static int getSlotsForType(Class<?> c) {
 201         if (c == long.class || c == double.class) {
 202             return 2;
 203         }
 204         return 1;
 205     }
 206 }
--- EOF ---