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