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.MemoryAddress;
 26 import jdk.incubator.foreign.MemoryHandles;
 27 import jdk.incubator.foreign.MemorySegment;
 28 import jdk.incubator.foreign.NativeScope;
 29 import jdk.internal.foreign.MemoryAddressImpl;
 30 import jdk.internal.foreign.Utils;
 31 
 32 import java.lang.invoke.MethodHandle;
 33 import java.lang.invoke.MethodHandles;
 34 import java.lang.invoke.MethodType;
 35 import java.lang.invoke.VarHandle;
 36 import java.nio.ByteOrder;
 37 import java.util.ArrayList;
 38 import java.util.Arrays;
 39 import java.util.List;
 40 import java.util.Map;
 41 import java.util.concurrent.ConcurrentHashMap;
 42 import java.util.stream.Collectors;
 43 import java.util.stream.IntStream;
 44 
 45 import static java.lang.invoke.MethodHandles.collectArguments;
 46 import static java.lang.invoke.MethodHandles.dropArguments;
 47 import static java.lang.invoke.MethodHandles.empty;
 48 import static java.lang.invoke.MethodHandles.filterArguments;
 49 import static java.lang.invoke.MethodHandles.identity;
 50 import static java.lang.invoke.MethodHandles.insertArguments;
 51 import static java.lang.invoke.MethodHandles.permuteArguments;
 52 import static java.lang.invoke.MethodHandles.tryFinally;
 53 import static java.lang.invoke.MethodType.methodType;
 54 import static sun.security.action.GetBooleanAction.privilegedGetProperty;
 55 
 56 /**
 57  * This class implements native call invocation through a so called 'universal adapter'. A universal adapter takes
 58  * an array of longs together with a call 'recipe', which is used to move the arguments in the right places as
 59  * expected by the system ABI.
 60  */
 61 public class ProgrammableInvoker {
 62     private static final boolean DEBUG =
 63         privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.DEBUG");
 64     private static final boolean NO_SPEC =
 65         privilegedGetProperty("jdk.internal.foreign.ProgrammableInvoker.NO_SPEC");
 66 
 67     private static final VarHandle VH_LONG = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
 68 
 69     private static final MethodHandle MH_INVOKE_MOVES;
 70     private static final MethodHandle MH_INVOKE_INTERP_BINDINGS;
 71 
 72     private static final MethodHandle MH_MAKE_ALLOCATOR;
 73     private static final MethodHandle MH_CLOSE_ALLOCATOR;
 74 
 75     private static final Map<ABIDescriptor, Long> adapterStubs = new ConcurrentHashMap<>();
 76 
 77     static {
 78         try {
 79             MethodHandles.Lookup lookup = MethodHandles.lookup();
 80             MH_INVOKE_MOVES = lookup.findVirtual(ProgrammableInvoker.class, "invokeMoves",
 81                     methodType(Object.class, Object[].class, Binding.Move[].class, Binding.Move[].class));
 82             MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(ProgrammableInvoker.class, "invokeInterpBindings",
 83                     methodType(Object.class, Object[].class, MethodHandle.class, Map.class, Map.class));
 84             MH_MAKE_ALLOCATOR = lookup.findStatic(NativeScope.class, "boundedScope",
 85                     methodType(NativeScope.class, long.class));
 86             MH_CLOSE_ALLOCATOR = lookup.findVirtual(NativeScope.class, "close",
 87                     methodType(void.class));
 88         } catch (ReflectiveOperationException e) {
 89             throw new RuntimeException(e);
 90         }
 91     }
 92 
 93     private final ABIDescriptor abi;
 94     private final BufferLayout layout;
 95     private final long stackArgsBytes;
 96 
 97     private final CallingSequence callingSequence;
 98 
 99     private final MemoryAddress addr;
100     private final long stubAddress;
101 
102     private final long bufferCopySize;
103 
104     public ProgrammableInvoker(ABIDescriptor abi, MemoryAddress addr, CallingSequence callingSequence) {
105         this.abi = abi;
106         this.layout = BufferLayout.of(abi);
107         this.stubAddress = adapterStubs.computeIfAbsent(abi, key -> generateAdapter(key, layout));
108 
109         this.addr = addr;
110         this.callingSequence = callingSequence;
111 
112         this.stackArgsBytes = callingSequence.argMoveBindings()
113                 .map(Binding.Move::storage)
114                 .filter(s -> abi.arch.isStackType(s.type()))
115                 .count()
116                 * abi.arch.typeSize(abi.arch.stackType());
117 
118         this.bufferCopySize = bufferCopySize(callingSequence);
119     }
120 
121     private static long bufferCopySize(CallingSequence callingSequence) {
122         // FIXME: > 16 bytes alignment might need extra space since the
123         // starting address of the allocator might be un-aligned.
124         long size = 0;
125         for (int i = 0; i < callingSequence.argumentCount(); i++) {
126             List<Binding> bindings = callingSequence.argumentBindings(i);
127             for (Binding b : bindings) {
128                 if (b instanceof Binding.Copy) {
129                     Binding.Copy c = (Binding.Copy) b;
130                     size = Utils.alignUp(size, c.alignment());
131                     size += c.size();
132                 }
133             }
134         }
135         return size;
136     }
137 
138     public MethodHandle getBoundMethodHandle() {
139         Binding.Move[] argMoves = callingSequence.argMoveBindings().toArray(Binding.Move[]::new);
140         Class<?>[] argMoveTypes = Arrays.stream(argMoves).map(Binding.Move::type).toArray(Class<?>[]::new);
141 
142         Binding.Move[] retMoves = callingSequence.retMoveBindings().toArray(Binding.Move[]::new);
143         Class<?> returnType = retMoves.length == 0
144                 ? void.class
145                 : retMoves.length == 1
146                     ? retMoves[0].type()
147                     : Object[].class;
148 
149         MethodType leafType = methodType(returnType, argMoveTypes);
150 
151         MethodHandle handle = insertArguments(MH_INVOKE_MOVES.bindTo(this), 1, argMoves, retMoves)
152                                             .asCollector(Object[].class, leafType.parameterCount())
153                                             .asType(leafType);
154 
155         if (NO_SPEC || retMoves.length > 1) {
156             Map<VMStorage, Integer> argIndexMap = indexMap(argMoves);
157             Map<VMStorage, Integer> retIndexMap = indexMap(retMoves);
158 
159             handle = insertArguments(MH_INVOKE_INTERP_BINDINGS.bindTo(this), 1, handle, argIndexMap, retIndexMap);
160             handle = handle.asCollector(Object[].class, callingSequence.methodType().parameterCount())
161                                              .asType(callingSequence.methodType());
162          } else {
163              handle = specialize(handle);
164          }
165 
166         return handle;
167     }
168 
169     private MethodHandle specialize(MethodHandle leafHandle) {
170         MethodType highLevelType = callingSequence.methodType();
171         MethodType leafType = leafHandle.type();
172 
173         MethodHandle specializedHandle = leafHandle; // initial
174 
175         int insertPos = -1;
176         if (bufferCopySize > 0) {
177             specializedHandle = dropArguments(specializedHandle, 0, NativeScope.class);
178             insertPos++;
179         }
180         for (int i = 0; i < highLevelType.parameterCount(); i++) {
181             List<Binding> bindings = callingSequence.argumentBindings(i);
182             insertPos += bindings.stream().filter(Binding.Move.class::isInstance).count() + 1;
183             // We interpret the bindings in reverse since we have to construct a MethodHandle from the bottom up
184             for (int j = bindings.size() - 1; j >= 0; j--) {
185                 Binding binding = bindings.get(j);
186                 if (binding.tag() == Binding.Tag.MOVE) {
187                     insertPos--;
188                 } else {
189                     specializedHandle = binding.specializeUnbox(specializedHandle, insertPos);
190                 }
191             }
192         }
193 
194         if (highLevelType.returnType() != void.class) {
195             MethodHandle returnFilter = identity(highLevelType.returnType());
196             List<Binding> bindings = callingSequence.returnBindings();
197             for (int j = bindings.size() - 1; j >= 0; j--) {
198                 Binding binding = bindings.get(j);
199                 returnFilter = binding.specializeBox(returnFilter);
200             }
201             specializedHandle = MethodHandles.filterReturnValue(specializedHandle, returnFilter);
202         }
203 
204         if (bufferCopySize > 0) {
205             // insert try-finally to close the NativeScope used for Binding.Copy
206             MethodHandle closer = leafType.returnType() == void.class
207                   // (Throwable, NativeScope) -> void
208                 ? collectArguments(empty(methodType(void.class, Throwable.class)), 1, MH_CLOSE_ALLOCATOR)
209                   // (Throwable, V, NativeScope) -> V
210                 : collectArguments(dropArguments(identity(specializedHandle.type().returnType()), 0, Throwable.class),
211                                    2, MH_CLOSE_ALLOCATOR);
212             specializedHandle = tryFinally(specializedHandle, closer);
213             specializedHandle = collectArguments(specializedHandle, 0, insertArguments(MH_MAKE_ALLOCATOR, 0, bufferCopySize));
214         }
215         return specializedHandle;
216     }
217 
218     private Map<VMStorage, Integer> indexMap(Binding.Move[] moves) {
219         return IntStream.range(0, moves.length)
220                         .boxed()
221                         .collect(Collectors.toMap(i -> moves[i].storage(), i -> i));
222     }
223 
224     /**
225      * Does a native invocation by moving primitive values from the arg array into an intermediate buffer
226      * and calling the assembly stub that forwards arguments from the buffer to the target function
227      *
228      * @param args an array of primitive values to be copied in to the buffer
229      * @param argBindings Binding.Move values describing how arguments should be copied
230      * @param returnBindings Binding.Move values describing how return values should be copied
231      * @return null, a single primitive value, or an Object[] of primitive values
232      */
233     Object invokeMoves(Object[] args, Binding.Move[] argBindings, Binding.Move[] returnBindings) {
234         MemorySegment stackArgsSeg = null;
235         try (MemorySegment argBuffer = MemorySegment.allocateNative(layout.size, 64)) {
236             MemoryAddress argsPtr = argBuffer.baseAddress();
237             MemoryAddress stackArgs;
238             if (stackArgsBytes > 0) {
239                 stackArgsSeg = MemorySegment.allocateNative(stackArgsBytes, 8);
240                 stackArgs = stackArgsSeg.baseAddress();
241             } else {
242                 stackArgs = MemoryAddressImpl.NULL;
243             }
244 
245             VH_LONG.set(argsPtr.addOffset(layout.arguments_next_pc), addr.toRawLongValue());
246             VH_LONG.set(argsPtr.addOffset(layout.stack_args_bytes), stackArgsBytes);
247             VH_LONG.set(argsPtr.addOffset(layout.stack_args), stackArgs.toRawLongValue());
248 
249             for (int i = 0; i < argBindings.length; i++) {
250                 Binding.Move binding = argBindings[i];
251                 VMStorage storage = binding.storage();
252                 MemoryAddress ptr = abi.arch.isStackType(storage.type())
253                     ? stackArgs.addOffset(storage.index() * abi.arch.typeSize(abi.arch.stackType()))
254                     : argsPtr.addOffset(layout.argOffset(storage));
255                 SharedUtils.writeOverSized(ptr, binding.type(), args[i]);
256             }
257 
258             if (DEBUG) {
259                 System.err.println("Buffer state before:");
260                 layout.dump(abi.arch, argsPtr, System.err);
261             }
262 
263             invokeNative(stubAddress, argsPtr.toRawLongValue());
264 
265             if (DEBUG) {
266                 System.err.println("Buffer state after:");
267                 layout.dump(abi.arch, argsPtr, System.err);
268             }
269 
270             if (returnBindings.length == 0) {
271                 return null;
272             } else if (returnBindings.length == 1) {
273                 Binding.Move move = returnBindings[0];
274                 VMStorage storage = move.storage();
275                 return SharedUtils.read(argsPtr.addOffset(layout.retOffset(storage)), move.type());
276             } else { // length > 1
277                 Object[] returns = new Object[returnBindings.length];
278                 for (int i = 0; i < returnBindings.length; i++) {
279                     Binding.Move move = returnBindings[i];
280                     VMStorage storage = move.storage();
281                     returns[i] = SharedUtils.read(argsPtr.addOffset(layout.retOffset(storage)), move.type());
282                 }
283                 return returns;
284             }
285         } finally {
286             if (stackArgsSeg != null) {
287                 stackArgsSeg.close();
288             }
289         }
290     }
291 
292     Object invokeInterpBindings(Object[] args, MethodHandle leaf,
293                                 Map<VMStorage, Integer> argIndexMap,
294                                 Map<VMStorage, Integer> retIndexMap) throws Throwable {
295         NativeScope scope = bufferCopySize != 0 ? NativeScope.boundedScope(bufferCopySize) : null;
296         try {
297             // do argument processing, get Object[] as result
298             Object[] moves = new Object[leaf.type().parameterCount()];
299             for (int i = 0; i < args.length; i++) {
300                 Object arg = args[i];
301                 BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
302                         (storage, type, value) -> {
303                             moves[argIndexMap.get(storage)] = value;
304                         }, scope);
305             }
306 
307             // call leaf
308             Object o = leaf.invokeWithArguments(moves);
309 
310             // return value processing
311             if (o == null) {
312                 return null;
313             } else if (o instanceof Object[]) {
314                 Object[] oArr = (Object[]) o;
315                 return BindingInterpreter.box(callingSequence.returnBindings(),
316                         (storage, type) -> oArr[retIndexMap.get(storage)]);
317             } else {
318                 return BindingInterpreter.box(callingSequence.returnBindings(), (storage, type) -> o);
319             }
320         } finally {
321             if (scope != null) {
322                 scope.close();
323             }
324         }
325     }
326 
327     //natives
328 
329     static native void invokeNative(long adapterStub, long buff);
330     static native long generateAdapter(ABIDescriptor abi, BufferLayout layout);
331 
332     private static native void registerNatives();
333     static {
334         registerNatives();
335     }
336 }
337