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 
 24 package jdk.internal.foreign.abi;
 25 
 26 import jdk.incubator.foreign.MemoryAddress;
 27 import jdk.incubator.foreign.MemoryHandles;
 28 import jdk.internal.foreign.MemoryAddressImpl;
 29 import jdk.internal.foreign.Utils;
 30 import jdk.internal.vm.annotation.Stable;
 31 
 32 import java.lang.invoke.MethodHandle;
 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.Arrays;
 38 import java.util.Objects;
 39 
 40 import static sun.security.action.GetBooleanAction.privilegedGetProperty;
 41 
 42 /**
 43  * This class implements upcall invocation from native code through a so called 'universal adapter'. A universal upcall adapter
 44  * takes an array of storage pointers, which describes the state of the CPU at the time of the upcall. This can be used
 45  * by the Java code to fetch the upcall arguments and to store the results to the desired location, as per system ABI.
 46  */
 47 public class ProgrammableUpcallHandler implements UpcallHandler {
 48 
 49     private static final boolean DEBUG =
 50         privilegedGetProperty("jdk.internal.foreign.ProgrammableUpcallHandler.DEBUG");
 51 
 52     private static final VarHandle VH_LONG = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
 53 
 54     @Stable
 55     private final MethodHandle mh;
 56     private final MethodType type;
 57     private final CallingSequence callingSequence;
 58     private final long entryPoint;
 59 
 60     private final ABIDescriptor abi;
 61     private final BufferLayout layout;
 62 
 63     public ProgrammableUpcallHandler(ABIDescriptor abi, MethodHandle target, CallingSequence callingSequence) {
 64         this.abi = abi;
 65         this.layout = BufferLayout.of(abi);
 66         this.type = callingSequence.methodType();
 67         this.callingSequence = callingSequence;
 68         this.mh = target.asSpreader(Object[].class, callingSequence.methodType().parameterCount());
 69         this.entryPoint = allocateUpcallStub(abi, layout);
 70     }
 71 
 72     @Override
 73     public long entryPoint() {
 74         return entryPoint;
 75     }
 76 
 77     public static void invoke(ProgrammableUpcallHandler handler, long address) {
 78         handler.invoke(MemoryAddress.ofLong(address));
 79     }
 80 
 81     private void invoke(MemoryAddress buffer) {
 82         try {
 83             if (DEBUG) {
 84                 System.err.println("Buffer state before:");
 85                 layout.dump(abi.arch, buffer, System.err);
 86             }
 87 
 88             MemoryAddress bufferBase = MemoryAddressImpl.ofLongUnchecked(buffer.toRawLongValue(), layout.size);
 89             MemoryAddress stackArgsBase = MemoryAddressImpl.ofLongUnchecked((long)VH_LONG.get(buffer.rebase(bufferBase.segment()).addOffset(layout.stack_args)));
 90             Object[] args = new Object[type.parameterCount()];
 91             for (int i = 0 ; i < type.parameterCount() ; i++) {
 92                 args[i] = BindingInterpreter.box(callingSequence.argumentBindings(i),
 93                         (storage, type) -> {
 94                             MemoryAddress ptr = abi.arch.isStackType(storage.type())
 95                                 ? stackArgsBase.addOffset(storage.index() * abi.arch.typeSize(abi.arch.stackType()))
 96                                 : bufferBase.addOffset(layout.argOffset(storage));
 97                             return SharedUtils.read(ptr, type);
 98                         });
 99             }
100 
101             if (DEBUG) {
102                 System.err.println("Java arguments:");
103                 System.err.println(Arrays.toString(args).indent(2));
104             }
105 
106             Object o = mh.invoke(args);
107 
108             if (DEBUG) {
109                 System.err.println("Java return:");
110                 System.err.println(Objects.toString(o).indent(2));
111             }
112 
113             if (mh.type().returnType() != void.class) {
114                 BindingInterpreter.unbox(o, callingSequence.returnBindings(),
115                         (storage, type, value) -> {
116                             MemoryAddress ptr = bufferBase.addOffset(layout.retOffset(storage));
117                             SharedUtils.writeOverSized(ptr, type, value);
118                         }, null);
119             }
120 
121             if (DEBUG) {
122                 System.err.println("Buffer state after:");
123                 layout.dump(abi.arch, buffer, System.err);
124             }
125         } catch (Throwable t) {
126             throw new IllegalStateException(t);
127         }
128     }
129 
130     public native long allocateUpcallStub(ABIDescriptor abi, BufferLayout layout);
131 
132     private static native void registerNatives();
133     static {
134         registerNatives();
135     }
136 }