Implement varargs in interop for Kotlin N (#393)

* Implement CValues.equals and .hashCode

* Add trivial test for interop varargs

* Implement varargs in interop for Kotlin N

* Compile and link runtime with libffi

* Fix few places.
This commit is contained in:
SvyatoslavScherbina
2017-03-27 16:44:24 +03:00
committed by Nikolay Igotti
parent 334d2f0ee6
commit c4abb8f706
15 changed files with 340 additions and 13 deletions
@@ -272,7 +272,9 @@ internal class NativeIndexImpl(val language: Language) : NativeIndex() {
val definitionCursor = clang_getCursorDefinition(cursor)
val isDefined = (clang_Cursor_isNull(definitionCursor) == 0)
functionByName[name] = FunctionDecl(name, args, returnType, binaryName, isDefined)
val isVararg = clang_Cursor_isVariadic(cursor) != 0
functionByName[name] = FunctionDecl(name, args, returnType, binaryName, isDefined, isVararg)
}
CXIdxEntity_Enum -> {
@@ -70,7 +70,7 @@ class Parameter(val name: String?, val type: Type)
* C function declaration.
*/
class FunctionDecl(val name: String, val parameters: List<Parameter>, val returnType: Type, val binaryName: String,
val isDefined: Boolean)
val isDefined: Boolean, val isVararg: Boolean)
/**
* C typedef definition.
+2 -2
View File
@@ -23,9 +23,9 @@ model {
}
binaries.all {
cCompiler.args compilerArgsForJniIncludes
cCompiler.args "-I$libffiDir/include"
cCompiler.args "-I$hostLibffiDir/include"
linker.args "$libffiDir/lib/libffi.a"
linker.args "$hostLibffiDir/lib/libffi.a"
}
}
}
@@ -60,6 +60,37 @@ abstract class CValues<T : CVariable> : CValuesRef<T>() {
* Copies the values to [placement] and returns the pointer to the copy.
*/
override abstract fun getPointer(placement: NativePlacement): CPointer<T>
// TODO: optimize
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is CValues<*>) return false
val thisBytes = this.getBytes()
val otherBytes = other.getBytes()
if (thisBytes.size != otherBytes.size) {
return false
}
for (index in 0 .. thisBytes.size - 1) {
if (thisBytes[index] != otherBytes[index]) {
return false
}
}
return true
}
override fun hashCode(): Int {
var result = 0
for (byte in this.getBytes()) {
result = result * 31 + byte
}
return result
}
abstract val size: Int
}
fun <T : CVariable> CValues<T>.placeTo(placement: NativePlacement) = this.getPointer(placement)
@@ -158,6 +158,8 @@ fun <T : CVariable> zeroValue(size: Int, align: Int): CValue<T> = object : CValu
nativeMemUtils.zeroMemory(result, size)
return interpretCPointer(result.rawPtr)!!
}
override val size get() = size
}
inline fun <reified T : CVariable> zeroValue(): CValue<T> =
@@ -175,6 +177,7 @@ fun <T : CVariable> CPointed.readValues(size: Int, align: Int): CValues<T> {
return object : CValues<T>() {
override fun getPointer(placement: NativePlacement): CPointer<T> = placement.placeBytes(bytes, align)
override val size get() = bytes.size
}
}
@@ -186,6 +189,7 @@ fun <T : CVariable> CPointed.readValue(size: Int, align: Int): CValue<T> {
nativeMemUtils.getByteArray(this, bytes, size)
return object : CValue<T>() {
override fun getPointer(placement: NativePlacement): CPointer<T> = placement.placeBytes(bytes, align)
override val size get() = bytes.size
}
}
@@ -193,6 +197,19 @@ fun <T : CVariable> CPointed.readValue(size: Int, align: Int): CValue<T> {
// TODO: find better name.
inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(sizeOf<T>().toInt(), alignOf<T>())
// TODO: optimize
fun <T : CVariable> CValues<T>.getBytes(): ByteArray = memScoped {
val result = ByteArray(size)
nativeMemUtils.getByteArray(
source = this@getBytes.placeTo(memScope).reinterpret<CInt8Var>().pointed,
dest = result,
length = result.size
)
result
}
/**
* Calls the [block] with temporary copy if this value as receiver.
*/
@@ -215,6 +232,7 @@ inline fun <reified T : CVariable> createValues(count: Int, initializer: T.(inde
fun cValuesOf(vararg elements: Byte): CValues<CInt8Var> = object : CValues<CInt8Var>() {
override fun getPointer(placement: NativePlacement) = placement.allocArrayOf(elements)[0].ptr
override val size get() = 1 * elements.size
}
// TODO: optimize other [cValuesOf] methods:
@@ -230,6 +248,7 @@ fun cValuesOf(vararg elements: Long): CValues<CInt64Var> =
fun cValuesOf(vararg elements: Float): CValues<CFloat32Var> = object : CValues<CFloat32Var>() {
override fun getPointer(placement: NativePlacement) = placement.allocArrayOf(*elements)[0].ptr
override val size get() = 4 * elements.size
}
fun cValuesOf(vararg elements: Double): CValues<CFloat64Var> =
@@ -258,6 +277,8 @@ val String.cstr: CValues<CInt8Var>
val bytes = encodeToUtf8(this)
return object : CValues<CInt8Var>() {
override val size get() = bytes.size + 1
override fun getPointer(placement: NativePlacement): CPointer<CInt8Var> {
val result = placement.allocArray<CInt8Var>(bytes.size + 1)
nativeMemUtils.putByteArray(bytes, result, bytes.size)
@@ -0,0 +1,111 @@
package kotlinx.cinterop
private const val MAX_ARGUMENT_SIZE = 8
typealias FfiTypeKind = Int
// Also declared in Interop.cpp
const val FFI_TYPE_KIND_VOID: FfiTypeKind = 0
const val FFI_TYPE_KIND_SINT8: FfiTypeKind = 1
const val FFI_TYPE_KIND_SINT16: FfiTypeKind = 2
const val FFI_TYPE_KIND_SINT32: FfiTypeKind = 3
const val FFI_TYPE_KIND_SINT64: FfiTypeKind = 4
const val FFI_TYPE_KIND_FLOAT: FfiTypeKind = 5
const val FFI_TYPE_KIND_DOUBLE: FfiTypeKind = 6
const val FFI_TYPE_KIND_POINTER: FfiTypeKind = 7
private tailrec fun convertArgument(
argument: Any?, isVariadic: Boolean, location: NativePointed,
additionalPlacement: NativePlacement
): FfiTypeKind = when (argument) {
is CValuesRef<*>? -> {
location.reinterpret<CPointerVar<*>>().value = argument?.getPointer(additionalPlacement)
FFI_TYPE_KIND_POINTER
}
is String -> {
location.reinterpret<CPointerVar<*>>().value = argument.cstr.getPointer(additionalPlacement)
FFI_TYPE_KIND_POINTER
}
is Int -> {
location.reinterpret<CInt32Var>().value = argument
FFI_TYPE_KIND_SINT32
}
is Long -> {
location.reinterpret<CInt64Var>().value = argument
FFI_TYPE_KIND_SINT64
}
is Byte -> if (isVariadic) {
convertArgument(argument.toInt(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<CInt8Var>().value = argument
FFI_TYPE_KIND_SINT8
}
is Short -> if (isVariadic) {
convertArgument(argument.toInt(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<CInt16Var>().value = argument
FFI_TYPE_KIND_SINT16
}
is Double -> {
location.reinterpret<CFloat64Var>().value = argument
FFI_TYPE_KIND_DOUBLE
}
is Float -> if (isVariadic) {
convertArgument(argument.toDouble(), isVariadic, location, additionalPlacement)
} else {
location.reinterpret<CFloat32Var>().value = argument
FFI_TYPE_KIND_FLOAT
}
else -> throw Error("unsupported argument: $argument")
}
fun callWithVarargs(codePtr: NativePtr, returnValuePtr: NativePtr, returnTypeKind: FfiTypeKind,
fixedArguments: Array<out Any?>, variadicArguments: Array<out Any?>,
argumentsPlacement: NativePlacement) {
val totalArgumentsNumber = fixedArguments.size + variadicArguments.size
// All supported arguments take at most 8 bytes each:
val argumentsStorage = argumentsPlacement.allocArray<CInt64Var>(totalArgumentsNumber)
val arguments = argumentsPlacement.allocArray<CPointerVar<*>>(totalArgumentsNumber)
val types = argumentsPlacement.allocArray<CPointerVar<*>>(totalArgumentsNumber)
var index = 0
inline fun addArgument(argument: Any?, isVariadic: Boolean) {
val storage = argumentsStorage[index]
val typeKind = convertArgument(argument, isVariadic = isVariadic,
location = storage, additionalPlacement = argumentsPlacement)
types[index].value = interpretCPointer<COpaque>(nativeNullPtr + typeKind.toLong())
arguments[index].value = storage.ptr
++index
}
for (argument in fixedArguments) {
addArgument(argument, isVariadic = false)
}
for (argument in variadicArguments) {
addArgument(argument, isVariadic = true)
}
assert (index == totalArgumentsNumber)
callWithVarargs(codePtr, returnValuePtr, returnTypeKind, arguments.rawPtr, types.rawPtr,
fixedArguments.size, totalArgumentsNumber)
}
@SymbolName("callWithVarargs")
private external fun callWithVarargs(codePtr: NativePtr, returnValuePtr: NativePtr, returnTypeKind: FfiTypeKind,
arguments: NativePtr, argumentTypeKinds: NativePtr,
fixedArgumentsNumber: Int, totalArgumentsNumber: Int)
@@ -694,6 +694,13 @@ class StubGenerator(
}
}
private fun FunctionDecl.generateAsFfiVarargs(): Boolean = (platform == KotlinPlatform.NATIVE && this.isVararg &&
// Neither takes nor returns structs by value or enum-typed values:
(this.parameters.map { it.type } + this.returnType).all {
val type = it.unwrapTypedefs()
type !is RecordType && type !is EnumType
})
/**
* Constructs [InValueBinding] for return value of Kotlin binding for given C function.
*/
@@ -760,6 +767,30 @@ class StubGenerator(
"${name.asSimpleName()}: " + paramBindings[i].kotlinType
}.joinToString(", ")
if (func.generateAsFfiVarargs()) {
val returnTypeKind = getFfiTypeKind(func.returnType)
val variadicParameter = "vararg variadicArguments: Any?"
val allParameters = if (args.isEmpty()) variadicParameter else "$args, $variadicParameter"
val header = "fun ${func.name.asSimpleName()}($allParameters): ${retValBinding.kotlinType} = memScoped"
val returnValueMirror = mirror(func.returnType)
block(header) {
val resultPtr = if (!func.returnsVoid()) {
out("val resultVar = alloc<${returnValueMirror.pointedTypeName}>()")
"resultVar.rawPtr"
} else {
"nativeNullPtr"
}
out("callWithVarargs(${func.kotlinExternalName}(), $resultPtr, $returnTypeKind, " +
"arrayOf(${paramNames.joinToString()}), variadicArguments, memScope)")
if (!func.returnsVoid()) {
out("resultVar.value")
}
}
return
}
val header = "fun ${func.name.asSimpleName()}($args): ${retValBinding.kotlinType}"
if (!func.requiresKotlinAdapter()) {
@@ -825,6 +856,27 @@ class StubGenerator(
}
}
private fun getFfiTypeKind(type: Type): String {
val unwrappedType = type.unwrapTypedefs()
return when (unwrappedType) {
is VoidType -> "FFI_TYPE_KIND_VOID"
is PointerType -> "FFI_TYPE_KIND_POINTER"
is IntegerType -> when (unwrappedType.size) {
1 -> "FFI_TYPE_KIND_SINT8"
2 -> "FFI_TYPE_KIND_SINT16"
4 -> "FFI_TYPE_KIND_SINT32"
8 -> "FFI_TYPE_KIND_SINT64"
else -> TODO(unwrappedType.toString())
}
is FloatingType -> when (unwrappedType.size) {
4 -> "FFI_TYPE_KIND_FLOAT"
8 -> "FFI_TYPE_KIND_DOUBLE"
else -> TODO(unwrappedType.toString())
}
else -> TODO(unwrappedType.toString())
}
}
/**
* Returns the expression to be parsed by Kotlin as string literal with given contents,
* i.e. transforms `foo$bar` to `"foo\$bar"`.
@@ -901,7 +953,7 @@ class StubGenerator(
return true
}
return this.returnsRecord() ||
return this.returnsRecord() || this.generateAsFfiVarargs() ||
this.parameters.map { it.type }.any {
it.unwrapTypedefs() is RecordType ||
representCFunctionParameterAsString(it) ||
@@ -919,6 +971,7 @@ class StubGenerator(
}
return this.isDefined ||
this.generateAsFfiVarargs() ||
this.returnsRecord() ||
this.parameters.map { it.type }.any { it.unwrapTypedefs() is RecordType }
}
@@ -1077,6 +1130,14 @@ class StubGenerator(
* Produces to [out] the definition of Kotlin JNI function used in binding for given C function.
*/
private fun generateKotlinExternalMethod(func: FunctionDecl) {
if (func.generateAsFfiVarargs()) {
assert (platform == KotlinPlatform.NATIVE)
// The C stub simply returns pointer to the function:
out(func.symbolNameAnnotation)
out("private external fun ${func.kotlinExternalName}(): NativePtr")
return
}
val paramNames = paramNames(func)
val paramBindings = paramBindings(func)
val retValBinding = retValBinding(func)
@@ -1266,6 +1327,13 @@ class StubGenerator(
* Produces to [out] the implementation of JNI function used in Kotlin binding for given C function.
*/
private fun generateCJniFunction(func: FunctionDecl) {
if (func.generateAsFfiVarargs()) {
assert (platform == KotlinPlatform.NATIVE)
// The C stub simply returns pointer to the function:
out("void* ${func.cStubName}() { return ${func.name}; }")
return
}
val paramNames = paramNames(func)
val paramBindings = paramBindings(func)
val retValBinding = retValBinding(func)
@@ -44,6 +44,8 @@ class Distribution(val config: CompilerConfiguration) {
sysRoot
}
val libffi = "$dependenciesDir/${properties.propertyString("libffiDir.$suffix")}/lib/libffi.a"
val llvmBin = "$llvmHome/bin"
val llvmLib = "$llvmHome/lib"
@@ -63,7 +63,7 @@ internal class IPhoneOSfromMacOSPlatform(distribution: Distribution)
override val arch = properties.propertyString("arch.osx-ios")!!
override val osVersionMin = properties.propertyList("osVersionMin.osx-ios")
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx-ios")
override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios")!!}"
override val targetSysRoot = "${distribution.dependenciesDir}/${properties.propertyString("targetSysRoot.osx-ios")!!}"
}
internal class IPhoneSimulatorFromMacOSPlatform(distribution: Distribution)
@@ -72,7 +72,7 @@ internal class IPhoneSimulatorFromMacOSPlatform(distribution: Distribution)
override val arch = properties.propertyString("arch.osx-ios-sim")!!
override val osVersionMin = properties.propertyList("osVersionMin.osx-ios-sim")
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx-ios-sim")
override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios-sim")!!}"
override val targetSysRoot = "${distribution.dependenciesDir}/${properties.propertyString("targetSysRoot.osx-ios-sim")!!}"
}
internal open class LinuxPlatform(distribution: Distribution)
@@ -119,7 +119,7 @@ internal open class LinuxPlatform(distribution: Distribution)
internal class RaspberryPiPlatform(distribution: Distribution)
: LinuxPlatform(distribution) {
override val sysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.linux-raspberrypi")!!}"
override val sysRoot = "${distribution.dependenciesDir}/${properties.propertyString("targetSysRoot.linux-raspberrypi")!!}"
override fun linkCommand(objectFiles: List<String>, executable: String, optimize: Boolean): List<String> {
// TODO: Can we extract more to the konan.properties?
@@ -229,6 +229,7 @@ internal class LinkStage(val context: Context) {
fun link(objectFiles: List<ObjectFile>): ExecutableFile {
val executable = config.get(KonanConfigKeys.EXECUTABLE_FILE)!!
val linkCommand = platform.linkCommand(objectFiles, executable, optimize) +
distribution.libffi +
asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) +
entryPointSelector
+11 -4
View File
@@ -6,6 +6,7 @@ dependenciesUrl = https://jetbrains.bintray.com/kotlin-native-dependencies
# macbook
arch.osx = x86_64
sysRoot.osx = target-sysroot-1-darwin-macos
libffiDir.osx = libffi-3.2.1-2-darwin-macos
llvmHome.osx = clang+llvm-3.9.0-darwin-macos
llvmLtoFlags.osx = -O3 -function-sections -exported-symbol=_Konan_main
llvmLlcFlags.osx = -mtriple=x86_64-apple-macosx10.10.0 --disable-fp-elim
@@ -13,12 +14,13 @@ linkerKonanFlags.osx = -lc++
linkerOptimizationFlags.osx = -dead_strip
osVersionMin.osx = -macosx_version_min 10.10.0
entrySelector.osx = -alias _Konan_main _main
dependencies.osx = target-sysroot-1-darwin-macos clang+llvm-3.9.0-darwin-macos
dependencies.osx = target-sysroot-1-darwin-macos libffi-3.2.1-2-darwin-macos clang+llvm-3.9.0-darwin-macos
# iphone
arch.osx-ios = arm64
sysRoot.osx-ios = target-sysroot-1-darwin-macos
targetSysRoot.osx-ios = target-sysroot-2-darwin-ios
libffiDir.osx-ios = libffi-3.2.1-2-darwin-ios
llvmHome.osx-ios = clang+llvm-3.9.0-darwin-macos
llvmLtoFlags.osx-ios = -O3 -function-sections -exported-symbol=_Konan_main
llvmLlcFlags.osx-ios = -mtriple=arm64-apple-ios5.0.0 --disable-fp-elim
@@ -27,6 +29,7 @@ linkerOptimizationFlags.osx-ios = -dead_strip
osVersionMin.osx-ios = -iphoneos_version_min 5.0.0
entrySelector.osx-ios = -alias _Konan_main _main
dependencies.osx-ios = target-sysroot-1-darwin-macos \
libffi-3.2.1-2-darwin-ios \
clang+llvm-3.9.0-darwin-macos \
target-sysroot-2-darwin-ios
@@ -34,6 +37,7 @@ dependencies.osx-ios = target-sysroot-1-darwin-macos \
arch.osx-ios-sim = x86_64
sysRoot.osx-ios-sim = target-sysroot-1-darwin-macos
targetSysRoot.osx-ios-sim = target-sysroot-1-darwin-ios-sim
libffiDir.osx-ios-sim = libffi-3.2.1-2-darwin-ios-sim
llvmHome.osx-ios-sim = clang+llvm-3.9.0-darwin-macos
llvmLtoFlags.osx-ios-sim = -O3 -function-sections -exported-symbol=_Konan_main
llvmLlcFlags.osx-ios-sim = -mtriple=x86_64-apple-ios5.0.0 --disable-fp-elim
@@ -42,11 +46,13 @@ linkerOptimizationFlags.osx-ios-sim = -dead_strip
osVersionMin.osx-ios-sim = -ios_simulator_version_min 5.0.0
entrySelector.osx-ios-sim = -alias _Konan_main _main
dependencies.osx-ios-sim = target-sysroot-1-darwin-macos \
libffi-3.2.1-2-darwin-ios-sim \
clang+llvm-3.9.0-darwin-macos \
target-sysroot-1-darwin-ios-sim
# linux
sysRoot.linux = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot
libffiDir.linux = libffi-3.2.1-2-linux-x86-64
llvmHome.linux = clang+llvm-3.9.0-linux-x86-64
libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux-gnu/4.8.5
gccToolChain.linux = target-gcc-toolchain-3-linux-x86-64
@@ -56,12 +62,13 @@ linkerKonanFlags.linux = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread
linkerOptimizationFlags.linux = --gc-sections
pluginOptimizationFlags.linux = -plugin-opt=mcpu=x86-64 -plugin-opt=O3
entrySelector.linux = --defsym main=Konan_main
dependencies.linux = clang+llvm-3.9.0-linux-x86-64 target-gcc-toolchain-3-linux-x86-64
dependencies.linux = clang+llvm-3.9.0-linux-x86-64 target-gcc-toolchain-3-linux-x86-64 libffi-3.2.1-2-linux-x86-64
# Raspberry Pi
arch.linux-raspberrypi = armv7
sysRoot.linux-raspberrypi = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot
targetSysRoot.linux-raspberrypi = target-sysroot-1-raspberrypi
libffiDir.linux-raspberrypi = libffi-3.2.1-2-raspberrypi
llvmHome.linux-raspberrypi = clang+llvm-3.9.0-linux-x86-64
libGcc.linux-raspberrypi = target-sysroot-1-raspberrypi/lib/gcc/arm-linux-gnueabihf/4.8.3/
gccToolChain.linux-raspberrypi = target-gcc-toolchain-3-linux-x86-64
@@ -71,5 +78,5 @@ linkerKonanFlags.linux-raspberrypi = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lc -l
entrySelector.linux-raspberrypi = --defsym main=Konan_main
dependencies.raspberrypi = clang+llvm-3.9.0-linux-x86-64 \
target-gcc-toolchain-3-linux-x86-64 \
target-sysroot-1-raspberrypi
target-sysroot-1-raspberrypi \
libffi-3.2.1-2-linux-x86-64
+6
View File
@@ -1617,6 +1617,12 @@ task interop3(type: RunInteropKonanTest) {
interop = 'cstdlib'
}
task interop4(type: RunInteropKonanTest) {
goldValue = "a b -1 2 3 9223372036854775807 0.1 0.2\n1 42\n"
source = "interop/basics/4.kt"
interop = 'cstdio'
}
task interop_echo_server(type: RunInteropKonanTest) {
if (isLinux()) {
disabled = true
+14
View File
@@ -0,0 +1,14 @@
import cstdio.*
import kotlinx.cinterop.*
fun main(args: Array<String>) {
printf("%s %s %d %d %d %lld %.1f %.1lf\n",
"a", "b".cstr, (-1).toByte(), 2.toShort(), 3, Long.MAX_VALUE, 0.1.toFloat(), 0.2)
memScoped {
val aVar = alloc<CInt32Var>()
val bVar = alloc<CInt32Var>()
val sscanfResult = sscanf("42", "%d%d", aVar.ptr, bVar.ptr)
printf("%d %d\n", sscanfResult, aVar.value)
}
}
+7 -1
View File
@@ -70,7 +70,7 @@ class TgzNativeDep extends NativeDep {
}
task libffi(type: TgzNativeDep) {
task hostLibffi(type: TgzNativeDep) {
baseName = "libffi-3.2.1-2-$host"
}
@@ -85,6 +85,9 @@ if (isLinux()) {
task raspberryPiSysroot(type: TgzNativeDep) {
baseName = "target-sysroot-1-raspberrypi"
}
task raspberrypiLibffi(type: TgzNativeDep) {
baseName = "libffi-3.2.1-2-raspberrypi"
}
} else {
task hostSysroot(type: TgzNativeDep) {
baseName = "target-sysroot-1-$host"
@@ -92,6 +95,9 @@ if (isLinux()) {
task iphoneSysroot(type: TgzNativeDep) {
baseName = "target-sysroot-2-darwin-ios"
}
task iphoneLibffi(type: TgzNativeDep) {
baseName = "libffi-3.2.1-2-darwin-ios"
}
// TODO: re-enable when we known how to bring the simulator
// sysroot to dependencies.
// task iphoneSimSysroot(type: TgzNativeDep) {
+1
View File
@@ -11,6 +11,7 @@ targetList.each { targetName ->
target targetName
compilerArgs targetArgs[targetName]
compilerArgs '-I' + project.file('../common/src/hash/headers')
compilerArgs '-I' + project.file(rootProject.ext.get("${targetName}LibffiDir") + "/include")
linkerArgs project.file("../common/build/$targetName/hash.bc").path
}
}
+57
View File
@@ -0,0 +1,57 @@
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <ffi.h>
namespace {
typedef int FfiTypeKind;
// Also declared in Varargs.kt
const FfiTypeKind FFI_TYPE_KIND_VOID = 0;
const FfiTypeKind FFI_TYPE_KIND_SINT8 = 1;
const FfiTypeKind FFI_TYPE_KIND_SINT16 = 2;
const FfiTypeKind FFI_TYPE_KIND_SINT32 = 3;
const FfiTypeKind FFI_TYPE_KIND_SINT64 = 4;
const FfiTypeKind FFI_TYPE_KIND_FLOAT = 5;
const FfiTypeKind FFI_TYPE_KIND_DOUBLE = 6;
const FfiTypeKind FFI_TYPE_KIND_POINTER = 7;
ffi_type* convertFfiTypeKindToType(FfiTypeKind typeKind) {
switch (typeKind) {
case FFI_TYPE_KIND_VOID: return &ffi_type_void;
case FFI_TYPE_KIND_SINT8: return &ffi_type_sint8;
case FFI_TYPE_KIND_SINT16: return &ffi_type_sint16;
case FFI_TYPE_KIND_SINT32: return &ffi_type_sint32;
case FFI_TYPE_KIND_SINT64: return &ffi_type_sint64;
case FFI_TYPE_KIND_FLOAT: return &ffi_type_float;
case FFI_TYPE_KIND_DOUBLE: return &ffi_type_double;
case FFI_TYPE_KIND_POINTER: return &ffi_type_pointer;
default: assert(false);
}
}
} // namespace
extern "C" {
void callWithVarargs(void* codePtr, void* returnValuePtr, FfiTypeKind returnTypeKind,
void** arguments, intptr_t* argumentTypeKinds,
int fixedArgumentsNumber, int totalArgumentsNumber) {
ffi_type** argumentTypes = (ffi_type**)argumentTypeKinds;
// In-place convertion:
for (int i = 0; i < totalArgumentsNumber; ++i) {
argumentTypes[i] = convertFfiTypeKindToType((FfiTypeKind) argumentTypeKinds[i]);
}
ffi_cif cif;
ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI,
fixedArgumentsNumber, totalArgumentsNumber,
convertFfiTypeKindToType(returnTypeKind), argumentTypes);
ffi_call(&cif, (void (*)())codePtr, returnValuePtr, arguments);
}
}