/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.nfi;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.nfi.LibFFIType.Direction;
import com.oracle.truffle.nfi.types.NativeSignature;
import com.oracle.truffle.nfi.NativeAllocation.FreeDestructor;
import com.oracle.truffle.nfi.types.NativeArrayTypeMirror;
import com.oracle.truffle.nfi.types.NativeTypeMirror;
import java.util.List;
final class LibFFISignature {
public static LibFFISignature create(NativeSignature signature) {
LibFFISignature ret = new LibFFISignature(signature);
NativeAllocation.registerNativeAllocation(ret, new FreeDestructor(ret.cif));
return ret;
}
private final LibFFIType retType;
private final LibFFIType[] argTypes;
private final int primitiveSize;
private final int objectCount;
private final long cif;
private final Direction allowedCallDirection;
private LibFFISignature(NativeSignature signature) {
if (signature.getRetType() instanceof NativeArrayTypeMirror) {
throw new IllegalArgumentException("array type as return value is not supported");
}
boolean allowJavaToNativeCall = true;
boolean allowNativeToJavaCall = true;
this.retType = LibFFIType.lookupRetType(signature.getRetType());
switch (retType.allowedDataFlowDirection) {
/*
* If the call goes from Java to native, the return value flows from native-to-Java, and
* vice-versa.
*/
case JAVA_TO_NATIVE_ONLY:
allowJavaToNativeCall = false;
break;
case NATIVE_TO_JAVA_ONLY:
allowNativeToJavaCall = false;
break;
}
List<NativeTypeMirror> args = signature.getArgTypes();
this.argTypes = new LibFFIType[args.size()];
for (int i = 0; i < argTypes.length; i++) {
LibFFIType argType = LibFFIType.lookupArgType(args.get(i));
if (argType instanceof LibFFIType.VoidType) {
throw new IllegalArgumentException("void is not a valid argument type");
}
switch (argType.allowedDataFlowDirection) {
case JAVA_TO_NATIVE_ONLY:
allowNativeToJavaCall = false;
break;
case NATIVE_TO_JAVA_ONLY:
allowJavaToNativeCall = false;
break;
}
this.argTypes[i] = argType;
}
if (allowNativeToJavaCall) {
if (allowJavaToNativeCall) {
this.allowedCallDirection = Direction.BOTH;
} else {
this.allowedCallDirection = Direction.NATIVE_TO_JAVA_ONLY;
}
} else {
if (allowJavaToNativeCall) {
this.allowedCallDirection = Direction.JAVA_TO_NATIVE_ONLY;
} else {
throw new IllegalArgumentException("invalid signature");
}
}
if (signature.isVarargs()) {
this.cif = prepareSignatureVarargs(this.retType, signature.getFixedArgCount(), this.argTypes);
} else {
this.cif = prepareSignature(this.retType, this.argTypes);
}
int primSize = 0;
int objCount = 0;
for (LibFFIType type : this.argTypes) {
int align = type.alignment;
if (primSize % align != 0) {
primSize += align - (primSize % align);
}
primSize += type.size;
objCount += type.objectCount;
}
this.primitiveSize = primSize;
this.objectCount = objCount;
}
public NativeArgumentBuffer.Array prepareBuffer() {
return new NativeArgumentBuffer.Array(primitiveSize, objectCount);
}
public LibFFIType[] getArgTypes() {
return argTypes;
}
public LibFFIType getRetType() {
return retType;
}
public Direction getAllowedCallDirection() {
return allowedCallDirection;
}
public Object execute(long functionPointer, NativeArgumentBuffer.Array argBuffer) {
CompilerAsserts.partialEvaluationConstant(retType);
if (retType instanceof LibFFIType.ObjectType) {
Object ret = executeObject(functionPointer, argBuffer.prim, argBuffer.getPatchCount(), argBuffer.patches, argBuffer.objects);
if (ret == null) {
return new NativePointer(0);
} else {
return ret;
}
} else if (retType instanceof LibFFIType.SimpleType) {
LibFFIType.SimpleType simpleType = (LibFFIType.SimpleType) retType;
long ret = executePrimitive(functionPointer, argBuffer.prim, argBuffer.getPatchCount(), argBuffer.patches, argBuffer.objects);
return simpleType.fromPrimitive(ret);
} else {
NativeArgumentBuffer.Array retBuffer = new NativeArgumentBuffer.Array(retType.size, retType.objectCount);
executeNative(functionPointer, argBuffer.prim, argBuffer.getPatchCount(), argBuffer.patches, argBuffer.objects, retBuffer.prim);
return retType.deserialize(retBuffer);
}
}
@TruffleBoundary
private native void executeNative(long functionPointer, byte[] primArgs, int patchCount, int[] patchOffsets, Object[] objArgs, byte[] ret);
@TruffleBoundary
private native long executePrimitive(long functionPointer, byte[] primArgs, int patchCount, int[] patchOffsets, Object[] objArgs);
@TruffleBoundary
private native TruffleObject executeObject(long functionPointer, byte[] primArgs, int patchCount, int[] patchOffsets, Object[] objArgs);
private static native long prepareSignature(LibFFIType retType, LibFFIType... args);
private static native long prepareSignatureVarargs(LibFFIType retType, int nFixedArgs, LibFFIType... args);
}