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