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:
committed by
Nikolay Igotti
parent
334d2f0ee6
commit
c4abb8f706
+3
-1
@@ -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 -> {
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
+69
-1
@@ -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)
|
||||
|
||||
+2
@@ -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"
|
||||
|
||||
|
||||
+4
-3
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Vendored
+7
-1
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user