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.foreign;
  24 
  25 import jdk.internal.foreign.memory.BoundedMemoryRegion;
  26 import jdk.internal.foreign.memory.BoundedPointer;
  27 import jdk.internal.foreign.memory.DescriptorParser;
  28 import jdk.internal.org.objectweb.asm.MethodVisitor;
  29 
  30 import java.foreign.Scope;
  31 import java.lang.invoke.MethodHandle;
  32 import java.lang.invoke.MethodHandles;
  33 import java.lang.invoke.MethodType;
  34 import java.lang.reflect.Method;
  35 import java.foreign.layout.Function;
  36 import java.foreign.layout.Layout;
  37 import java.foreign.annotations.*;
  38 import java.foreign.memory.LayoutType;
  39 import java.foreign.memory.Pointer;
  40 import java.util.HashMap;
  41 import java.util.Map;
  42 
  43 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  44 
  45 class HeaderImplGenerator extends BinderClassGenerator {
  46 
  47     // lookup helper to use for looking up symbols
  48     private final SymbolLookup lookup;
  49 
  50     // dictionary from method name to member info
  51     final Map<String, MemberInfo<?>> nameToInfo = new HashMap<>();
  52 
  53     // global scope for this library
  54     private final Scope libScope;
  55 
  56     HeaderImplGenerator(Class<?> hostClass, String implClassName, Class<?> c, SymbolLookup lookup, Scope libScope) {
  57         super(hostClass, implClassName, new Class<?>[] { c });
  58         this.lookup = lookup;
  59         this.libScope = libScope;
  60     }
  61 
  62     abstract class MemberInfo<D> {
  63         String symbolName;
  64         D descriptor;
  65 
  66         MemberInfo(String symbolName, D descriptor) {
  67             this.symbolName = symbolName;
  68             this.descriptor = descriptor;
  69         }
  70     }
  71 
  72     class GlobalVarInfo extends MemberInfo<Layout> {
  73 
  74         AccessorKind accessorKind;
  75 
  76         GlobalVarInfo(String symbolName, Layout layout, AccessorKind accessorKind) {
  77             super(symbolName, layout);
  78             this.accessorKind = accessorKind;
  79         }
  80     }
  81 
  82     class FunctionInfo extends MemberInfo<Function> {
  83         public FunctionInfo(String symbolName, Function descriptor) {
  84             super(symbolName, descriptor);
  85         }
  86     }
  87 
  88     @Override
  89     protected void generateMembers(BinderClassWriter cw) {
  90         Class<?> headerClass = interfaces[0];
  91         String declarations = headerClass.getAnnotation(NativeHeader.class).declarations();
  92         for (Map.Entry<String, Object> declEntry : DescriptorParser.parseHeaderDeclarations(declarations).entrySet()) {
  93             if (declEntry.getValue() instanceof Layout) {
  94                 Layout l = (Layout)declEntry.getValue();
  95                 for (Map.Entry<AccessorKind, String> accessorEntry : AccessorKind.from(l).entrySet()) {
  96                     nameToInfo.put(accessorEntry.getValue(), new GlobalVarInfo(declEntry.getKey(), l, accessorEntry.getKey()));
  97                 }
  98             } else {
  99                 Function f = (Function)declEntry.getValue();
 100                 nameToInfo.put(declEntry.getKey(), new FunctionInfo(declEntry.getKey(), f));
 101             }
 102         }
 103         super.generateMembers(cw);
 104     }
 105 
 106     @Override
 107     protected void generateConstructor(BinderClassWriter cw) {
 108         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 109         mv.visitCode();
 110         mv.visitVarInsn(ALOAD, 0);
 111         mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
 112         mv.visitInsn(RETURN);
 113         mv.visitMaxs(1,1);
 114         mv.visitEnd();
 115     }
 116 
 117     @Override
 118     protected void generateMethodImplementation(BinderClassWriter cw, Method method) {
 119         MemberInfo<?> memberInfo = nameToInfo.get(method.getName());
 120         if (memberInfo instanceof FunctionInfo) {
 121             generateFunctionMethod(cw, method, (FunctionInfo)memberInfo);
 122         } else if (memberInfo instanceof GlobalVarInfo) {
 123             generateGlobalVariableMethod(cw, method, (GlobalVarInfo)memberInfo);
 124         }
 125     }
 126 
 127     void generateFunctionMethod(BinderClassWriter cw, Method method, FunctionInfo info) {
 128         MethodType methodType = Util.methodTypeFor(method);
 129         Function function = info.descriptor;
 130         try {
 131             NativeInvoker invoker = new NativeInvoker(layoutResolver.resolve(function), methodType, method.isVarArgs(), method.toString(), method.getGenericReturnType());
 132             long addr = lookup.lookup(info.symbolName).getAddress().addr();
 133             addMethodFromHandle(cw, method.getName(), methodType, method.isVarArgs(), invoker.getBoundMethodHandle(),
 134                 mv -> mv.visitLdcInsn(addr));
 135         } catch (ReflectiveOperationException e) {
 136             throw new IllegalStateException(e);
 137         }
 138     }
 139 
 140     private void generateGlobalVariableMethod(BinderClassWriter cw, Method method, GlobalVarInfo info) {
 141         java.lang.reflect.Type type = info.accessorKind.carrier(method);
 142         Class<?> c = Util.erasure(type);
 143         Layout l = info.descriptor;
 144         LayoutType<?> lt = Util.makeType(type, l);
 145 
 146         String methodName = method.getName();
 147         Pointer<?> p;
 148 
 149         try {
 150             p = BoundedPointer.createNativeVoidPointer(libScope,
 151                     lookup.lookup(info.symbolName).getAddress().addr(), BoundedMemoryRegion.MODE_RW).
 152                     cast(lt).limit(1);
 153         } catch (IllegalAccessException | NoSuchMethodException e) {
 154             throw new IllegalStateException(e);
 155         }
 156 
 157         MethodHandle target;
 158         AccessorKind kind = info.accessorKind;
 159         switch (kind) {
 160             case GET:
 161                 target = lt.getter();
 162                 target = target.bindTo(p).asType(MethodType.methodType(c));
 163                 break;
 164 
 165             case SET:
 166                 target = lt.setter();
 167                 target = target.bindTo(p).asType(MethodType.methodType(void.class, c));
 168                 break;
 169 
 170             case PTR:
 171                 target = MethodHandles.constant(Pointer.class, p);
 172                 break;
 173 
 174             default:
 175                 throw new InternalError("Unexpected access method type: " + kind);
 176         }
 177 
 178         addMethodFromHandle(cw, methodName, kind.getMethodType(c), false, target, mv -> {});
 179     }
 180 }