package com.oracle.svm.truffle.nfi;
import static com.oracle.svm.truffle.nfi.Target_com_oracle_truffle_nfi_impl_NativeArgumentBuffer_TypeTag.getOffset;
import static com.oracle.svm.truffle.nfi.Target_com_oracle_truffle_nfi_impl_NativeArgumentBuffer_TypeTag.getTag;
import org.graalvm.nativeimage.PinnedObject;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.UnmanagedMemory;
import org.graalvm.nativeimage.c.CContext;
import org.graalvm.nativeimage.c.struct.CFieldAddress;
import org.graalvm.nativeimage.c.struct.CStruct;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.word.Pointer;
import org.graalvm.word.PointerBase;
import org.graalvm.word.WordBase;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.CErrorNumber;
import com.oracle.svm.core.annotate.NeverInline;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.nodes.CFunctionEpilogueNode;
import com.oracle.svm.core.nodes.CFunctionPrologueNode;
import com.oracle.svm.core.thread.VMThreads;
import com.oracle.svm.truffle.nfi.NativeAPI.NativeTruffleContext;
import com.oracle.svm.truffle.nfi.NativeAPI.NativeTruffleEnv;
import com.oracle.svm.truffle.nfi.libffi.LibFFI;
import com.oracle.svm.truffle.nfi.libffi.LibFFI.ffi_cif;
import com.oracle.svm.truffle.nfi.libffi.LibFFI.ffi_type;
import com.oracle.svm.truffle.nfi.libffi.LibFFI.ffi_type_array;
import com.oracle.svm.truffle.nfi.libffi.LibFFIHeaderDirectives;
final class NativeSignature {
@CContext(LibFFIHeaderDirectives.class)
@CStruct("svm_cif_data")
public interface CifData extends PointerBase {
@CFieldAddress
ffi_cif cif();
@CFieldAddress
ffi_type_array args();
}
static class PrepareHelper {
static CifData prepareArgs(int argCount, Target_com_oracle_truffle_nfi_impl_LibFFIType... args) {
CifData data = UnmanagedMemory.malloc(SizeOf.get(CifData.class) + argCount * SizeOf.get(ffi_type_array.class));
for (int i = 0; i < argCount; i++) {
data.args().write(i, WordFactory.pointer(args[i].type));
}
return data;
}
static long checkRet(CifData data, int ret) {
if (ret == LibFFI.FFI_OK()) {
return data.rawValue();
} else {
UnmanagedMemory.free(data);
return 0;
}
}
}
static class ExecuteHelper {
static int alignUp(int index, int alignment) {
int ret = index;
if (ret % alignment != 0) {
ret += alignment - (ret % alignment);
}
return ret;
}
@SuppressWarnings("try")
static void execute(NativeTruffleContext ctx, ffi_cif cif, PointerBase ret, long functionPointer, byte[] primArgs, int patchCount, int[] patchOffsets, Object[] objArgs,
LocalNativeScope scope) {
int nargs = cif.nargs();
WordPointer argPtrs = UnmanagedMemory.malloc(nargs * SizeOf.get(WordPointer.class));
NativeTruffleEnv env = StackValue.get(NativeTruffleEnv.class);
NFIInitialization.initializeEnv(env, ctx);
try (PinnedObject primBuffer = PinnedObject.create(primArgs)) {
Pointer prim = primBuffer.addressOfArrayElement(0);
int primIdx = 0;
for (int i = 0; i < nargs; i++) {
ffi_type type = cif.arg_types().read(i);
primIdx = alignUp(primIdx, type.alignment());
argPtrs.write(i, prim.add(primIdx));
primIdx += type.size().rawValue();
}
for (int i = 0; i < patchCount; i++) {
Target_com_oracle_truffle_nfi_impl_NativeArgumentBuffer_TypeTag tag = getTag(patchOffsets[i]);
int offset = getOffset(patchOffsets[i]);
Object obj = objArgs[i];
if (tag == Target_com_oracle_truffle_nfi_impl_NativeArgumentBuffer_TypeTag.OBJECT) {
WordBase handle = scope.createLocalHandle(obj);
prim.writeWord(offset, handle);
} else if (tag == Target_com_oracle_truffle_nfi_impl_NativeArgumentBuffer_TypeTag.STRING) {
PointerBase strPtr = scope.pinString((String) obj);
prim.writeWord(offset, strPtr);
} else if (tag == Target_com_oracle_truffle_nfi_impl_NativeArgumentBuffer_TypeTag.KEEPALIVE) {
} else if (tag == Target_com_oracle_truffle_nfi_impl_NativeArgumentBuffer_TypeTag.ENV) {
prim.writeWord(offset, env);
} else {
PointerBase arrPtr = scope.pinArray(obj);
prim.writeWord(offset, arrPtr);
}
}
ffiCall(cif, WordFactory.pointer(functionPointer), ret, argPtrs, ErrnoMirror.errnoMirror.getAddress());
Throwable pending = NativeClosure.pendingException.get();
if (pending != null) {
NativeClosure.pendingException.set(null);
throw rethrow(pending);
}
} finally {
UnmanagedMemory.free(argPtrs);
}
}
@NeverInline("Must not be inlined in a caller that has an exception handler: We only support InvokeNode and not InvokeWithExceptionNode between a CFunctionPrologueNode and CFunctionEpilogueNode")
private static void ffiCall(ffi_cif cif, PointerBase fn, PointerBase rvalue, WordPointer avalue, CIntPointer errnoMirror) {
CFunctionPrologueNode.cFunctionPrologue(VMThreads.StatusSupport.STATUS_IN_NATIVE);
doFfiCall(cif, fn, rvalue, avalue, errnoMirror);
CFunctionEpilogueNode.cFunctionEpilogue(VMThreads.StatusSupport.STATUS_IN_NATIVE);
}
@Uninterruptible(reason = "In native.")
@NeverInline("Can have only a single invoke between CFunctionPrologueNode and CFunctionEpilogueNode.")
private static void doFfiCall(ffi_cif cif, PointerBase fn, PointerBase rvalue, WordPointer avalue, CIntPointer errnoMirror) {
CErrorNumber.setCErrorNumber(errnoMirror.read());
LibFFI.NoTransitions.ffi_call(cif, fn, rvalue, avalue);
errnoMirror.write(CErrorNumber.getCErrorNumber());
}
}
@SuppressWarnings({"unchecked"})
private static <E extends Throwable> RuntimeException rethrow(Throwable ex) throws E {
throw (E) ex;
}
}