1 /*
  2  * Copyright (c) 2019, 2020, 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.incubator.foreign.FunctionDescriptor;
 26 import jdk.incubator.foreign.MemoryAddress;
 27 import jdk.incubator.foreign.MemoryHandles;
 28 import jdk.incubator.foreign.MemorySegment;
 29 import jdk.internal.foreign.MemoryAddressImpl;
 30 
 31 import java.lang.invoke.MethodHandle;
 32 import java.lang.invoke.MethodHandles;
 33 import java.lang.invoke.MethodType;
 34 import java.lang.invoke.VarHandle;
 35 import java.nio.ByteOrder;
 36 import java.util.ArrayList;
 37 import java.util.List;
 38 import java.util.Map;
 39 import java.util.concurrent.ConcurrentHashMap;
 40 
 41 import static sun.security.action.GetBooleanAction.privilegedGetProperty;
 42 
 43 /**
 44  * This class implements native call invocation through a so called 'universal adapter'. A universal adapter takes
 45  * an array of longs together with a call 'recipe', which is used to move the arguments in the right places as
 46  * expected by the system ABI.
 47  */
 48 public class ProgrammableInvoker {
 49     private static final boolean DEBUG =
 50         privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.DEBUG");
 51 
 52     private static final VarHandle VH_LONG = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
 53 
 54     // Unbound MH for the invoke() method
 55     private static final MethodHandle INVOKE_MH;
 56 
 57     private static final Map<ABIDescriptor, Long> adapterStubs = new ConcurrentHashMap<>();
 58 
 59     static {
 60         try {
 61             INVOKE_MH = MethodHandles.lookup().findVirtual(ProgrammableInvoker.class, "invoke", MethodType.methodType(Object.class, Object[].class));
 62         } catch (ReflectiveOperationException e) {
 63             throw new RuntimeException(e);
 64         }
 65     }
 66 
 67     private final ABIDescriptor abi;
 68     private final BufferLayout layout;
 69     private final long stackArgsBytes;
 70 
 71     private final MethodType type;
 72     private final FunctionDescriptor function;
 73     private final CallingSequence callingSequence;
 74 
 75     private final MemoryAddress addr;
 76     private final long stubAddress;
 77 
 78     public ProgrammableInvoker(ABIDescriptor abi, MemoryAddress addr, CallingSequence callingSequence) {
 79         this.abi = abi;
 80         this.layout = BufferLayout.of(abi);
 81         this.stubAddress = adapterStubs.computeIfAbsent(abi, key -> generateAdapter(key, layout));
 82 
 83         this.addr = addr;
 84         this.callingSequence = callingSequence;
 85         this.type = callingSequence.methodType();
 86         this.function = callingSequence.functionDesc();
 87 
 88         this.stackArgsBytes = callingSequence.moveBindings()
 89                 .map(Binding.Move::storage)
 90                 .filter(s -> abi.arch.isStackType(s.type()))
 91                 .count()
 92                 * abi.arch.typeSize(abi.arch.stackType());
 93     }
 94 
 95     public MethodHandle getBoundMethodHandle() {
 96         return INVOKE_MH.bindTo(this).asCollector(Object[].class, type.parameterCount()).asType(type);
 97     }
 98 
 99     Object invoke(Object[] args) {
100         List<MemorySegment> tempBuffers = new ArrayList<>();
101         try (MemorySegment argBuffer = MemorySegment.allocateNative(layout.size, 64)) {
102             MemoryAddress argsPtr = argBuffer.baseAddress();
103             MemoryAddress stackArgs;
104             if (stackArgsBytes > 0) {
105                 MemorySegment stackArgsSeg = MemorySegment.allocateNative(stackArgsBytes, 8);
106                 tempBuffers.add(stackArgsSeg);
107                 stackArgs = stackArgsSeg.baseAddress();
108             } else {
109                 stackArgs = MemoryAddressImpl.NULL;
110             }
111 
112             VH_LONG.set(argsPtr.addOffset(layout.arguments_next_pc), addr.toRawLongValue());
113             VH_LONG.set(argsPtr.addOffset(layout.stack_args_bytes), stackArgsBytes);
114             VH_LONG.set(argsPtr.addOffset(layout.stack_args), stackArgs.toRawLongValue());
115 
116             for (int i = 0; i < args.length; i++) {
117                 Object arg = args[i];
118                 jdk.internal.foreign.abi.BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
119                         s -> {
120                             if (abi.arch.isStackType(s.type())) {
121                                 return stackArgs.addOffset(s.index() * abi.arch.typeSize(abi.arch.stackType()));
122                             }
123                             return argsPtr.addOffset(layout.argOffset(s));
124                         }, tempBuffers);
125             }
126 
127             if (DEBUG) {
128                 System.err.println("Buffer state before:");
129                 layout.dump(abi.arch, argsPtr, System.err);
130             }
131 
132             invokeNative(stubAddress, argsPtr.toRawLongValue());
133 
134             if (DEBUG) {
135                 System.err.println("Buffer state after:");
136                 layout.dump(abi.arch, argsPtr, System.err);
137             }
138 
139             return function.returnLayout().isEmpty()
140                     ? null
141                     : jdk.internal.foreign.abi.BindingInterpreter.box(callingSequence.returnBindings(),
142                     s -> argsPtr.addOffset(layout.retOffset(s))); // buffers are leaked
143         } finally {
144             tempBuffers.forEach(MemorySegment::close);
145         }
146     }
147 
148     //natives
149 
150     static native void invokeNative(long adapterStub, long buff);
151     static native long generateAdapter(ABIDescriptor abi, BufferLayout layout);
152 
153     private static native void registerNatives();
154     static {
155         registerNatives();
156     }
157 }
158