1 /*
   2  * Copyright (c) 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.abi;
  24 
  25 import jdk.internal.foreign.ScopeImpl;
  26 import jdk.internal.foreign.memory.BoundedPointer;
  27 
  28 import java.foreign.NativeMethodType;
  29 import java.foreign.NativeTypes;
  30 import java.foreign.Scope;
  31 import java.foreign.memory.LayoutType;
  32 import java.foreign.memory.Pointer;
  33 import java.lang.invoke.MethodHandle;
  34 import java.lang.invoke.MethodHandles;
  35 import java.lang.invoke.MethodType;
  36 import java.util.List;
  37 import java.util.function.Function;
  38 
  39 import static sun.security.action.GetBooleanAction.privilegedGetProperty;
  40 
  41 /**
  42  * This class implements native call invocation through a so called 'universal adapter'. A universal adapter takes
  43  * an array of longs together with a call 'recipe', which is used to move the arguments in the right places as
  44  * expected by the system ABI.
  45  */
  46 public abstract class UniversalNativeInvoker {
  47     private static final boolean DEBUG =
  48         privilegedGetProperty("jdk.internal.foreign.NativeInvoker.DEBUG");
  49 
  50     // Unbound MH for the invoke() method
  51     private static final MethodHandle INVOKE_MH;
  52     private static final MethodHandle INVOKE_NATIVE_MH;
  53 
  54     static {
  55         try {
  56             MethodType INVOKE_NATIVE_MT = MethodType.methodType(void.class, long[].class, long[].class, long[].class, long.class);
  57             INVOKE_NATIVE_MH = MethodHandles.lookup().findStatic(UniversalNativeInvoker.class, "invokeNative", INVOKE_NATIVE_MT);
  58             INVOKE_MH = MethodHandles.lookup().findVirtual(UniversalNativeInvoker.class, "invoke", MethodType.methodType(Object.class, Object[].class));
  59         } catch (ReflectiveOperationException e) {
  60             throw new RuntimeException(e);
  61         }
  62     }
  63 
  64     private final ShuffleRecipe shuffleRecipe;
  65     private final NativeMethodType nmt;
  66     private final CallingSequence callingSequence;
  67     private final long addr;
  68     private final String methodName;
  69 
  70     protected UniversalNativeInvoker(long addr, String methodName, CallingSequence callingSequence, NativeMethodType nmt) {
  71         this.addr = addr;
  72         this.methodName = methodName;
  73         this.callingSequence = callingSequence;
  74         this.nmt = nmt;
  75         this.shuffleRecipe = ShuffleRecipe.make(callingSequence);
  76     }
  77 
  78     public MethodHandle getBoundMethodHandle() {
  79         return INVOKE_MH.bindTo(this).asCollector(Object[].class, nmt.parameterCount())
  80                 .asType(nmt.methodType());
  81     }
  82 
  83     Object invoke(Object[] args) throws Throwable {
  84         boolean isVoid = nmt.returnType() == NativeTypes.VOID;
  85         int nValues = shuffleRecipe.getNoofArgumentPulls();
  86         long[] values = new long[nValues];
  87         Pointer<Long> argsPtr = nValues > 0 ?
  88                 BoundedPointer.fromArray(NativeTypes.UINT64, values) :
  89                 Pointer.ofNull();
  90 
  91         for (int i = 0; i < args.length; i++) {
  92             Object arg = args[i];
  93             unboxValue(arg, nmt.parameterType(i), b -> argsPtr.offset(callingSequence.argumentStorageOffset(b)),
  94                     callingSequence.getArgumentBindings(i));
  95         }
  96 
  97         final Pointer<?> retPtr;
  98         long[] returnValues = new long[shuffleRecipe.getNoofReturnPulls()];
  99         if (callingSequence.returnsInMemory()) {
 100             // FIXME (STRUCT-LIFECYCLE):
 101             // Leak the allocated structs for now until the life cycle has been figured out
 102             Scope scope = Scope.globalScope().fork();
 103             retPtr = ((ScopeImpl)scope).allocate(nmt.returnType(), 8);
 104             unboxValue(retPtr, NativeTypes.UINT64.pointer(), b -> argsPtr.offset(callingSequence.argumentStorageOffset(b)),
 105                     callingSequence.getReturnBindings());
 106         } else if (!isVoid && returnValues.length != 0) {
 107             retPtr = BoundedPointer.fromArray(NativeTypes.UINT64, returnValues);
 108         } else {
 109             retPtr = Pointer.ofNull();
 110         }
 111 
 112         if (DEBUG) {
 113             System.err.println("Invoking method " + methodName + " with " + values.length + " argument values");
 114             for (int i = 0; i < values.length; i++) {
 115                 System.err.println("value[" + i + "] = 0x" + Long.toHexString(values[i]));
 116             }
 117         }
 118 
 119         INVOKE_NATIVE_MH.invokeExact(values, returnValues, shuffleRecipe.getRecipe(), addr);
 120 
 121         if (DEBUG) {
 122             System.err.println("Returned from method " + methodName + " with " + returnValues.length + " return values");
 123             System.err.println("structPtr = 0x" + Long.toHexString(retPtr.addr()));
 124             for (int i = 0; i < returnValues.length; i++) {
 125                 System.err.println("returnValues[" + i + "] = 0x" + Long.toHexString(returnValues[i]));
 126             }
 127         }
 128 
 129         if (isVoid) {
 130             return null;
 131         } else if (!callingSequence.returnsInMemory()) {
 132             return boxValue(nmt.returnType(), b -> retPtr.offset(callingSequence.returnStorageOffset(b)), callingSequence.getReturnBindings());
 133         } else {
 134             return retPtr.get();
 135         }
 136     }
 137 
 138     public abstract void unboxValue(Object o, LayoutType<?> type, Function<ArgumentBinding,
 139                 Pointer<?>> dstPtrFunc, List<ArgumentBinding> bindings) throws Throwable;
 140 
 141     public abstract Object boxValue(LayoutType<?> type, Function<ArgumentBinding,
 142             Pointer<?>> srcPtrFunc, List<ArgumentBinding> bindings) throws IllegalAccessException;
 143 
 144     //natives
 145 
 146     static native void invokeNative(long[] args, long[] rets, long[] recipe, long addr);
 147 
 148     private static native void registerNatives();
 149     static {
 150         registerNatives();
 151     }
 152 }