Implement C stubs generation in compiler
Use it in * C varargs support (thus dropping libffi from runtime) * C callbacks support (thus enabling callbacks with structs)
This commit is contained in:
committed by
SvyatoslavScherbina
parent
7f2103a0ee
commit
6ca196399a
+29
-42
@@ -27,14 +27,11 @@ private class StructDeclImpl(spelling: String, override val location: Location)
|
||||
|
||||
private class StructDefImpl(
|
||||
size: Long, align: Int, decl: StructDecl,
|
||||
hasNaturalLayout: Boolean
|
||||
override val kind: Kind
|
||||
) : StructDef(
|
||||
size, align, decl,
|
||||
hasNaturalLayout = hasNaturalLayout
|
||||
size, align, decl
|
||||
) {
|
||||
|
||||
override val fields = mutableListOf<Field>()
|
||||
override val bitFields = mutableListOf<BitField>()
|
||||
override val members = mutableListOf<StructMember>()
|
||||
}
|
||||
|
||||
private class EnumDefImpl(spelling: String, type: Type, override val location: Location) : EnumDef(spelling, type) {
|
||||
@@ -196,39 +193,56 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
|
||||
private fun createStructDef(structDecl: StructDeclImpl, cursor: CValue<CXCursor>) {
|
||||
val type = clang_getCursorType(cursor)
|
||||
|
||||
val fields = mutableListOf<StructMember>()
|
||||
addDeclaredFields(fields, type, type)
|
||||
|
||||
val size = clang_Type_getSizeOf(type)
|
||||
val align = clang_Type_getAlignOf(type).toInt()
|
||||
|
||||
val structDef = StructDefImpl(
|
||||
size, align, structDecl,
|
||||
hasNaturalLayout = structHasNaturalLayout(cursor)
|
||||
when (cursor.kind) {
|
||||
CXCursorKind.CXCursor_UnionDecl -> StructDef.Kind.UNION
|
||||
CXCursorKind.CXCursor_StructDecl -> StructDef.Kind.STRUCT
|
||||
else -> error(cursor.kind)
|
||||
}
|
||||
)
|
||||
|
||||
structDecl.def = structDef
|
||||
structDef.members += fields
|
||||
|
||||
addDeclaredFields(structDef, type, type)
|
||||
structDecl.def = structDef
|
||||
}
|
||||
|
||||
private fun addDeclaredFields(structDef: StructDefImpl, structType: CValue<CXType>, containerType: CValue<CXType>) {
|
||||
private fun addDeclaredFields(result: MutableList<StructMember>, structType: CValue<CXType>, containerType: CValue<CXType>) {
|
||||
getFields(containerType).forEach { fieldCursor ->
|
||||
val name = getCursorSpelling(fieldCursor)
|
||||
if (name.isNotEmpty()) {
|
||||
val fieldType = convertCursorType(fieldCursor)
|
||||
val offset = clang_Type_getOffsetOf(structType, name)
|
||||
if (clang_Cursor_isBitField(fieldCursor) == 0) {
|
||||
val typeAlign = clang_Type_getAlignOf(clang_getCursorType(fieldCursor))
|
||||
structDef.fields.add(Field(name, fieldType, offset, typeAlign))
|
||||
val member = if (offset < 0) {
|
||||
IncompleteField(name, fieldType)
|
||||
} else if (clang_Cursor_isBitField(fieldCursor) == 0) {
|
||||
val canonicalFieldType = clang_getCanonicalType(clang_getCursorType(fieldCursor))
|
||||
Field(
|
||||
name,
|
||||
fieldType,
|
||||
offset,
|
||||
clang_Type_getSizeOf(canonicalFieldType),
|
||||
clang_Type_getAlignOf(canonicalFieldType)
|
||||
)
|
||||
} else {
|
||||
val size = clang_getFieldDeclBitWidth(fieldCursor)
|
||||
structDef.bitFields.add(BitField(name, fieldType, offset, size))
|
||||
BitField(name, fieldType, offset, size)
|
||||
}
|
||||
result.add(member)
|
||||
} else {
|
||||
// Unnamed field.
|
||||
val fieldType = clang_getCursorType(fieldCursor)
|
||||
when (fieldType.kind) {
|
||||
CXTypeKind.CXType_Record -> {
|
||||
// Unnamed struct fields also contribute their fields:
|
||||
addDeclaredFields(structDef, structType, fieldType)
|
||||
addDeclaredFields(result, structType, fieldType)
|
||||
}
|
||||
else -> {
|
||||
// Nothing.
|
||||
@@ -447,33 +461,6 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
return Typedef(typedefDef)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes [StructDef.hasNaturalLayout] property.
|
||||
*/
|
||||
fun structHasNaturalLayout(structDefCursor: CValue<CXCursor>): Boolean {
|
||||
val defKind = structDefCursor.kind
|
||||
|
||||
when (defKind) {
|
||||
|
||||
CXCursorKind.CXCursor_UnionDecl -> return false
|
||||
|
||||
CXCursorKind.CXCursor_StructDecl -> {
|
||||
var hasAttributes = false
|
||||
|
||||
visitChildren(structDefCursor) { cursor, _ ->
|
||||
if (clang_isAttribute(cursor.kind) != 0) {
|
||||
hasAttributes = true
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
return !hasAttributes
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException(defKind.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertCursorType(cursor: CValue<CXCursor>) =
|
||||
convertType(clang_getCursorType(cursor), clang_getDeclTypeAttributes(cursor))
|
||||
|
||||
|
||||
+21
-8
@@ -85,15 +85,24 @@ interface TypeDeclaration {
|
||||
val location: Location
|
||||
}
|
||||
|
||||
sealed class StructMember(val name: String, val type: Type) {
|
||||
abstract val offset: Long?
|
||||
}
|
||||
|
||||
/**
|
||||
* C struct field.
|
||||
*/
|
||||
class Field(val name: String, val type: Type, val offset: Long, val typeAlign: Long)
|
||||
class Field(name: String, type: Type, override val offset: Long, val typeSize: Long, val typeAlign: Long)
|
||||
: StructMember(name, type)
|
||||
|
||||
val Field.isAligned: Boolean
|
||||
get() = offset % (typeAlign * 8) == 0L
|
||||
|
||||
class BitField(val name: String, val type: Type, val offset: Long, val size: Int)
|
||||
class BitField(name: String, type: Type, override val offset: Long, val size: Int) : StructMember(name, type)
|
||||
|
||||
class IncompleteField(name: String, type: Type) : StructMember(name, type) {
|
||||
override val offset: Long? get() = null
|
||||
}
|
||||
|
||||
/**
|
||||
* C struct declaration.
|
||||
@@ -109,13 +118,17 @@ abstract class StructDecl(val spelling: String) : TypeDeclaration {
|
||||
* @param hasNaturalLayout must be `false` if the struct has unnatural layout, e.g. it is `packed`.
|
||||
* May be `false` even if the struct has natural layout.
|
||||
*/
|
||||
abstract class StructDef(val size: Long, val align: Int,
|
||||
val decl: StructDecl,
|
||||
val hasNaturalLayout: Boolean) {
|
||||
abstract class StructDef(val size: Long, val align: Int, val decl: StructDecl) {
|
||||
|
||||
abstract val fields: List<Field>
|
||||
// TODO: merge two lists to preserve declaration order.
|
||||
abstract val bitFields: List<BitField>
|
||||
enum class Kind {
|
||||
STRUCT, UNION
|
||||
}
|
||||
|
||||
abstract val members: List<StructMember>
|
||||
abstract val kind: Kind
|
||||
|
||||
val fields: List<Field> get() = members.filterIsInstance<Field>()
|
||||
val bitFields: List<BitField> get() = members.filterIsInstance<BitField>()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -183,6 +183,23 @@ internal fun getFields(type: CValue<CXType>): List<CValue<CXCursor>> {
|
||||
return result
|
||||
}
|
||||
|
||||
fun StructDef.fieldsHaveDefaultAlignment(): Boolean {
|
||||
fun alignUp(x: Long, alignment: Long): Long = (x + alignment - 1) and (alignment - 1).inv()
|
||||
|
||||
var offset = 0L
|
||||
this.members.forEach {
|
||||
when (it) {
|
||||
is Field -> {
|
||||
if (alignUp(offset, it.typeAlign) * 8 != it.offset) return false
|
||||
offset = it.offset / 8 + it.typeSize
|
||||
}
|
||||
is BitField -> return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun CValue<CXCursor>.isLeaf(): Boolean {
|
||||
var hasChildren = false
|
||||
|
||||
|
||||
@@ -107,7 +107,11 @@ class Arena(parent: NativeFreeablePlacement = nativeHeap) : ArenaBase(parent) {
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
inline fun <reified T : CVariable> NativePlacement.alloc(): T =
|
||||
alloc(sizeOf<T>(), alignOf<T>()).reinterpret()
|
||||
alloc(typeOf<T>()).reinterpret()
|
||||
|
||||
@PublishedApi
|
||||
internal fun NativePlacement.alloc(type: CVariable.Type): NativePointed =
|
||||
alloc(type.size, type.align)
|
||||
|
||||
/**
|
||||
* Allocates variable of given type and initializes it applying given block.
|
||||
@@ -263,9 +267,12 @@ fun <T : CVariable> CPointed.readValue(size: Long, align: Int): CValue<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@PublishedApi internal fun <T : CVariable> CPointed.readValue(type: CVariable.Type): CValue<T> =
|
||||
readValue(type.size, type.align)
|
||||
|
||||
// Note: can't be declared as property due to possible clash with a struct field.
|
||||
// TODO: find better name.
|
||||
inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(sizeOf<T>(), alignOf<T>())
|
||||
inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(typeOf<T>())
|
||||
|
||||
fun CValue<*>.write(location: NativePtr) {
|
||||
// TODO: probably CValue must be redesigned.
|
||||
|
||||
@@ -65,89 +65,3 @@ import kotlin.native.internal.ExportForCompiler
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplUnitRet(ptr: COpaquePointer, vararg args: Any?): Unit = memScoped {
|
||||
callWithVarargs(ptr.rawValue, nativeNullPtr, FFI_TYPE_KIND_VOID, args, null, memScope)
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplBooleanRet(ptr: COpaquePointer, vararg args: Any?): Boolean =
|
||||
invokeImplByteRet(ptr, *args).toBoolean()
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplByteRet(ptr: COpaquePointer, vararg args: Any?): Byte = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<ByteVar>(ByteVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_SINT8, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplShortRet(ptr: COpaquePointer, vararg args: Any?): Short = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<ShortVar>(ShortVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_SINT16, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplIntRet(ptr: COpaquePointer, vararg args: Any?): Int = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<IntVar>(IntVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_SINT32, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplLongRet(ptr: COpaquePointer, vararg args: Any?): Long = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<LongVar>(LongVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_SINT64, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplUByteRet(ptr: COpaquePointer, vararg args: Any?): UByte = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<UByteVar>(UByteVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT8, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplUShortRet(ptr: COpaquePointer, vararg args: Any?): UShort = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<UShortVar>(UShortVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT16, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplUIntRet(ptr: COpaquePointer, vararg args: Any?): UInt = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<UIntVar>(UIntVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT32, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplULongRet(ptr: COpaquePointer, vararg args: Any?): ULong = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<ULongVar>(ULongVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_UINT64, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplFloatRet(ptr: COpaquePointer, vararg args: Any?): Float = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<FloatVar>(FloatVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_FLOAT, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplDoubleRet(ptr: COpaquePointer, vararg args: Any?): Double = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<DoubleVar>(DoubleVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_DOUBLE, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@ExportForCompiler
|
||||
private fun invokeImplPointerRet(ptr: COpaquePointer, vararg args: Any?): COpaquePointer? = memScoped {
|
||||
val resultBuffer = allocFfiReturnValueBuffer<COpaquePointerVar>(COpaquePointerVar)
|
||||
callWithVarargs(ptr.rawValue, resultBuffer.rawPtr, FFI_TYPE_KIND_POINTER, args, null, memScope)
|
||||
resultBuffer.value
|
||||
}
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package kotlinx.cinterop
|
||||
import kotlin.native.*
|
||||
|
||||
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
|
||||
const val FFI_TYPE_KIND_UINT8: FfiTypeKind = 8
|
||||
const val FFI_TYPE_KIND_UINT16: FfiTypeKind = 9
|
||||
const val FFI_TYPE_KIND_UINT32: FfiTypeKind = 10
|
||||
const val FFI_TYPE_KIND_UINT64: FfiTypeKind = 11
|
||||
|
||||
private tailrec fun convertArgument(
|
||||
argument: Any?, isVariadic: Boolean, location: COpaquePointer,
|
||||
additionalPlacement: AutofreeScope
|
||||
): FfiTypeKind = when (argument) { // FIXME: optimize
|
||||
is CValuesRef<*>? -> {
|
||||
location.reinterpret<CPointerVar<*>>()[0] = argument?.getPointer(additionalPlacement)
|
||||
FFI_TYPE_KIND_POINTER
|
||||
}
|
||||
|
||||
is String -> {
|
||||
location.reinterpret<CPointerVar<*>>()[0] = if (!isVariadic) {
|
||||
// If it is fixed argument, then it is not C string because it must have been already converted;
|
||||
// then treat it as NSString.
|
||||
// TODO: handle fixed NSString arguments in the stub instead.
|
||||
interpretCPointer<COpaque>(argument.objcPtr())
|
||||
} else {
|
||||
// It is passed as variadic argument; no type information available, so treat it as C string.
|
||||
argument.cstr.getPointer(additionalPlacement)
|
||||
}
|
||||
FFI_TYPE_KIND_POINTER
|
||||
}
|
||||
|
||||
is Int -> {
|
||||
location.reinterpret<IntVar>()[0] = argument
|
||||
FFI_TYPE_KIND_SINT32
|
||||
}
|
||||
|
||||
is Long -> {
|
||||
location.reinterpret<LongVar>()[0] = argument
|
||||
FFI_TYPE_KIND_SINT64
|
||||
}
|
||||
|
||||
is Boolean -> convertArgument(argument.toByte(), isVariadic, location, additionalPlacement)
|
||||
|
||||
is Byte -> if (isVariadic) {
|
||||
convertArgument(argument.toInt(), isVariadic, location, additionalPlacement)
|
||||
} else {
|
||||
location.reinterpret<ByteVar>()[0] = argument
|
||||
FFI_TYPE_KIND_SINT8
|
||||
}
|
||||
|
||||
is Short -> if (isVariadic) {
|
||||
convertArgument(argument.toInt(), isVariadic, location, additionalPlacement)
|
||||
} else {
|
||||
location.reinterpret<ShortVar>()[0] = argument
|
||||
FFI_TYPE_KIND_SINT16
|
||||
}
|
||||
|
||||
is UInt -> {
|
||||
location.reinterpret<UIntVar>()[0] = argument
|
||||
FFI_TYPE_KIND_UINT32
|
||||
}
|
||||
|
||||
is ULong -> {
|
||||
location.reinterpret<ULongVar>()[0] = argument
|
||||
FFI_TYPE_KIND_UINT64
|
||||
}
|
||||
|
||||
is UByte -> if (isVariadic) {
|
||||
convertArgument(argument.toUInt(), isVariadic, location, additionalPlacement)
|
||||
} else {
|
||||
location.reinterpret<UByteVar>()[0] = argument
|
||||
FFI_TYPE_KIND_UINT8
|
||||
}
|
||||
|
||||
is UShort -> if (isVariadic) {
|
||||
convertArgument(argument.toUInt(), isVariadic, location, additionalPlacement)
|
||||
} else {
|
||||
location.reinterpret<UShortVar>()[0] = argument
|
||||
FFI_TYPE_KIND_UINT16
|
||||
}
|
||||
|
||||
is Double -> {
|
||||
location.reinterpret<DoubleVar>()[0] = argument
|
||||
FFI_TYPE_KIND_DOUBLE
|
||||
}
|
||||
|
||||
is Float -> if (isVariadic) {
|
||||
convertArgument(argument.toDouble(), isVariadic, location, additionalPlacement)
|
||||
} else {
|
||||
location.reinterpret<FloatVar>()[0] = argument
|
||||
FFI_TYPE_KIND_FLOAT
|
||||
}
|
||||
|
||||
is CEnum -> convertArgument(argument.value, isVariadic, location, additionalPlacement)
|
||||
|
||||
is ForeignObjCObject -> {
|
||||
location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer(argument.objcPtr())
|
||||
FFI_TYPE_KIND_POINTER
|
||||
}
|
||||
|
||||
else -> throw Error("unsupported argument: $argument")
|
||||
}
|
||||
|
||||
inline fun <reified T : CVariable> NativePlacement.allocFfiReturnValueBuffer(type: CVariable.Type): T {
|
||||
var size = type.size
|
||||
var align = type.align
|
||||
|
||||
// libffi requires return value buffer to be no smaller than system register size;
|
||||
// TODO: system register size is not exactly the same as pointer size.
|
||||
|
||||
if (size < pointerSize) {
|
||||
size = pointerSize.toLong()
|
||||
}
|
||||
|
||||
if (align < pointerSize) {
|
||||
align = pointerSize
|
||||
}
|
||||
|
||||
return this.alloc(size, align).reinterpret<T>()
|
||||
}
|
||||
|
||||
fun callWithVarargs(codePtr: NativePtr, returnValuePtr: NativePtr, returnTypeKind: FfiTypeKind,
|
||||
fixedArguments: Array<out Any?>, variadicArguments: Array<out Any?>?,
|
||||
argumentsPlacement: AutofreeScope) {
|
||||
|
||||
val totalArgumentsNumber = fixedArguments.size + if (variadicArguments == null) 0 else variadicArguments.size
|
||||
|
||||
// All supported arguments take at most 8 bytes each:
|
||||
val argumentsStorage = argumentsPlacement.allocArray<LongVar>(totalArgumentsNumber)
|
||||
val arguments = argumentsPlacement.allocArray<CPointerVar<*>>(totalArgumentsNumber)
|
||||
val types = argumentsPlacement.allocArray<COpaquePointerVar>(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] = typeKind.toLong().toCPointer()
|
||||
arguments[index] = storage
|
||||
|
||||
++index
|
||||
}
|
||||
|
||||
for (argument in fixedArguments) {
|
||||
addArgument(argument, isVariadic = false)
|
||||
}
|
||||
|
||||
val variadicArgumentsNumber: Int
|
||||
|
||||
if (variadicArguments != null) {
|
||||
for (argument in variadicArguments) {
|
||||
addArgument(argument, isVariadic = true)
|
||||
}
|
||||
variadicArgumentsNumber = variadicArguments.size
|
||||
} else {
|
||||
variadicArgumentsNumber = -1
|
||||
}
|
||||
|
||||
assert (index == totalArgumentsNumber)
|
||||
|
||||
callFunctionPointer(codePtr, returnValuePtr, returnTypeKind, arguments.rawValue, types.rawValue,
|
||||
totalArgumentsNumber, variadicArgumentsNumber)
|
||||
}
|
||||
|
||||
@SymbolName("Kotlin_Interop_callFunctionPointer")
|
||||
private external fun callFunctionPointer(
|
||||
codePtr: NativePtr,
|
||||
returnValuePtr: NativePtr,
|
||||
returnTypeKind: FfiTypeKind,
|
||||
arguments: NativePtr,
|
||||
argumentTypeKinds: NativePtr,
|
||||
totalArgumentsNumber: Int,
|
||||
variadicArgumentsNumber: Int
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package kotlinx.cinterop.internal
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class CStruct(val spelling: String)
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
public annotation class CCall(val id: String) {
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class CString
|
||||
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class WCString
|
||||
}
|
||||
+7
-2
@@ -342,8 +342,13 @@ private fun Type.isIntegerLikeType(): Boolean = when (this) {
|
||||
false
|
||||
} else {
|
||||
def.size <= 4 &&
|
||||
def.bitFields.all { it.type.isIntegerLikeType() } &&
|
||||
def.fields.all { it.offset == 0L && it.type.isIntegerLikeType() }
|
||||
def.members.all {
|
||||
when (it) {
|
||||
is BitField -> it.type.isIntegerLikeType()
|
||||
is Field -> it.offset == 0L && it.type.isIntegerLikeType()
|
||||
is IncompleteField -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is ObjCPointer, is PointerType, CharType, BoolType -> true
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
fun tryRenderStructOrUnion(def: StructDef): String? = when (def.kind) {
|
||||
StructDef.Kind.STRUCT -> tryRenderStruct(def)
|
||||
StructDef.Kind.UNION -> tryRenderUnion(def)
|
||||
}
|
||||
|
||||
private fun tryRenderStruct(def: StructDef): String? {
|
||||
val isPackedStruct = def.fields.any { !it.isAligned }
|
||||
|
||||
var offset = 0L
|
||||
|
||||
return buildString {
|
||||
append("struct")
|
||||
if (isPackedStruct) append(" __attribute__((packed))")
|
||||
append(" { ")
|
||||
|
||||
def.members.forEachIndexed { index, it ->
|
||||
val name = "p$index"
|
||||
val decl = when (it) {
|
||||
is Field -> {
|
||||
val defaultAlignment = if (isPackedStruct) 1L else it.typeAlign
|
||||
val alignment = guessAlignment(offset, it.offsetBytes, defaultAlignment) ?: return null
|
||||
|
||||
offset = it.offsetBytes + it.typeSize
|
||||
|
||||
tryRenderVar(it.type, name)
|
||||
?.plus(if (alignment == defaultAlignment) "" else "__attribute__((aligned($alignment)))")
|
||||
}
|
||||
|
||||
is BitField, // TODO: tryRenderVar(it.type, name)?.plus(" : ${it.size}")
|
||||
is IncompleteField -> null // e.g. flexible array member.
|
||||
} ?: return null
|
||||
append("$decl; ")
|
||||
}
|
||||
|
||||
append("}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun guessAlignment(offset: Long, paddedOffset: Long, defaultAlignment: Long): Long? =
|
||||
longArrayOf(defaultAlignment, 1L, 2L, 4L, 8L, 16L, 32L).firstOrNull {
|
||||
alignUp(offset, it) == paddedOffset
|
||||
}
|
||||
|
||||
private fun alignUp(x: Long, alignment: Long): Long = (x + alignment - 1) and ((alignment - 1).inv())
|
||||
|
||||
private fun tryRenderUnion(def: StructDef): String? =
|
||||
if (def.members.any { it.offset != 0L }) null else buildString {
|
||||
append("union { ")
|
||||
def.members.forEachIndexed { index, it ->
|
||||
val decl = when (it) {
|
||||
is Field -> tryRenderVar(it.type, "p$index")
|
||||
is BitField, is IncompleteField -> null
|
||||
} ?: return null
|
||||
|
||||
append("$decl; ")
|
||||
}
|
||||
append("}")
|
||||
|
||||
}
|
||||
|
||||
private fun tryRenderVar(type: Type, name: String): String? = when (type) {
|
||||
CharType, BoolType -> "char $name"
|
||||
is IntegerType -> "${type.spelling} $name"
|
||||
is FloatingType -> "${type.spelling} $name"
|
||||
is RecordType -> "${tryRenderStructOrUnion(type.decl.def!!)} $name"
|
||||
is EnumType -> tryRenderVar(type.def.baseType, name)
|
||||
is PointerType -> "void* $name"
|
||||
is ConstArrayType -> tryRenderVar(type.elemType, "$name[${type.length}]")
|
||||
is IncompleteArrayType -> tryRenderVar(type.elemType, "$name[]")
|
||||
is Typedef -> tryRenderVar(type.def.aliased, name)
|
||||
is ObjCPointer -> "void* $name"
|
||||
else -> null
|
||||
}
|
||||
|
||||
private val Field.offsetBytes: Long get() {
|
||||
require(this.offset % 8 == 0L)
|
||||
return this.offset / 8
|
||||
}
|
||||
+32
-62
@@ -354,8 +354,12 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
if (def.hasNaturalLayout) {
|
||||
out("@CNaturalStruct(${def.fields.joinToString { it.name.quoteAsKotlinLiteral() }})")
|
||||
if (def.kind == StructDef.Kind.STRUCT && def.fieldsHaveDefaultAlignment()) {
|
||||
out("@CNaturalStruct(${def.members.joinToString { it.name.quoteAsKotlinLiteral() }})")
|
||||
}
|
||||
} else {
|
||||
tryRenderStructOrUnion(def)?.let {
|
||||
out("@CStruct".applyToStrings(it))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,8 +372,6 @@ class StubGenerator(
|
||||
for (field in def.fields) {
|
||||
try {
|
||||
assert(field.name.isNotEmpty())
|
||||
|
||||
if (field.offset < 0) throw NotImplementedError();
|
||||
assert(field.offset % 8 == 0L)
|
||||
val offset = field.offset / 8
|
||||
val fieldRefType = mirror(field.type)
|
||||
@@ -588,16 +590,13 @@ class StubGenerator(
|
||||
return stubs
|
||||
}
|
||||
|
||||
private fun FunctionDecl.generateAsFfiVarargs(): Boolean = (platform == KotlinPlatform.NATIVE && this.isVararg &&
|
||||
// Neither takes nor returns structs by value:
|
||||
!this.returnsRecord() && this.parameters.all { it.type.unwrapTypedefs() !is RecordType })
|
||||
|
||||
private fun FunctionDecl.returnsRecord(): Boolean = this.returnType.unwrapTypedefs() is RecordType
|
||||
private fun FunctionDecl.returnsVoid(): Boolean = this.returnType.unwrapTypedefs() is VoidType
|
||||
|
||||
private inner class KotlinFunctionStub(val func: FunctionDecl) : KotlinStub, NativeBacked {
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> =
|
||||
if (context.nativeBridges.isSupported(this)) {
|
||||
if (isCCall) {
|
||||
sequenceOf("@CCall".applyToStrings(cCallSymbolName!!), "external $header")
|
||||
} else if (context.nativeBridges.isSupported(this)) {
|
||||
block(header, bodyLines)
|
||||
} else {
|
||||
sequenceOf(
|
||||
@@ -608,6 +607,8 @@ class StubGenerator(
|
||||
|
||||
private val header: String
|
||||
private val bodyLines: List<String>
|
||||
private val isCCall: Boolean
|
||||
private val cCallSymbolName: String?
|
||||
|
||||
init {
|
||||
// TODO: support dumpShims
|
||||
@@ -627,11 +628,19 @@ class StubGenerator(
|
||||
val representAsValuesRef = representCFunctionParameterAsValuesRef(parameter.type)
|
||||
|
||||
val bridgeArgument = if (representCFunctionParameterAsString(func, parameter.type)) {
|
||||
kotlinParameters.add(parameterName to KotlinTypes.string.makeNullable())
|
||||
val annotations = when (platform) {
|
||||
KotlinPlatform.JVM -> ""
|
||||
KotlinPlatform.NATIVE -> "@CCall.CString "
|
||||
}
|
||||
kotlinParameters.add(annotations + parameterName to KotlinTypes.string.makeNullable())
|
||||
bodyGenerator.pushMemScoped()
|
||||
"$parameterName?.cstr?.getPointer(memScope)"
|
||||
} else if (representCFunctionParameterAsWString(func, parameter.type)) {
|
||||
kotlinParameters.add(parameterName to KotlinTypes.string.makeNullable())
|
||||
val annotations = when (platform) {
|
||||
KotlinPlatform.JVM -> ""
|
||||
KotlinPlatform.NATIVE -> "@CCall.WCString "
|
||||
}
|
||||
kotlinParameters.add(annotations + parameterName to KotlinTypes.string.makeNullable())
|
||||
bodyGenerator.pushMemScoped()
|
||||
"$parameterName?.wcstr?.getPointer(memScope)"
|
||||
} else if (representAsValuesRef != null) {
|
||||
@@ -647,7 +656,7 @@ class StubGenerator(
|
||||
bridgeArguments.add(TypedKotlinValue(parameter.type, bridgeArgument))
|
||||
}
|
||||
|
||||
if (!func.generateAsFfiVarargs()) {
|
||||
if (!func.isVararg || platform != KotlinPlatform.NATIVE) {
|
||||
val result = mappingBridgeGenerator.kotlinToNative(
|
||||
bodyGenerator,
|
||||
this,
|
||||
@@ -657,37 +666,19 @@ class StubGenerator(
|
||||
"${func.name}(${nativeValues.joinToString()})"
|
||||
}
|
||||
bodyGenerator.out("return $result")
|
||||
isCCall = false
|
||||
cCallSymbolName = null
|
||||
} else {
|
||||
val returnTypeKind = getFfiTypeKind(func.returnType)
|
||||
|
||||
kotlinParameters.add("vararg variadicArguments" to KotlinTypes.any.makeNullable())
|
||||
bodyGenerator.pushMemScoped()
|
||||
isCCall = true // TODO: don't generate unused body in this case.
|
||||
cCallSymbolName = "knifunptr_" + pkgName.replace('.', '_') + nextUniqueId()
|
||||
|
||||
val resultVar = "kniResult"
|
||||
|
||||
val resultPtr = if (!func.returnsVoid()) {
|
||||
val returnType = mirror(func.returnType).pointedType.render(kotlinFile)
|
||||
bodyGenerator.out("val $resultVar = allocFfiReturnValueBuffer<$returnType>(typeOf<$returnType>())")
|
||||
"$resultVar.rawPtr"
|
||||
} else {
|
||||
"nativeNullPtr"
|
||||
}
|
||||
val fixedArguments = bridgeArguments.joinToString(", ") { it.value }
|
||||
|
||||
val functionPtr = simpleBridgeGenerator.kotlinToNative(
|
||||
simpleBridgeGenerator.insertNativeBridge(
|
||||
this,
|
||||
BridgedType.NATIVE_PTR,
|
||||
emptyList()
|
||||
) {
|
||||
func.name
|
||||
}
|
||||
|
||||
bodyGenerator.out("callWithVarargs($functionPtr, $resultPtr, $returnTypeKind, " +
|
||||
"arrayOf($fixedArguments), variadicArguments, memScope)")
|
||||
|
||||
if (!func.returnsVoid()) {
|
||||
bodyGenerator.out("return $resultVar.value")
|
||||
}
|
||||
emptyList(),
|
||||
listOf("extern const void* $cCallSymbolName __asm(${cCallSymbolName.quoteAsKotlinLiteral()});",
|
||||
"extern const void* $cCallSymbolName = &${func.name};")
|
||||
)
|
||||
}
|
||||
|
||||
val returnType = if (func.returnsVoid()) {
|
||||
@@ -705,28 +696,6 @@ 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())
|
||||
}
|
||||
is EnumType -> getFfiTypeKind(unwrappedType.def.baseType)
|
||||
else -> TODO(unwrappedType.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun integerLiteral(type: Type, value: Long): String? {
|
||||
val integerType = type.unwrapTypedefs() as? IntegerType ?: return null
|
||||
return integerLiteral(integerType.size, declarationMapper.isMappedToSigned(integerType), value)
|
||||
@@ -943,6 +912,7 @@ class StubGenerator(
|
||||
}
|
||||
if (platform == KotlinPlatform.NATIVE) {
|
||||
out("import kotlin.native.SymbolName")
|
||||
out("import kotlinx.cinterop.internal.*")
|
||||
}
|
||||
out("import kotlinx.cinterop.*")
|
||||
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.konan.exec.Command
|
||||
import org.jetbrains.kotlin.konan.file.*
|
||||
import org.jetbrains.kotlin.konan.target.ClangArgs
|
||||
|
||||
class CStubsManager {
|
||||
|
||||
fun getUniqueName(prefix: String) = "$prefix${counter++}"
|
||||
|
||||
fun addStub(kotlinLocation: CompilerMessageLocation?, lines: List<String>) {
|
||||
stubs += Stub(kotlinLocation, lines)
|
||||
}
|
||||
|
||||
fun compile(clang: ClangArgs, messageCollector: MessageCollector, verbose: Boolean): File? {
|
||||
if (stubs.isEmpty()) return null
|
||||
|
||||
val cSource = createTempFile("cstubs", ".c").deleteOnExit()
|
||||
cSource.writeLines(stubs.flatMap { it.lines })
|
||||
|
||||
val bitcode = createTempFile("cstubs", ".bc").deleteOnExit()
|
||||
|
||||
val cSourcePath = cSource.absolutePath
|
||||
val clangCommand = clang.clangC(cSourcePath, "-emit-llvm", "-c", "-o", bitcode.absolutePath)
|
||||
val result = Command(clangCommand).getResult(withErrors = true)
|
||||
if (result.exitCode != 0) {
|
||||
reportCompilationErrors(cSourcePath, result, messageCollector, verbose)
|
||||
}
|
||||
|
||||
return bitcode
|
||||
}
|
||||
|
||||
private fun reportCompilationErrors(
|
||||
cSourcePath: String,
|
||||
result: Command.Result,
|
||||
messageCollector: MessageCollector,
|
||||
verbose: Boolean
|
||||
): Nothing {
|
||||
val regex = Regex("${Regex.escape(cSourcePath)}:([0-9]+):[0-9]+: error: .*")
|
||||
val errorLines = result.outputLines.mapNotNull { line ->
|
||||
regex.matchEntire(line)?.let { matchResult ->
|
||||
matchResult.groupValues[1].toInt()
|
||||
}
|
||||
}
|
||||
|
||||
val lineToStub = ArrayList<Stub>()
|
||||
stubs.forEach { stub ->
|
||||
repeat(stub.lines.size) { lineToStub.add(stub) }
|
||||
}
|
||||
|
||||
val cSourceCopyPath = "cstubs.c"
|
||||
if (verbose) {
|
||||
File(cSourcePath).copyTo(File(cSourceCopyPath))
|
||||
}
|
||||
|
||||
if (errorLines.isNotEmpty()) {
|
||||
errorLines.forEach {
|
||||
messageCollector.report(
|
||||
CompilerMessageSeverity.ERROR,
|
||||
"Unable to compile C bridge" + if (verbose) " at $cSourceCopyPath:$it" else "",
|
||||
lineToStub[it - 1].kotlinLocation
|
||||
)
|
||||
}
|
||||
} else {
|
||||
messageCollector.report(
|
||||
CompilerMessageSeverity.ERROR,
|
||||
"Unable to compile C bridges",
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
throw KonanCompilationException()
|
||||
}
|
||||
|
||||
private val stubs = mutableListOf<Stub>()
|
||||
private class Stub(val kotlinLocation: CompilerMessageLocation?, val lines: List<String>)
|
||||
private var counter = 0
|
||||
}
|
||||
+15
-6
@@ -5,6 +5,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import llvm.LLVMLinkModules2
|
||||
import llvm.LLVMModuleRef
|
||||
import llvm.LLVMWriteBitcodeToFile
|
||||
import org.jetbrains.kotlin.backend.konan.library.impl.buildLibrary
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.parseBitcodeFile
|
||||
@@ -26,6 +27,12 @@ internal fun produceOutput(context: Context, phaser: PhaseManager) {
|
||||
val tempFiles = context.config.tempFiles
|
||||
val produce = config.get(KonanConfigKeys.PRODUCE)
|
||||
|
||||
phaser.phase(KonanPhase.C_STUBS) {
|
||||
context.cStubsManager.compile(context.config.clang, context.messageCollector, context.phase!!.verbose)?.let {
|
||||
parseAndLinkBitcodeFile(llvmModule, it.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
when (produce) {
|
||||
CompilerOutputKind.STATIC,
|
||||
CompilerOutputKind.DYNAMIC,
|
||||
@@ -50,11 +57,7 @@ internal fun produceOutput(context: Context, phaser: PhaseManager) {
|
||||
|
||||
phaser.phase(KonanPhase.BITCODE_LINKER) {
|
||||
for (library in nativeLibraries) {
|
||||
val libraryModule = parseBitcodeFile(library)
|
||||
val failed = LLVMLinkModules2(llvmModule, libraryModule)
|
||||
if (failed != 0) {
|
||||
throw Error("failed to link $library") // TODO: retrieve error message from LLVM.
|
||||
}
|
||||
parseAndLinkBitcodeFile(llvmModule, library)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,4 +102,10 @@ internal fun produceOutput(context: Context, phaser: PhaseManager) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun parseAndLinkBitcodeFile(llvmModule: LLVMModuleRef, path: String) {
|
||||
val parsedModule = parseBitcodeFile(path)
|
||||
val failed = LLVMLinkModules2(llvmModule, parsedModule)
|
||||
if (failed != 0) {
|
||||
throw Error("failed to link $path") // TODO: retrieve error message from LLVM.
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -314,10 +314,13 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
||||
}
|
||||
|
||||
lateinit var llvm: Llvm
|
||||
val llvmImports: LlvmImports = Llvm.ImportsImpl(this)
|
||||
lateinit var llvmDeclarations: LlvmDeclarations
|
||||
lateinit var bitcodeFileName: String
|
||||
lateinit var library: KonanLibraryWriter
|
||||
|
||||
val cStubsManager = CStubsManager()
|
||||
|
||||
var phase: KonanPhase? = null
|
||||
var depth: Int = 0
|
||||
|
||||
|
||||
+16
-18
@@ -33,6 +33,18 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
|
||||
|
||||
val nativePointed = packageScope.getContributedClass(InteropFqNames.nativePointedName)
|
||||
|
||||
val cValuesRef = this.packageScope.getContributedClass("CValuesRef")
|
||||
val cValues = this.packageScope.getContributedClass("CValues")
|
||||
val cValue = this.packageScope.getContributedClass("CValue")
|
||||
val cValueWrite = this.packageScope.getContributedFunctions("write")
|
||||
.single { it.extensionReceiverParameter?.type?.constructor?.declarationDescriptor == cValue }
|
||||
val cValueRead = this.packageScope.getContributedFunctions("readValue")
|
||||
.single { it.valueParameters.size == 1 }
|
||||
|
||||
val allocType = this.packageScope.getContributedFunctions("alloc")
|
||||
.single { it.extensionReceiverParameter != null
|
||||
&& it.valueParameters.singleOrNull()?.name?.toString() == "type" }
|
||||
|
||||
val cPointer = this.packageScope.getContributedClass(InteropFqNames.cPointerName)
|
||||
|
||||
val cPointerRawValue = cPointer.unsubstitutedMemberScope.getContributedVariables("rawValue").single()
|
||||
@@ -43,6 +55,10 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
|
||||
TypeUtils.getClassDescriptor(extensionReceiverParameter.type) == cPointer
|
||||
}
|
||||
|
||||
val cstr = packageScope.getContributedVariables("cstr").single()
|
||||
val wcstr = packageScope.getContributedVariables("wcstr").single()
|
||||
val memScope = packageScope.getContributedClass("MemScope")
|
||||
|
||||
val nativePointedRawPtrGetter =
|
||||
nativePointed.unsubstitutedMemberScope.getContributedVariables("rawPtr").single().getter!!
|
||||
|
||||
@@ -61,24 +77,6 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns, vararg konanPrimitives:
|
||||
private fun KonanBuiltIns.getUnsignedClass(unsignedType: UnsignedType): ClassDescriptor =
|
||||
this.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!!
|
||||
|
||||
val invokeImpls = mapOf(
|
||||
builtIns.unit to "invokeImplUnitRet",
|
||||
builtIns.boolean to "invokeImplBooleanRet",
|
||||
builtIns.byte to "invokeImplByteRet",
|
||||
builtIns.short to "invokeImplShortRet",
|
||||
builtIns.int to "invokeImplIntRet",
|
||||
builtIns.long to "invokeImplLongRet",
|
||||
builtIns.getUnsignedClass(UnsignedType.UBYTE) to "invokeImplUByteRet",
|
||||
builtIns.getUnsignedClass(UnsignedType.USHORT) to "invokeImplUShortRet",
|
||||
builtIns.getUnsignedClass(UnsignedType.UINT) to "invokeImplUIntRet",
|
||||
builtIns.getUnsignedClass(UnsignedType.ULONG) to "invokeImplULongRet",
|
||||
builtIns.float to "invokeImplFloatRet",
|
||||
builtIns.double to "invokeImplDoubleRet",
|
||||
cPointer to "invokeImplPointerRet"
|
||||
).mapValues { (_, name) ->
|
||||
packageScope.getContributedFunctions(name).single()
|
||||
}.toMap()
|
||||
|
||||
val objCObject = packageScope.getContributedClass("ObjCObject")
|
||||
|
||||
val objCObjectBase = packageScope.getContributedClass("ObjCObjectBase")
|
||||
|
||||
+23
-10
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.KonanSharedVariablesManager
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.kotlinNativeInternal
|
||||
import org.jetbrains.kotlin.backend.konan.ir.KonanIr
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
@@ -19,6 +20,9 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilder
|
||||
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
abstract internal class KonanBackendContext(val config: KonanConfig) : CommonBackendContext {
|
||||
@@ -49,14 +53,23 @@ abstract internal class KonanBackendContext(val config: KonanConfig) : CommonBac
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrElement.getCompilerMessageLocation(containingFile: IrFile): CompilerMessageLocation? {
|
||||
val sourceRangeInfo = containingFile.fileEntry.getSourceRangeInfo(this.startOffset, this.endOffset)
|
||||
return CompilerMessageLocation.create(
|
||||
path = sourceRangeInfo.filePath,
|
||||
line = sourceRangeInfo.startLineNumber + 1,
|
||||
column = sourceRangeInfo.startColumnNumber + 1,
|
||||
lineContent = null // TODO: retrieve the line content.
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun IrElement.getCompilerMessageLocation(containingFile: IrFile): CompilerMessageLocation? =
|
||||
createCompilerMessageLocation(containingFile, this.startOffset, this.endOffset)
|
||||
|
||||
internal fun IrBuilderWithScope.getCompilerMessageLocation(): CompilerMessageLocation? {
|
||||
val declaration = this.scope.scopeOwnerSymbol.owner as? IrDeclaration ?: return null
|
||||
val file = declaration.findPackage() as? IrFile ?: return null
|
||||
return createCompilerMessageLocation(file, startOffset, endOffset)
|
||||
}
|
||||
|
||||
private fun createCompilerMessageLocation(containingFile: IrFile, startOffset: Int, endOffset: Int): CompilerMessageLocation? {
|
||||
val sourceRangeInfo = containingFile.fileEntry.getSourceRangeInfo(startOffset, endOffset)
|
||||
return CompilerMessageLocation.create(
|
||||
path = sourceRangeInfo.filePath,
|
||||
line = sourceRangeInfo.startLineNumber + 1,
|
||||
column = sourceRangeInfo.startColumnNumber + 1,
|
||||
lineContent = null // TODO: retrieve the line content.
|
||||
)
|
||||
}
|
||||
+1
@@ -58,6 +58,7 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG, DESERIALIZE_DFG, enabled = false), // TODO: Requires devirtualization.
|
||||
/* ... ... */ SERIALIZE_DFG("Data flow graph serializing", BUILD_DFG), // TODO: Requires escape analysis.
|
||||
/* ... ... */ CODEGEN("Code Generation"),
|
||||
/* ... ... */ C_STUBS("C stubs compilation"),
|
||||
/* ... ... */ BITCODE_LINKER("Bitcode linking"),
|
||||
/* */ LINK_STAGE("Link stage"),
|
||||
/* ... */ OBJECT_FILES("Bitcode to object file"),
|
||||
|
||||
+1
-2
@@ -181,8 +181,7 @@ internal class LinkStage(val context: Context, val phaser: PhaseManager) {
|
||||
try {
|
||||
File(executable).delete()
|
||||
linker.linkCommands(objectFiles = objectFiles, executable = executable,
|
||||
libraries = linker.targetLibffi + linker.linkStaticLibraries(includedBinaries) +
|
||||
context.config.defaultSystemLibraries,
|
||||
libraries = linker.linkStaticLibraries(includedBinaries) + context.config.defaultSystemLibraries,
|
||||
linkerArgs = entryPointSelector +
|
||||
asLinkerArgs(config.getNotNull(KonanConfigKeys.LINKER_ARGS)) +
|
||||
BitcodeEmbedding.getLinkerOptions(context.config) +
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
object RuntimeNames {
|
||||
val symbolName = FqName("kotlin.native.SymbolName")
|
||||
val exportForCppRuntime = FqName("kotlin.native.internal.ExportForCppRuntime")
|
||||
val cCall = FqName("kotlinx.cinterop.internal.CCall")
|
||||
}
|
||||
+803
@@ -0,0 +1,803 @@
|
||||
package org.jetbrains.kotlin.backend.konan.cgen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.common.lower.irNot
|
||||
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.createAnnotation
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue
|
||||
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrSpreadElement
|
||||
import org.jetbrains.kotlin.ir.expressions.IrVararg
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal interface KotlinStubs {
|
||||
val irBuiltIns: IrBuiltIns
|
||||
val symbols: KonanSymbols
|
||||
val target: KonanTarget
|
||||
fun addKotlin(declaration: IrDeclaration)
|
||||
fun addC(lines: List<String>)
|
||||
fun getUniqueCName(prefix: String): String
|
||||
|
||||
fun reportError(location: IrElement, message: String): Nothing
|
||||
}
|
||||
|
||||
private class KotlinToCCallBuilder(
|
||||
val irBuilder: IrBuilderWithScope,
|
||||
cBridgeName: String,
|
||||
val stubs: KotlinStubs
|
||||
) {
|
||||
|
||||
val symbols: KonanSymbols get() = stubs.symbols
|
||||
|
||||
val bridgeCallBuilder = KotlinCallBuilder(irBuilder, symbols)
|
||||
val bridgeBuilder = KotlinCBridgeBuilder(irBuilder.startOffset, irBuilder.endOffset, cBridgeName, stubs, isKotlinToC = true)
|
||||
val cBridgeBodyLines = mutableListOf<String>()
|
||||
val cCallBuilder = CCallBuilder()
|
||||
val cFunctionBuilder = CFunctionBuilder()
|
||||
|
||||
}
|
||||
|
||||
private fun KotlinToCCallBuilder.passThroughBridge(argument: IrExpression, kotlinType: IrType, cType: CType): CVariable {
|
||||
bridgeCallBuilder.arguments += argument
|
||||
return bridgeBuilder.addParameter(kotlinType, cType).second
|
||||
}
|
||||
|
||||
private fun KotlinToCCallBuilder.addArgument(
|
||||
argument: IrExpression,
|
||||
type: IrType,
|
||||
variadic: Boolean,
|
||||
parameter: IrValueParameter?
|
||||
) {
|
||||
val argumentPassing = mapParameter(type, variadic, parameter, argument)
|
||||
val cArgument = with(argumentPassing) { passValue(argument) }
|
||||
cCallBuilder.arguments += cArgument.expression
|
||||
if (!variadic) cFunctionBuilder.addParameter(cArgument.type)
|
||||
}
|
||||
|
||||
private fun KotlinToCCallBuilder.buildKotlinBridgeCall(transformCall: (IrCall) -> IrExpression = { it }): IrExpression =
|
||||
bridgeCallBuilder.build(
|
||||
bridgeBuilder.buildKotlinBridge().also {
|
||||
this.stubs.addKotlin(it)
|
||||
},
|
||||
transformCall
|
||||
)
|
||||
|
||||
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean): IrExpression {
|
||||
require(expression.dispatchReceiver == null)
|
||||
|
||||
val cBridgeName = this.getUniqueCName("knbridge")
|
||||
val callBuilder = KotlinToCCallBuilder(builder, cBridgeName, this)
|
||||
|
||||
val cFunctionBuilder = callBuilder.cFunctionBuilder
|
||||
|
||||
val callee = expression.symbol.owner
|
||||
|
||||
// TODO: consider computing all arguments before converting.
|
||||
|
||||
val targetPtrParameter: String?
|
||||
val targetFunctionName: String
|
||||
|
||||
if (isInvoke) {
|
||||
targetPtrParameter = callBuilder.passThroughBridge(
|
||||
expression.extensionReceiver!!,
|
||||
symbols.interopCPointer.typeWithStarProjections,
|
||||
CTypes.voidPtr
|
||||
).name
|
||||
targetFunctionName = "targetPtr"
|
||||
|
||||
(0 until expression.valueArgumentsCount).forEach {
|
||||
callBuilder.addArgument(
|
||||
expression.getValueArgument(it)!!,
|
||||
type = expression.getTypeArgument(it)!!,
|
||||
variadic = false,
|
||||
parameter = null
|
||||
)
|
||||
}
|
||||
} else {
|
||||
require(expression.extensionReceiver == null)
|
||||
targetPtrParameter = null
|
||||
targetFunctionName = this.getUniqueCName("target")
|
||||
|
||||
(0 until expression.valueArgumentsCount).forEach { index ->
|
||||
val parameter = callee.valueParameters[index]
|
||||
val argument = expression.getValueArgument(index)
|
||||
if (parameter.isVararg) {
|
||||
require(index == expression.valueArgumentsCount - 1)
|
||||
require(argument is IrVararg?)
|
||||
argument?.elements.orEmpty().forEach {
|
||||
when (it) {
|
||||
is IrExpression -> callBuilder.addArgument(it, it.type, variadic = true, parameter = null)
|
||||
|
||||
is IrSpreadElement ->
|
||||
reportError(it, "spread operator is not supported for variadic C functions")
|
||||
|
||||
else -> error(it)
|
||||
}
|
||||
}
|
||||
cFunctionBuilder.variadic = true
|
||||
} else {
|
||||
callBuilder.addArgument(argument!!, parameter.type, variadic = false, parameter = parameter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val cLines = mutableListOf<String>()
|
||||
|
||||
val returnValuePassing = if (isInvoke) {
|
||||
val returnType = expression.getTypeArgument(expression.typeArgumentsCount - 1)!!
|
||||
mapReturnType(returnType, TypeLocation.FunctionCallResult(expression))
|
||||
} else {
|
||||
mapReturnType(callee.returnType, TypeLocation.FunctionCallResult(expression))
|
||||
}
|
||||
|
||||
val result = with(returnValuePassing) {
|
||||
callBuilder.returnValue(callBuilder.cCallBuilder.build(targetFunctionName))
|
||||
}
|
||||
|
||||
val targetFunctionVariable = CVariable(CTypes.pointer(cFunctionBuilder.getType()), targetFunctionName)
|
||||
if (isInvoke) {
|
||||
callBuilder.cBridgeBodyLines.add(0, "$targetFunctionVariable = ${targetPtrParameter!!};")
|
||||
} else {
|
||||
val cCallSymbolName = callee.descriptor.annotations.findAnnotation(cCall)!!.getStringValue("id")
|
||||
cLines += "extern const $targetFunctionVariable __asm(\"$cCallSymbolName\");" // Exported from cinterop stubs.
|
||||
}
|
||||
|
||||
cLines += "${callBuilder.bridgeBuilder.buildCSignature(cBridgeName)} {"
|
||||
cLines += callBuilder.cBridgeBodyLines
|
||||
cLines += "}"
|
||||
|
||||
this.addC(cLines)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private class CCallbackBuilder(
|
||||
val stubs: KotlinStubs,
|
||||
val location: IrElement
|
||||
) {
|
||||
|
||||
val irBuiltIns: IrBuiltIns get() = stubs.irBuiltIns
|
||||
val symbols: KonanSymbols get() = stubs.symbols
|
||||
|
||||
private val cBridgeName = stubs.getUniqueCName("knbridge")
|
||||
|
||||
fun buildCBridgeCall(): String = cBridgeCallBuilder.build(cBridgeName)
|
||||
fun buildCBridge(): String = bridgeBuilder.buildCSignature(cBridgeName)
|
||||
|
||||
val bridgeBuilder = KotlinCBridgeBuilder(location.startOffset, location.endOffset, cBridgeName, stubs, isKotlinToC = false)
|
||||
val kotlinCallBuilder = KotlinCallBuilder(bridgeBuilder.kotlinIrBuilder, symbols)
|
||||
val kotlinBridgeStatements = mutableListOf<IrStatement>()
|
||||
val cBridgeCallBuilder = CCallBuilder()
|
||||
val cBodyLines = mutableListOf<String>()
|
||||
val cFunctionBuilder = CFunctionBuilder()
|
||||
|
||||
}
|
||||
|
||||
private fun CCallbackBuilder.passThroughBridge(
|
||||
cBridgeArgument: String,
|
||||
cBridgeParameterType: CType,
|
||||
kotlinBridgeParameterType: IrType
|
||||
): IrValueParameter {
|
||||
cBridgeCallBuilder.arguments += cBridgeArgument
|
||||
return bridgeBuilder.addParameter(kotlinBridgeParameterType, cBridgeParameterType).first
|
||||
}
|
||||
|
||||
private fun CCallbackBuilder.addParameter(it: IrValueParameter) {
|
||||
val typeLocation = TypeLocation.FunctionPointerParameter(cFunctionBuilder.numberOfParameters, location)
|
||||
val valuePassing = stubs.mapType(it.type, typeLocation)
|
||||
|
||||
val kotlinArgument = with(valuePassing) { receiveValue() }
|
||||
kotlinCallBuilder.arguments += kotlinArgument
|
||||
}
|
||||
|
||||
private fun CCallbackBuilder.build(function: IrSimpleFunction, signature: IrFunction): String {
|
||||
val kotlinCall = kotlinCallBuilder.build(function)
|
||||
|
||||
with(stubs.mapReturnType(signature.returnType, TypeLocation.FunctionPointerReturnValue(location))) {
|
||||
returnValue(kotlinCall)
|
||||
}
|
||||
|
||||
val cLines = mutableListOf<String>()
|
||||
|
||||
val kotlinBridge = bridgeBuilder.buildKotlinBridge()
|
||||
kotlinBridge.body = bridgeBuilder.kotlinIrBuilder.irBlockBody {
|
||||
kotlinBridgeStatements.forEach { +it }
|
||||
}
|
||||
stubs.addKotlin(kotlinBridge)
|
||||
|
||||
val result = stubs.getUniqueCName("kncfun")
|
||||
|
||||
cLines += "${buildCBridge()};"
|
||||
cLines += "${cFunctionBuilder.buildSignature(result)} {"
|
||||
cLines += cBodyLines
|
||||
cLines += "}"
|
||||
|
||||
stubs.addC(cLines)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun KotlinStubs.generateCFunction(
|
||||
function: IrSimpleFunction,
|
||||
signature: IrSimpleFunction,
|
||||
isObjCMethod: Boolean,
|
||||
location: IrElement
|
||||
): String {
|
||||
val callbackBuilder = CCallbackBuilder(this, location)
|
||||
|
||||
signature.dispatchReceiverParameter?.let { callbackBuilder.addParameter(it) }
|
||||
if (isObjCMethod) {
|
||||
// assert(mapType(signature.dispatchReceiverParameter!!.type) is ObjCReferenceValuePassing)
|
||||
// Selector is ignored:
|
||||
with(TrivialValuePassing(symbols.nativePtrType, CTypes.voidPtr)) { callbackBuilder.receiveValue() }
|
||||
}
|
||||
signature.extensionReceiverParameter?.let { callbackBuilder.addParameter(it) }
|
||||
|
||||
signature.valueParameters.forEach {
|
||||
callbackBuilder.addParameter(it)
|
||||
}
|
||||
|
||||
return callbackBuilder.build(function, signature)
|
||||
}
|
||||
|
||||
internal fun KotlinStubs.generateCFunctionPointer(
|
||||
function: IrSimpleFunction,
|
||||
signature: IrSimpleFunction,
|
||||
isObjCMethod: Boolean,
|
||||
expression: IrExpression
|
||||
): IrExpression {
|
||||
val cFunction = generateCFunction(function, signature, isObjCMethod, expression)
|
||||
val fakeFunction = createFakeKotlinExternalFunction(signature, cFunction, isObjCMethod)
|
||||
addKotlin(fakeFunction)
|
||||
|
||||
return IrFunctionReferenceImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
fakeFunction.symbol,
|
||||
fakeFunction.descriptor,
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
private fun KotlinStubs.createFakeKotlinExternalFunction(
|
||||
signature: IrSimpleFunction,
|
||||
cFunctionName: String,
|
||||
isObjCMethod: Boolean
|
||||
): IrSimpleFunction {
|
||||
val objCMethodImpAnnotation = if (isObjCMethod) {
|
||||
val methodInfo = signature.getObjCMethodInfo()!!
|
||||
createObjCMethodImpAnnotation(methodInfo.selector, methodInfo.encoding, symbols)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val bridgeAnnotations = Annotations.create(
|
||||
listOfNotNull(
|
||||
createAnnotation(symbols.symbolName.descriptor, "value" to cFunctionName),
|
||||
objCMethodImpAnnotation
|
||||
)
|
||||
)
|
||||
val bridgeDescriptor = WrappedSimpleFunctionDescriptor(bridgeAnnotations)
|
||||
val bridge = IrFunctionImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
IrDeclarationOrigin.DEFINED,
|
||||
IrSimpleFunctionSymbolImpl(bridgeDescriptor),
|
||||
Name.identifier(cFunctionName),
|
||||
Visibilities.PRIVATE,
|
||||
Modality.FINAL,
|
||||
signature.returnType,
|
||||
isInline = false,
|
||||
isExternal = true,
|
||||
isTailrec = false,
|
||||
isSuspend = false
|
||||
)
|
||||
bridgeDescriptor.bind(bridge)
|
||||
|
||||
return bridge
|
||||
}
|
||||
|
||||
private fun createObjCMethodImpAnnotation(selector: String, encoding: String, symbols: KonanSymbols) =
|
||||
createAnnotation(symbols.objCMethodImp.descriptor, "selector" to selector, "encoding" to encoding)
|
||||
|
||||
private val cCall = RuntimeNames.cCall
|
||||
|
||||
private fun IrType.isUnsigned(unsignedType: UnsignedType) = this is IrSimpleType && !this.hasQuestionMark &&
|
||||
(this.classifier.owner as? IrClass)?.classId == unsignedType.classId
|
||||
|
||||
private fun IrType.isUByte() = this.isUnsigned(UnsignedType.UBYTE)
|
||||
private fun IrType.isUShort() = this.isUnsigned(UnsignedType.USHORT)
|
||||
private fun IrType.isUInt() = this.isUnsigned(UnsignedType.UINT)
|
||||
private fun IrType.isULong() = this.isUnsigned(UnsignedType.ULONG)
|
||||
|
||||
private fun IrType.isCEnumType(): Boolean {
|
||||
val simpleType = this as? IrSimpleType ?: return false
|
||||
if (simpleType.hasQuestionMark) return false
|
||||
val enumClass = simpleType.classifier.owner as? IrClass ?: return false
|
||||
if (!enumClass.isEnumClass) return false
|
||||
|
||||
return enumClass.superTypes
|
||||
.any { (it.classifierOrNull?.owner as? IrClass)?.fqNameSafe == FqName("kotlinx.cinterop.CEnum") }
|
||||
}
|
||||
|
||||
private fun IrValueParameter.isWCStringParameter() =
|
||||
this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier("WCString")))
|
||||
|
||||
private fun IrValueParameter.isCStringParameter() =
|
||||
this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier("CString")))
|
||||
|
||||
private fun getStructSpelling(kotlinClass: IrClass): String? =
|
||||
kotlinClass.descriptor.annotations
|
||||
.findAnnotation(FqName("kotlinx.cinterop.internal.CStruct"))
|
||||
?.getStringValue("spelling")
|
||||
|
||||
private fun getCStructType(kotlinClass: IrClass): CType? =
|
||||
getStructSpelling(kotlinClass)?.let { CTypes.simple(it) }
|
||||
|
||||
private fun KotlinStubs.getNamedCStructType(kotlinClass: IrClass): CType? {
|
||||
val cStructType = getCStructType(kotlinClass) ?: return null
|
||||
val name = getUniqueCName("struct")
|
||||
addC(listOf("typedef ${cStructType.render(name)};"))
|
||||
return CTypes.simple(name)
|
||||
}
|
||||
|
||||
// TODO: rework Boolean support.
|
||||
private fun cBoolType(target: KonanTarget): CType? = when (target.family) {
|
||||
Family.IOS -> CTypes.C99Bool
|
||||
else -> CTypes.signedChar
|
||||
}
|
||||
|
||||
private fun KotlinToCCallBuilder.mapParameter(
|
||||
type: IrType,
|
||||
variadic: Boolean,
|
||||
parameter: IrValueParameter?,
|
||||
argument: IrExpression
|
||||
): KotlinToCArgumentPassing {
|
||||
val classifier = type.classifierOrNull
|
||||
return when {
|
||||
classifier == symbols.interopCValues || // Note: this should not be accepted, but is required for compatibility
|
||||
classifier == symbols.interopCValuesRef -> CValuesRefArgumentPassing
|
||||
|
||||
classifier == symbols.string && (variadic || parameter?.isCStringParameter() == true) ->
|
||||
CStringArgumentPassing()
|
||||
|
||||
classifier == symbols.string && parameter?.isWCStringParameter() == true ->
|
||||
WCStringArgumentPassing()
|
||||
|
||||
else -> stubs.mapType(type, TypeLocation.FunctionArgument(argument))
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TypeLocation(val element: IrElement) {
|
||||
class FunctionArgument(val argument: IrExpression) : TypeLocation(argument)
|
||||
class FunctionCallResult(val call: IrCall) : TypeLocation(call)
|
||||
|
||||
class FunctionPointerParameter(val index: Int, element: IrElement) : TypeLocation(element)
|
||||
class FunctionPointerReturnValue(element: IrElement) : TypeLocation(element)
|
||||
}
|
||||
|
||||
private fun KotlinStubs.mapReturnType(type: IrType, location: TypeLocation): ValueReturning =
|
||||
when {
|
||||
type.isUnit() -> VoidReturning
|
||||
else -> mapType(type, location)
|
||||
}
|
||||
|
||||
private fun KotlinStubs.mapType(type: IrType, location: TypeLocation): ValuePassing =
|
||||
mapType(type, { reportUnsupportedType(it, type, location) })
|
||||
|
||||
private fun KotlinStubs.mapType(type: IrType, reportUnsupportedType: (String) -> Nothing): ValuePassing = when {
|
||||
type.isBoolean() -> BooleanValuePassing(
|
||||
cBoolType(target) ?: reportUnsupportedType("unavailable on target platform"),
|
||||
irBuiltIns
|
||||
)
|
||||
|
||||
type.isByte() -> TrivialValuePassing(irBuiltIns.byteType, CTypes.signedChar)
|
||||
type.isShort() -> TrivialValuePassing(irBuiltIns.shortType, CTypes.short)
|
||||
type.isInt() -> TrivialValuePassing(irBuiltIns.intType, CTypes.int)
|
||||
type.isLong() -> TrivialValuePassing(irBuiltIns.longType, CTypes.longLong)
|
||||
type.isFloat() -> TrivialValuePassing(irBuiltIns.floatType, CTypes.float)
|
||||
type.isDouble() -> TrivialValuePassing(irBuiltIns.doubleType, CTypes.double)
|
||||
type.classifierOrNull == symbols.interopCPointer -> TrivialValuePassing(type, CTypes.voidPtr)
|
||||
type.isUByte() -> UnsignedValuePassing(type, CTypes.signedChar, CTypes.unsignedChar)
|
||||
type.isUShort() -> UnsignedValuePassing(type, CTypes.short, CTypes.unsignedShort)
|
||||
type.isUInt() -> UnsignedValuePassing(type, CTypes.int, CTypes.unsignedInt)
|
||||
type.isULong() -> UnsignedValuePassing(type, CTypes.longLong, CTypes.unsignedLongLong)
|
||||
|
||||
type.isCEnumType() -> {
|
||||
val enumClass = type.getClass()!!
|
||||
val value = enumClass.declarations
|
||||
.filterIsInstance<IrProperty>()
|
||||
.single { it.name.asString() == "value" }
|
||||
|
||||
CEnumValuePassing(enumClass, value, mapType(value.getter!!.returnType, reportUnsupportedType) as SimpleValuePassing)
|
||||
}
|
||||
|
||||
type.classifierOrNull == symbols.interopCValue -> if (type.isNullable()) {
|
||||
reportUnsupportedType("must not be nullable")
|
||||
} else {
|
||||
val kotlinClass = (type as IrSimpleType).arguments.singleOrNull()?.typeOrNull?.getClass()
|
||||
?: reportUnsupportedType("must be parameterized with concrete class")
|
||||
|
||||
StructValuePassing(kotlinClass, getNamedCStructType(kotlinClass)
|
||||
?: reportUnsupportedType("not a structure or too complex"))
|
||||
}
|
||||
|
||||
type.isFunction() -> reportUnsupportedType("")
|
||||
isObjCReferenceType(type) -> ObjCReferenceValuePassing(symbols, type)
|
||||
|
||||
else -> reportUnsupportedType("doesn't correspond to any C type")
|
||||
}
|
||||
|
||||
private fun KotlinStubs.isObjCReferenceType(type: IrType): Boolean {
|
||||
if (target.family != Family.OSX && target.family != Family.IOS) return false
|
||||
|
||||
if (type.isObjCObjectType()) return true
|
||||
|
||||
val descriptor = type.classifierOrNull?.descriptor ?: return false
|
||||
val builtIns = irBuiltIns.builtIns
|
||||
|
||||
return when (descriptor) {
|
||||
builtIns.string,
|
||||
builtIns.list, builtIns.mutableList,
|
||||
builtIns.set, builtIns.mutableSet,
|
||||
builtIns.map, builtIns.mutableMap -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private class CExpression(val expression: String, val type: CType)
|
||||
|
||||
private interface KotlinToCArgumentPassing {
|
||||
fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression
|
||||
}
|
||||
|
||||
private interface ValueReturning {
|
||||
fun KotlinToCCallBuilder.returnValue(expression: String): IrExpression
|
||||
fun CCallbackBuilder.returnValue(expression: IrExpression)
|
||||
}
|
||||
|
||||
private interface ValuePassing : KotlinToCArgumentPassing, ValueReturning {
|
||||
fun CCallbackBuilder.receiveValue(): IrExpression
|
||||
}
|
||||
|
||||
private abstract class SimpleValuePassing : ValuePassing {
|
||||
abstract val kotlinBridgeType: IrType
|
||||
abstract val cBridgeType: CType
|
||||
abstract val cType: CType
|
||||
abstract fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression
|
||||
abstract fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression
|
||||
abstract fun bridgedToC(expression: String): String
|
||||
abstract fun cToBridged(expression: String): String
|
||||
|
||||
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
|
||||
val bridgeArgument = irBuilder.kotlinToBridged(expression)
|
||||
val cBridgeValue = passThroughBridge(bridgeArgument, kotlinBridgeType, cBridgeType).name
|
||||
return CExpression(bridgedToC(cBridgeValue), cType)
|
||||
}
|
||||
|
||||
override fun KotlinToCCallBuilder.returnValue(expression: String): IrExpression {
|
||||
cFunctionBuilder.setReturnType(cType)
|
||||
bridgeBuilder.setReturnType(kotlinBridgeType, cBridgeType)
|
||||
cBridgeBodyLines.add("return ${cToBridged(expression)};")
|
||||
val kotlinBridgeCall = buildKotlinBridgeCall()
|
||||
return irBuilder.bridgedToKotlin(kotlinBridgeCall, symbols)
|
||||
}
|
||||
|
||||
override fun CCallbackBuilder.receiveValue(): IrExpression {
|
||||
val cParameter = cFunctionBuilder.addParameter(cType)
|
||||
val cBridgeArgument = cToBridged(cParameter.name)
|
||||
val kotlinParameter = passThroughBridge(cBridgeArgument, cBridgeType, kotlinBridgeType)
|
||||
return with(bridgeBuilder.kotlinIrBuilder) {
|
||||
bridgedToKotlin(irGet(kotlinParameter), symbols)
|
||||
}
|
||||
}
|
||||
|
||||
override fun CCallbackBuilder.returnValue(expression: IrExpression) {
|
||||
cFunctionBuilder.setReturnType(cType)
|
||||
bridgeBuilder.setReturnType(kotlinBridgeType, cBridgeType)
|
||||
|
||||
kotlinBridgeStatements += with(bridgeBuilder.kotlinIrBuilder) {
|
||||
irReturn(kotlinToBridged(expression))
|
||||
}
|
||||
val cBridgeCall = buildCBridgeCall()
|
||||
cBodyLines += "return ${bridgedToC(cBridgeCall)};"
|
||||
}
|
||||
}
|
||||
|
||||
private class TrivialValuePassing(val kotlinType: IrType, override val cType: CType) : SimpleValuePassing() {
|
||||
override val kotlinBridgeType: IrType
|
||||
get() = kotlinType
|
||||
override val cBridgeType: CType
|
||||
get() = cType
|
||||
|
||||
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression = expression
|
||||
override fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression = expression
|
||||
override fun bridgedToC(expression: String): String = expression
|
||||
override fun cToBridged(expression: String): String = expression
|
||||
}
|
||||
|
||||
private class UnsignedValuePassing(val kotlinType: IrType, val cSignedType: CType, override val cType: CType) : SimpleValuePassing() {
|
||||
override val kotlinBridgeType: IrType
|
||||
get() = kotlinType
|
||||
override val cBridgeType: CType
|
||||
get() = cSignedType
|
||||
|
||||
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression = expression
|
||||
|
||||
override fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression = expression
|
||||
|
||||
override fun bridgedToC(expression: String): String = cType.cast(expression)
|
||||
|
||||
override fun cToBridged(expression: String): String = cBridgeType.cast(expression)
|
||||
}
|
||||
|
||||
private class BooleanValuePassing(override val cType: CType, private val irBuiltIns: IrBuiltIns) : SimpleValuePassing() {
|
||||
override val cBridgeType: CType get() = CTypes.signedChar
|
||||
override val kotlinBridgeType: IrType get() = irBuiltIns.byteType
|
||||
|
||||
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression = irIfThenElse(
|
||||
irBuiltIns.byteType,
|
||||
condition = expression,
|
||||
thenPart = IrConstImpl.byte(startOffset, endOffset, irBuiltIns.byteType, 1),
|
||||
elsePart = IrConstImpl.byte(startOffset, endOffset, irBuiltIns.byteType, 0)
|
||||
)
|
||||
|
||||
override fun IrBuilderWithScope.bridgedToKotlin(
|
||||
expression: IrExpression,
|
||||
symbols: KonanSymbols
|
||||
): IrExpression = irNot(irCall(symbols.areEqualByValue[PrimitiveBinaryType.BYTE]!!.owner).apply {
|
||||
putValueArgument(0, expression)
|
||||
putValueArgument(1, IrConstImpl.byte(startOffset, endOffset, irBuiltIns.byteType, 0))
|
||||
})
|
||||
|
||||
override fun bridgedToC(expression: String): String = cType.cast(expression)
|
||||
|
||||
override fun cToBridged(expression: String): String = cBridgeType.cast(expression)
|
||||
}
|
||||
|
||||
private class StructValuePassing(private val kotlinClass: IrClass, private val cType: CType) : ValuePassing {
|
||||
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
|
||||
val cBridgeValue = passThroughBridge(
|
||||
cValuesRefToPointer(expression),
|
||||
symbols.interopCPointer.typeWithStarProjections,
|
||||
CTypes.pointer(cType)
|
||||
).name
|
||||
|
||||
return CExpression("*$cBridgeValue", cType)
|
||||
}
|
||||
|
||||
override fun KotlinToCCallBuilder.returnValue(expression: String): IrExpression = with(irBuilder) {
|
||||
cFunctionBuilder.setReturnType(cType)
|
||||
bridgeBuilder.setReturnType(context.irBuiltIns.unitType, CTypes.void)
|
||||
|
||||
val kotlinPointed = scope.createTemporaryVariable(irCall(symbols.interopAllocType.owner).apply {
|
||||
extensionReceiver = bridgeCallBuilder.getMemScope()
|
||||
putValueArgument(0, getTypeObject())
|
||||
})
|
||||
|
||||
bridgeCallBuilder.prepare += kotlinPointed
|
||||
|
||||
val cPointer = passThroughBridge(irGet(kotlinPointed), kotlinPointedType, CTypes.pointer(cType))
|
||||
cBridgeBodyLines += "*${cPointer.name} = $expression;"
|
||||
|
||||
buildKotlinBridgeCall {
|
||||
irBlock {
|
||||
at(it)
|
||||
+it
|
||||
+readCValue(irGet(kotlinPointed), symbols)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun CCallbackBuilder.receiveValue(): IrExpression = with(bridgeBuilder.kotlinIrBuilder) {
|
||||
val cParameter = cFunctionBuilder.addParameter(cType)
|
||||
val kotlinPointed = passThroughBridge("&${cParameter.name}", CTypes.voidPtr, kotlinPointedType)
|
||||
|
||||
readCValue(irGet(kotlinPointed), symbols)
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.readCValue(kotlinPointed: IrExpression, symbols: KonanSymbols): IrExpression =
|
||||
irCall(symbols.interopCValueRead.owner).apply {
|
||||
extensionReceiver = kotlinPointed
|
||||
putValueArgument(0, getTypeObject())
|
||||
}
|
||||
|
||||
override fun CCallbackBuilder.returnValue(expression: IrExpression) = with(bridgeBuilder.kotlinIrBuilder) {
|
||||
bridgeBuilder.setReturnType(irBuiltIns.unitType, CTypes.void)
|
||||
cFunctionBuilder.setReturnType(cType)
|
||||
|
||||
val result = "callbackResult"
|
||||
val cReturnValue = CVariable(cType, result)
|
||||
cBodyLines += "$cReturnValue;"
|
||||
val kotlinPtr = passThroughBridge("&$result", CTypes.voidPtr, symbols.nativePtrType)
|
||||
|
||||
kotlinBridgeStatements += irCall(symbols.interopCValueWrite.owner).apply {
|
||||
extensionReceiver = expression
|
||||
putValueArgument(0, irGet(kotlinPtr))
|
||||
}
|
||||
val cBridgeCall = buildCBridgeCall()
|
||||
cBodyLines += "$cBridgeCall;"
|
||||
cBodyLines += "return $result;"
|
||||
}
|
||||
|
||||
private val kotlinPointedType: IrType get() = kotlinClass.defaultType
|
||||
|
||||
private fun IrBuilderWithScope.getTypeObject() =
|
||||
irGetObject(
|
||||
kotlinClass.declarations.filterIsInstance<IrClass>()
|
||||
.single { it.isCompanion }.symbol
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private class CEnumValuePassing(
|
||||
val enumClass: IrClass,
|
||||
val value: IrProperty,
|
||||
val baseValuePassing: SimpleValuePassing
|
||||
) : SimpleValuePassing() {
|
||||
override val kotlinBridgeType: IrType
|
||||
get() = baseValuePassing.kotlinBridgeType
|
||||
override val cBridgeType: CType
|
||||
get() = baseValuePassing.cBridgeType
|
||||
override val cType: CType
|
||||
get() = baseValuePassing.cType
|
||||
|
||||
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression {
|
||||
val value = irCall(value.getter!!).apply {
|
||||
dispatchReceiver = expression
|
||||
}
|
||||
|
||||
return with(baseValuePassing) { kotlinToBridged(value) }
|
||||
}
|
||||
|
||||
override fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression {
|
||||
val companionClass = enumClass.declarations.filterIsInstance<IrClass>().single { it.isCompanion }
|
||||
val byValue = companionClass.simpleFunctions().single { it.name.asString() == "byValue" }
|
||||
|
||||
return irCall(byValue).apply {
|
||||
dispatchReceiver = irGetObject(companionClass.symbol)
|
||||
putValueArgument(0, expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun bridgedToC(expression: String): String = with(baseValuePassing) { bridgedToC(expression) }
|
||||
override fun cToBridged(expression: String): String = with(baseValuePassing) { cToBridged(expression) }
|
||||
}
|
||||
|
||||
private class ObjCReferenceValuePassing(private val symbols: KonanSymbols, private val type: IrType) : SimpleValuePassing() {
|
||||
override val kotlinBridgeType: IrType
|
||||
get() = symbols.nativePtrType
|
||||
override val cBridgeType: CType
|
||||
get() = CTypes.voidPtr
|
||||
override val cType: CType
|
||||
get() = CTypes.voidPtr
|
||||
|
||||
override fun IrBuilderWithScope.kotlinToBridged(expression: IrExpression): IrExpression =
|
||||
irCall(symbols.interopObjCObjectRawValueGetter.owner).apply {
|
||||
extensionReceiver = expression
|
||||
}
|
||||
|
||||
override fun IrBuilderWithScope.bridgedToKotlin(expression: IrExpression, symbols: KonanSymbols): IrExpression =
|
||||
irCall(symbols.interopInterpretObjCPointerOrNull, listOf(type)).apply {
|
||||
putValueArgument(0, expression)
|
||||
}
|
||||
|
||||
override fun bridgedToC(expression: String): String = expression
|
||||
override fun cToBridged(expression: String): String = expression
|
||||
|
||||
}
|
||||
|
||||
private class WCStringArgumentPassing : KotlinToCArgumentPassing {
|
||||
|
||||
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
|
||||
val wcstr = irBuilder.irCall(symbols.interopWcstr.owner).apply {
|
||||
extensionReceiver = expression
|
||||
}
|
||||
return with(CValuesRefArgumentPassing) { passValue(wcstr) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class CStringArgumentPassing : KotlinToCArgumentPassing {
|
||||
|
||||
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
|
||||
val cstr = irBuilder.irCall(symbols.interopCstr.owner).apply {
|
||||
extensionReceiver = expression
|
||||
}
|
||||
return with(CValuesRefArgumentPassing) { passValue(cstr) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private object CValuesRefArgumentPassing : KotlinToCArgumentPassing {
|
||||
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression {
|
||||
val bridgeArgument = cValuesRefToPointer(expression)
|
||||
val cBridgeValue = passThroughBridge(
|
||||
bridgeArgument,
|
||||
symbols.interopCPointer.typeWithStarProjections.makeNullable(),
|
||||
CTypes.voidPtr
|
||||
)
|
||||
return CExpression(cBridgeValue.name, cBridgeValue.type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinToCCallBuilder.cValuesRefToPointer(
|
||||
value: IrExpression
|
||||
): IrExpression = if (value.type.classifierOrNull == symbols.interopCPointer) {
|
||||
value // Optimization
|
||||
} else with(irBuilder) {
|
||||
val getPointerFunction = symbols.interopCValuesRef.owner
|
||||
.simpleFunctions()
|
||||
.single { it.name.asString() == "getPointer" }
|
||||
|
||||
fun getPointer(expression: IrExpression) = irCall(getPointerFunction).apply {
|
||||
dispatchReceiver = expression
|
||||
putValueArgument(0, bridgeCallBuilder.getMemScope())
|
||||
}
|
||||
|
||||
if (!value.type.isNullable()) {
|
||||
getPointer(value) // Optimization
|
||||
} else irLetS(value) { valueVarSymbol ->
|
||||
val valueVar = valueVarSymbol.owner
|
||||
irIfThenElse(
|
||||
type = symbols.interopCPointer.typeWithStarProjections.makeNullable(),
|
||||
condition = irEqeqeq(irGet(valueVar), irNull()),
|
||||
thenPart = irNull(),
|
||||
elsePart = getPointer(irGet(valueVar))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private object VoidReturning : ValueReturning {
|
||||
override fun KotlinToCCallBuilder.returnValue(expression: String): IrExpression {
|
||||
bridgeBuilder.setReturnType(irBuilder.context.irBuiltIns.unitType, CTypes.void)
|
||||
cFunctionBuilder.setReturnType(CTypes.void)
|
||||
cBridgeBodyLines += "$expression;"
|
||||
return buildKotlinBridgeCall()
|
||||
}
|
||||
|
||||
override fun CCallbackBuilder.returnValue(expression: IrExpression) {
|
||||
bridgeBuilder.setReturnType(irBuiltIns.unitType, CTypes.void)
|
||||
cFunctionBuilder.setReturnType(CTypes.void)
|
||||
kotlinBridgeStatements += bridgeBuilder.kotlinIrBuilder.irReturn(expression)
|
||||
cBodyLines += "${buildCBridgeCall()};"
|
||||
}
|
||||
}
|
||||
|
||||
internal fun CType.cast(expression: String): String = "((${this.render("")})$expression)"
|
||||
|
||||
private fun KotlinStubs.reportUnsupportedType(reason: String, type: IrType, location: TypeLocation): Nothing {
|
||||
val typeLocation: String = when (location) {
|
||||
is TypeLocation.FunctionArgument -> ""
|
||||
is TypeLocation.FunctionCallResult -> " of return value"
|
||||
is TypeLocation.FunctionPointerParameter -> " of callback parameter ${location.index + 1}"
|
||||
is TypeLocation.FunctionPointerReturnValue -> " of callback return value"
|
||||
}
|
||||
|
||||
reportError(location.element, "type ${type.toKotlinType()}$typeLocation is not supported here" +
|
||||
if (reason.isNotEmpty()) ": $reason" else "")
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
package org.jetbrains.kotlin.backend.konan.cgen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.createAnnotation
|
||||
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTryImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal class CFunctionBuilder {
|
||||
private val parameters = mutableListOf<CVariable>()
|
||||
private lateinit var returnType: CType
|
||||
|
||||
var variadic: Boolean = false
|
||||
|
||||
fun setReturnType(type: CType) {
|
||||
require(!::returnType.isInitialized)
|
||||
returnType = type
|
||||
}
|
||||
|
||||
fun addParameter(type: CType): CVariable {
|
||||
val result = CVariable(type, "p${counter++}")
|
||||
parameters += result
|
||||
return result
|
||||
}
|
||||
|
||||
val numberOfParameters: Int get() = parameters.size
|
||||
|
||||
private var counter = 1
|
||||
|
||||
fun getType(): CType = CTypes.function(returnType, parameters.map { it.type }, variadic)
|
||||
|
||||
fun buildSignature(name: String): String = returnType.render(buildString {
|
||||
append(name)
|
||||
append('(')
|
||||
parameters.joinTo(this)
|
||||
if (parameters.isEmpty()) {
|
||||
if (!variadic) append("void")
|
||||
} else {
|
||||
if (variadic) append(", ...")
|
||||
}
|
||||
append(')')
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
internal class KotlinBridgeBuilder(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
cName: String,
|
||||
stubs: KotlinStubs,
|
||||
isExternal: Boolean
|
||||
) {
|
||||
private var counter = 0
|
||||
private val bridge: IrFunction = createKotlinBridge(startOffset, endOffset, cName, stubs.symbols, isExternal)
|
||||
val irBuilder: IrBuilderWithScope = irBuilder(stubs.irBuiltIns, bridge.symbol).at(startOffset, endOffset)
|
||||
|
||||
fun addParameter(type: IrType): IrValueParameter {
|
||||
val index = counter++
|
||||
val descriptor = WrappedValueParameterDescriptor()
|
||||
|
||||
return IrValueParameterImpl(
|
||||
bridge.startOffset, bridge.endOffset, bridge.origin,
|
||||
IrValueParameterSymbolImpl(descriptor),
|
||||
Name.identifier("p$index"), index, type,
|
||||
null, false, false
|
||||
).apply {
|
||||
descriptor.bind(this)
|
||||
parent = bridge
|
||||
bridge.valueParameters += this
|
||||
}
|
||||
}
|
||||
|
||||
fun setReturnType(type: IrType) {
|
||||
bridge.returnType = type
|
||||
}
|
||||
|
||||
fun build(): IrFunction = bridge
|
||||
}
|
||||
|
||||
private fun createKotlinBridge(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
cBridgeName: String,
|
||||
symbols: KonanSymbols,
|
||||
isExternal: Boolean
|
||||
): IrFunctionImpl {
|
||||
val bridgeAnnotations = Annotations.create(
|
||||
listOf(
|
||||
if (isExternal) {
|
||||
createAnnotation(symbols.symbolName.descriptor, "value" to cBridgeName)
|
||||
} else {
|
||||
createAnnotation(symbols.exportForCppRuntime.descriptor, "name" to cBridgeName)
|
||||
}
|
||||
)
|
||||
)
|
||||
val bridgeDescriptor = WrappedSimpleFunctionDescriptor(bridgeAnnotations)
|
||||
val bridge = IrFunctionImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
IrDeclarationOrigin.DEFINED,
|
||||
IrSimpleFunctionSymbolImpl(bridgeDescriptor),
|
||||
Name.identifier(cBridgeName),
|
||||
Visibilities.PRIVATE,
|
||||
Modality.FINAL,
|
||||
IrUninitializedType,
|
||||
isInline = false,
|
||||
isExternal = isExternal,
|
||||
isTailrec = false,
|
||||
isSuspend = false
|
||||
)
|
||||
bridgeDescriptor.bind(bridge)
|
||||
return bridge
|
||||
}
|
||||
|
||||
internal class KotlinCBridgeBuilder(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
cName: String,
|
||||
stubs: KotlinStubs,
|
||||
isKotlinToC: Boolean
|
||||
) {
|
||||
private val kotlinBridgeBuilder = KotlinBridgeBuilder(startOffset, endOffset, cName, stubs, isExternal = isKotlinToC)
|
||||
private val cBridgeBuilder = CFunctionBuilder()
|
||||
|
||||
val kotlinIrBuilder: IrBuilderWithScope get() = kotlinBridgeBuilder.irBuilder
|
||||
|
||||
fun addParameter(kotlinType: IrType, cType: CType): Pair<IrValueParameter, CVariable> {
|
||||
return kotlinBridgeBuilder.addParameter(kotlinType) to cBridgeBuilder.addParameter(cType)
|
||||
}
|
||||
|
||||
fun setReturnType(kotlinReturnType: IrType, cReturnType: CType) {
|
||||
kotlinBridgeBuilder.setReturnType(kotlinReturnType)
|
||||
cBridgeBuilder.setReturnType(cReturnType)
|
||||
}
|
||||
|
||||
fun buildCSignature(name: String): String = cBridgeBuilder.buildSignature(name)
|
||||
|
||||
fun buildKotlinBridge() = kotlinBridgeBuilder.build()
|
||||
}
|
||||
|
||||
internal class KotlinCallBuilder(private val irBuilder: IrBuilderWithScope, private val symbols: KonanSymbols) {
|
||||
val prepare = mutableListOf<IrStatement>()
|
||||
val arguments = mutableListOf<IrExpression>()
|
||||
val cleanup = mutableListOf<IrStatement>()
|
||||
|
||||
private var memScope: IrVariable? = null
|
||||
|
||||
fun getMemScope(): IrExpression = with(irBuilder) {
|
||||
memScope?.let { return irGet(it) }
|
||||
|
||||
val newMemScope = scope.createTemporaryVariable(irCall(symbols.interopMemScope.owner.constructors.single()))
|
||||
memScope = newMemScope
|
||||
|
||||
prepare += newMemScope
|
||||
|
||||
val clearImpl = symbols.interopMemScope.owner.simpleFunctions().single { it.name.asString() == "clearImpl" }
|
||||
cleanup += irCall(clearImpl).apply {
|
||||
dispatchReceiver = irGet(memScope!!)
|
||||
}
|
||||
|
||||
irGet(newMemScope)
|
||||
}
|
||||
|
||||
fun build(
|
||||
function: IrFunction,
|
||||
transformCall: (IrCall) -> IrExpression = { it }
|
||||
): IrExpression {
|
||||
val arguments = this.arguments.toMutableList()
|
||||
|
||||
val kotlinCall = irBuilder.irCall(function).run {
|
||||
if (function.dispatchReceiverParameter != null) {
|
||||
dispatchReceiver = arguments.removeAt(0)
|
||||
}
|
||||
if (function.extensionReceiverParameter != null) {
|
||||
extensionReceiver = arguments.removeAt(0)
|
||||
}
|
||||
assert(arguments.size == function.valueParameters.size)
|
||||
arguments.forEachIndexed { index, it -> putValueArgument(index, it) }
|
||||
|
||||
transformCall(this)
|
||||
}
|
||||
|
||||
return if (prepare.isEmpty() && cleanup.isEmpty()) {
|
||||
kotlinCall
|
||||
} else {
|
||||
irBuilder.irBlock(kotlinCall) {
|
||||
prepare.forEach { +it }
|
||||
if (cleanup.isEmpty()) {
|
||||
+kotlinCall
|
||||
} else {
|
||||
// Note: generating try-catch as finally blocks are already lowered.
|
||||
+IrTryImpl(startOffset, endOffset, kotlinCall.type).apply {
|
||||
tryResult = kotlinCall
|
||||
catches += irCatch(context.irBuiltIns.throwableType).apply {
|
||||
result = irBlock(kotlinCall) {
|
||||
cleanup.forEach { +it }
|
||||
+irThrow(irGet(catchParameter))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CCallBuilder {
|
||||
val arguments = mutableListOf<String>()
|
||||
|
||||
fun build(function: String) = buildString {
|
||||
append(function)
|
||||
append('(')
|
||||
arguments.joinTo(this)
|
||||
append(')')
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package org.jetbrains.kotlin.backend.konan.cgen
|
||||
|
||||
internal interface CType {
|
||||
fun render(name: String): String
|
||||
}
|
||||
|
||||
internal class CVariable(val type: CType, val name: String) {
|
||||
override fun toString() = type.render(name)
|
||||
}
|
||||
|
||||
internal object CTypes {
|
||||
fun simple(type: String): CType = SimpleCType(type)
|
||||
fun pointer(pointee: CType): CType = PointerCType(pointee)
|
||||
fun function(returnType: CType, parameterTypes: List<CType>, variadic: Boolean): CType =
|
||||
FunctionCType(returnType, parameterTypes, variadic)
|
||||
|
||||
val void = simple("void")
|
||||
val voidPtr = pointer(void)
|
||||
val signedChar = simple("signed char")
|
||||
val unsignedChar = simple("unsigned char")
|
||||
val short = simple("short")
|
||||
val unsignedShort = simple("unsigned short")
|
||||
val int = simple("int")
|
||||
val unsignedInt = simple("unsigned int")
|
||||
val longLong = simple("long long")
|
||||
val unsignedLongLong = simple("unsigned long long")
|
||||
val float = simple("float")
|
||||
val double = simple("double")
|
||||
val C99Bool = simple("_Bool")
|
||||
val char = simple("char")
|
||||
}
|
||||
|
||||
private class SimpleCType(private val type: String) : CType {
|
||||
override fun render(name: String): String = if (name.isEmpty()) type else "$type $name"
|
||||
}
|
||||
|
||||
private class PointerCType(private val pointee: CType) : CType {
|
||||
override fun render(name: String): String = pointee.render("*$name")
|
||||
}
|
||||
|
||||
private class FunctionCType(
|
||||
private val returnType: CType,
|
||||
private val parameterTypes: List<CType>,
|
||||
private val variadic: Boolean
|
||||
) : CType {
|
||||
override fun render(name: String): String = returnType.render(buildString {
|
||||
append("(")
|
||||
append(name)
|
||||
append(")(")
|
||||
parameterTypes.joinTo(this) { it.render("") }
|
||||
if (parameterTypes.isEmpty()) {
|
||||
if (!variadic) append("void")
|
||||
} else {
|
||||
if (variadic) append(", ...")
|
||||
}
|
||||
append(')')
|
||||
})
|
||||
}
|
||||
+14
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.atMostOne
|
||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||
import org.jetbrains.kotlin.backend.konan.binaryTypeIsReference
|
||||
import org.jetbrains.kotlin.backend.konan.isObjCClass
|
||||
import org.jetbrains.kotlin.backend.konan.serialization.isExported
|
||||
@@ -16,10 +17,12 @@ import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
@@ -270,5 +273,16 @@ fun CallableMemberDescriptor.externalSymbolOrThrow(): String? {
|
||||
|
||||
if (this.annotations.hasAnnotation(TypedIntrinsic)) return null
|
||||
|
||||
if (this.annotations.hasAnnotation(RuntimeNames.cCall)) return null
|
||||
|
||||
throw Error("external function ${this} must have @TypedIntrinsic, @SymbolName or @ObjCMethod annotation")
|
||||
}
|
||||
|
||||
fun createAnnotation(
|
||||
descriptor: ClassDescriptor,
|
||||
vararg values: Pair<String, String>
|
||||
): AnnotationDescriptor = AnnotationDescriptorImpl(
|
||||
descriptor.defaultType,
|
||||
values.map { (name, value) -> Name.identifier(name) to StringValue(value) }.toMap(),
|
||||
SourceElement.NO_SOURCE
|
||||
)
|
||||
|
||||
+32
-22
@@ -132,8 +132,7 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
||||
val nativePtr = symbolTable.referenceClass(context.nativePtr)
|
||||
val nativePtrType = nativePtr.typeWith(arguments = emptyList())
|
||||
|
||||
private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol =
|
||||
symbolTable.referenceClass(builtIns.builtInsModule.findClassAcrossModuleDependencies(unsignedType.classId)!!)
|
||||
private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol = classById(unsignedType.classId)
|
||||
|
||||
val uByte = unsignedClass(UnsignedType.UBYTE)
|
||||
val uShort = unsignedClass(UnsignedType.USHORT)
|
||||
@@ -181,9 +180,25 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
||||
|
||||
val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context))
|
||||
|
||||
val symbolName = topLevelClass(RuntimeNames.symbolName)
|
||||
val exportForCppRuntime = topLevelClass(RuntimeNames.exportForCppRuntime)
|
||||
|
||||
val objCMethodImp = symbolTable.referenceClass(context.interopBuiltIns.objCMethodImp)
|
||||
|
||||
val interopNativePointedGetRawPointer =
|
||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.nativePointedGetRawPointer)
|
||||
|
||||
val interopCPointer = symbolTable.referenceClass(context.interopBuiltIns.cPointer)
|
||||
val interopCstr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cstr.getter!!)
|
||||
val interopWcstr = symbolTable.referenceSimpleFunction(context.interopBuiltIns.wcstr.getter!!)
|
||||
val interopMemScope = symbolTable.referenceClass(context.interopBuiltIns.memScope)
|
||||
val interopCValue = symbolTable.referenceClass(context.interopBuiltIns.cValue)
|
||||
val interopCValues = symbolTable.referenceClass(context.interopBuiltIns.cValues)
|
||||
val interopCValuesRef = symbolTable.referenceClass(context.interopBuiltIns.cValuesRef)
|
||||
val interopCValueWrite = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cValueWrite)
|
||||
val interopCValueRead = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cValueRead)
|
||||
val interopAllocType = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocType)
|
||||
|
||||
val interopCPointerGetRawValue = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cPointerGetRawValue)
|
||||
|
||||
val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)
|
||||
@@ -194,6 +209,12 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
||||
.single()
|
||||
)
|
||||
|
||||
val interopObjCRetain = symbolTable.referenceSimpleFunction(
|
||||
context.interopBuiltIns.packageScope
|
||||
.getContributedFunctions(Name.identifier("objc_retain"), NoLookupLocation.FROM_BACKEND)
|
||||
.single()
|
||||
)
|
||||
|
||||
val interopGetObjCClass = symbolTable.referenceSimpleFunction(context.interopBuiltIns.getObjCClass)
|
||||
|
||||
val interopObjCObjectSuperInitCheck =
|
||||
@@ -204,10 +225,6 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
||||
val interopObjCObjectRawValueGetter =
|
||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr)
|
||||
|
||||
val interopInvokeImpls = context.interopBuiltIns.invokeImpls.mapValues { (_, function) ->
|
||||
symbolTable.referenceSimpleFunction(function)
|
||||
}
|
||||
|
||||
val interopInterpretObjCPointer =
|
||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretObjCPointer)
|
||||
|
||||
@@ -397,20 +414,11 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
||||
|
||||
override val coroutineImpl get() = TODO()
|
||||
|
||||
val baseContinuationImpl = symbolTable.referenceClass(
|
||||
builtIns.builtInsModule.findClassAcrossModuleDependencies(
|
||||
ClassId.topLevel(FqName("kotlin.coroutines.native.internal.BaseContinuationImpl")))!!
|
||||
)
|
||||
val baseContinuationImpl = topLevelClass("kotlin.coroutines.native.internal.BaseContinuationImpl")
|
||||
|
||||
val restrictedContinuationImpl = symbolTable.referenceClass(
|
||||
builtIns.builtInsModule.findClassAcrossModuleDependencies(
|
||||
ClassId.topLevel(FqName("kotlin.coroutines.native.internal.RestrictedContinuationImpl")))!!
|
||||
)
|
||||
val restrictedContinuationImpl = topLevelClass("kotlin.coroutines.native.internal.RestrictedContinuationImpl")
|
||||
|
||||
val continuationImpl = symbolTable.referenceClass(
|
||||
builtIns.builtInsModule.findClassAcrossModuleDependencies(
|
||||
ClassId.topLevel(FqName("kotlin.coroutines.native.internal.ContinuationImpl")))!!
|
||||
)
|
||||
val continuationImpl = topLevelClass("kotlin.coroutines.native.internal.ContinuationImpl")
|
||||
|
||||
override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
|
||||
coroutinesIntrinsicsPackage
|
||||
@@ -418,10 +426,7 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
||||
.filterNot { it.isExpect }.single().getter!!
|
||||
)
|
||||
|
||||
val kotlinResult = symbolTable.referenceClass(
|
||||
builtIns.builtInsModule.findClassAcrossModuleDependencies(
|
||||
ClassId.topLevel(FqName("kotlin.Result")))!!
|
||||
)
|
||||
val kotlinResult = topLevelClass("kotlin.Result")
|
||||
|
||||
val kotlinResultGetOrThrow = symbolTable.referenceSimpleFunction(
|
||||
builtInsPackage("kotlin")
|
||||
@@ -477,6 +482,11 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
|
||||
context.builtIns.builtInsModule.findClassAcrossModuleDependencies(
|
||||
ClassId.topLevel(FqName("kotlin.native.concurrent.SharedImmutable")))!!
|
||||
|
||||
private fun topLevelClass(fqName: String): IrClassSymbol = topLevelClass(FqName(fqName))
|
||||
private fun topLevelClass(fqName: FqName): IrClassSymbol = classById(ClassId.topLevel(fqName))
|
||||
private fun classById(classId: ClassId): IrClassSymbol =
|
||||
symbolTable.referenceClass(builtIns.builtInsModule.findClassAcrossModuleDependencies(classId)!!)
|
||||
|
||||
private fun internalFunction(name: String): IrSimpleFunctionSymbol =
|
||||
symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
|
||||
|
||||
|
||||
+11
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.explicitParameters
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
|
||||
@@ -34,6 +35,16 @@ val IrDeclarationParent.fqNameSafe: FqName get() = when (this) {
|
||||
else -> error(this)
|
||||
}
|
||||
|
||||
val IrClass.classId: ClassId?
|
||||
get() {
|
||||
val parent = this.parent
|
||||
return when (parent) {
|
||||
is IrClass -> parent.classId?.createNestedClassId(this.name)
|
||||
is IrPackageFragment -> ClassId.topLevel(parent.fqName.child(this.name))
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val IrDeclaration.name: Name
|
||||
get() = when (this) {
|
||||
is IrSimpleFunction -> this.name
|
||||
|
||||
+3
-2
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import llvm.LLVMTypeRef
|
||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.externalSymbolOrThrow
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationValue
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
|
||||
@@ -102,11 +103,11 @@ internal tailrec fun DeclarationDescriptor.isExported(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
private val symbolNameAnnotation = FqName("kotlin.native.SymbolName")
|
||||
private val symbolNameAnnotation = RuntimeNames.symbolName
|
||||
|
||||
private val cnameAnnotation = FqName("kotlin.native.CName")
|
||||
|
||||
private val exportForCppRuntimeAnnotation = FqName("kotlin.native.internal.ExportForCppRuntime")
|
||||
private val exportForCppRuntimeAnnotation = RuntimeNames.exportForCppRuntime
|
||||
|
||||
private val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
|
||||
|
||||
|
||||
+12
-5
@@ -302,7 +302,10 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
|
||||
val found = LLVMGetNamedFunction(llvmModule, name)
|
||||
if (found != null) {
|
||||
assert(getFunctionType(found) == type)
|
||||
assert(getFunctionType(found) == type) {
|
||||
"Expected: ${LLVMPrintTypeToString(type)!!.toKString()} " +
|
||||
"found: ${LLVMPrintTypeToString(getFunctionType(found))!!.toKString()}"
|
||||
}
|
||||
assert(LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage)
|
||||
return found
|
||||
} else {
|
||||
@@ -330,11 +333,13 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
return function
|
||||
}
|
||||
|
||||
private val usedLibraries = mutableSetOf<KonanLibrary>()
|
||||
val imports get() = context.llvmImports
|
||||
|
||||
val imports = object : LlvmImports {
|
||||
class ImportsImpl(private val context: Context) : LlvmImports {
|
||||
|
||||
private val allLibraries = context.librariesWithDependencies.toSet()
|
||||
private val usedLibraries = mutableSetOf<KonanLibrary>()
|
||||
|
||||
private val allLibraries by lazy { context.librariesWithDependencies.toSet() }
|
||||
|
||||
override fun add(origin: CompiledKonanModuleOrigin) {
|
||||
val library = when (origin) {
|
||||
@@ -348,11 +353,13 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
|
||||
usedLibraries.add(library)
|
||||
}
|
||||
|
||||
override fun isImported(library: KonanLibrary): Boolean = library in usedLibraries
|
||||
}
|
||||
|
||||
val librariesToLink: List<KonanLibrary> by lazy {
|
||||
context.config.resolvedLibraries
|
||||
.filterRoots { (!it.isDefault && !context.config.purgeUserLibs) || it.library in usedLibraries }
|
||||
.filterRoots { (!it.isDefault && !context.config.purgeUserLibs) || imports.isImported(it.library) }
|
||||
.getFullList(TopologicalLibraryOrder)
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -11,10 +11,12 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.konan.CompiledKonanModuleOrigin
|
||||
import org.jetbrains.kotlin.descriptors.konan.SyntheticModulesOrigin
|
||||
import org.jetbrains.kotlin.descriptors.konan.konanModuleOrigin
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
|
||||
internal interface LlvmImports {
|
||||
fun add(origin: CompiledKonanModuleOrigin)
|
||||
fun isImported(library: KonanLibrary): Boolean
|
||||
}
|
||||
|
||||
internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKonanModuleOrigin
|
||||
|
||||
+2
-3
@@ -398,9 +398,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
}
|
||||
|
||||
val llvmFunction = if (descriptor.isExternal) {
|
||||
if (descriptor.isTypedIntrinsic || descriptor.isObjCBridgeBased()) {
|
||||
return
|
||||
}
|
||||
if (descriptor.isTypedIntrinsic || descriptor.isObjCBridgeBased()
|
||||
|| descriptor.annotations.hasAnnotation(RuntimeNames.cCall)) return
|
||||
|
||||
context.llvm.externalFunction(descriptor.symbolName, llvmFunctionType,
|
||||
// Assume that `external fun` is defined in native libs attached to this module:
|
||||
|
||||
+54
-40
@@ -11,12 +11,17 @@ import org.jetbrains.kotlin.backend.common.peek
|
||||
import org.jetbrains.kotlin.backend.common.pop
|
||||
import org.jetbrains.kotlin.backend.common.push
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.cgen.KotlinStubs
|
||||
import org.jetbrains.kotlin.backend.konan.cgen.generateCCall
|
||||
import org.jetbrains.kotlin.backend.konan.cgen.generateCFunctionPointer
|
||||
import org.jetbrains.kotlin.backend.konan.getInlinedClass
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.namePrefix
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
||||
import org.jetbrains.kotlin.builtins.UnsignedTypes
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
@@ -29,6 +34,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -38,6 +44,7 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
@@ -743,14 +750,53 @@ internal class InteropLoweringPart2(val context: Context) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
val transformer = InteropTransformer(context, irFile)
|
||||
irFile.transformChildrenVoid(transformer)
|
||||
|
||||
irFile.addChildren(transformer.newTopLevelDeclarations)
|
||||
}
|
||||
}
|
||||
|
||||
private class InteropTransformer(val context: Context, val irFile: IrFile) : IrBuildingTransformer(context) {
|
||||
|
||||
val newTopLevelDeclarations = mutableListOf<IrDeclaration>()
|
||||
|
||||
val interop = context.interopBuiltIns
|
||||
val symbols = context.ir.symbols
|
||||
|
||||
private inline fun <T> generateWithStubs(element: IrElement? = null, block: KotlinStubs.() -> T): T =
|
||||
createKotlinStubs(element).block()
|
||||
|
||||
private fun createKotlinStubs(element: IrElement?): KotlinStubs {
|
||||
val location = if (element != null) {
|
||||
element.getCompilerMessageLocation(irFile)
|
||||
} else {
|
||||
builder.getCompilerMessageLocation()
|
||||
}
|
||||
|
||||
return object : KotlinStubs {
|
||||
override val irBuiltIns get() = context.irBuiltIns
|
||||
override val symbols get() = context.ir.symbols
|
||||
|
||||
override fun addKotlin(declaration: IrDeclaration) {
|
||||
newTopLevelDeclarations += declaration
|
||||
}
|
||||
|
||||
override fun addC(lines: List<String>) {
|
||||
context.cStubsManager.addStub(location, lines)
|
||||
}
|
||||
|
||||
override fun getUniqueCName(prefix: String) =
|
||||
"_${context.moduleDescriptor.namePrefix}_${context.cStubsManager.getUniqueName(prefix)}"
|
||||
|
||||
override val target get() = context.config.target
|
||||
|
||||
override fun reportError(location: IrElement, message: String): Nothing =
|
||||
context.reportCompilationError(message, irFile, location)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateCFunctionPointer(function: IrSimpleFunction, expression: IrExpression): IrExpression =
|
||||
generateWithStubs { generateCFunctionPointer(function, function, false, expression) }
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
|
||||
expression.transformChildrenVoid(this)
|
||||
@@ -774,7 +820,10 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
|
||||
}
|
||||
}
|
||||
|
||||
fun reportError(message: String): Nothing = context.reportCompilationError(message, irFile, expression)
|
||||
if (function.descriptor.annotations.hasAnnotation(RuntimeNames.cCall)) {
|
||||
context.llvmImports.add(function.descriptor.llvmSymbolOrigin)
|
||||
return generateWithStubs { generateCCall(expression, builder, isInvoke = false) }
|
||||
}
|
||||
|
||||
val intrinsicType = tryGetIntrinsicType(expression)
|
||||
|
||||
@@ -801,7 +850,8 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
|
||||
IntrinsicType.INTEROP_STATIC_C_FUNCTION -> {
|
||||
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!)
|
||||
|
||||
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()) {
|
||||
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()
|
||||
|| irCallableReference.symbol !is IrSimpleFunctionSymbol) {
|
||||
context.reportCompilationError(
|
||||
"${descriptor.fqNameSafe} must take an unbound, non-capturing function or lambda",
|
||||
irFile, expression
|
||||
@@ -813,13 +863,6 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
|
||||
val target = targetSymbol.owner
|
||||
val signatureTypes = target.allParameters.map { it.type } + target.returnType
|
||||
|
||||
signatureTypes.forEachIndexed { index, type ->
|
||||
type.ensureSupportedInCallbacks(
|
||||
isReturnType = (index == signatureTypes.lastIndex),
|
||||
reportError = ::reportError
|
||||
)
|
||||
}
|
||||
|
||||
descriptor.typeParameters.forEachIndexed { index, typeParameterDescriptor ->
|
||||
val typeArgument = expression.getTypeArgument(typeParameterDescriptor)!!.toKotlinType()
|
||||
val signatureType = signatureTypes[index].toKotlinType()
|
||||
@@ -832,39 +875,10 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
|
||||
}
|
||||
}
|
||||
|
||||
IrFunctionReferenceImpl(
|
||||
builder.startOffset, builder.endOffset,
|
||||
expression.type,
|
||||
targetSymbol, target.descriptor,
|
||||
typeArgumentsCount = 0)
|
||||
generateCFunctionPointer(target as IrSimpleFunction, expression)
|
||||
}
|
||||
IntrinsicType.INTEROP_FUNPTR_INVOKE -> {
|
||||
// Replace by `invokeImpl${type}Ret`:
|
||||
|
||||
val returnType =
|
||||
expression.getTypeArgument(descriptor.typeParameters.single { it.name.asString() == "R" })!!
|
||||
|
||||
returnType.checkCTypeNullability(::reportError)
|
||||
|
||||
val invokeImpl = symbols.interopInvokeImpls[returnType.getClass()?.descriptor] ?:
|
||||
context.reportCompilationError(
|
||||
"Invocation of C function pointer with return type '${returnType.toKotlinType()}' is not supported yet",
|
||||
irFile, expression
|
||||
)
|
||||
|
||||
builder.irCall(invokeImpl).apply {
|
||||
putValueArgument(0, expression.extensionReceiver)
|
||||
|
||||
val varargParameter = invokeImpl.owner.valueParameters[1]
|
||||
val varargArgument = IrVarargImpl(
|
||||
startOffset, endOffset, varargParameter.type, varargParameter.varargElementType!!
|
||||
).apply {
|
||||
descriptor.valueParameters.forEach {
|
||||
this.addElement(expression.getValueArgument(it)!!)
|
||||
}
|
||||
}
|
||||
putValueArgument(varargParameter.index, varargArgument)
|
||||
}
|
||||
generateWithStubs { generateCCall(expression, builder, isInvoke = true) }
|
||||
}
|
||||
IntrinsicType.INTEROP_SIGN_EXTEND, IntrinsicType.INTEROP_NARROW -> {
|
||||
|
||||
|
||||
+31
@@ -24,14 +24,17 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
|
||||
@@ -44,6 +47,14 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
internal fun irBuilder(irBuiltIns: IrBuiltIns, scopeOwnerSymbol: IrSymbol): IrBuilderWithScope =
|
||||
object : IrBuilderWithScope(
|
||||
IrGeneratorContextBase(irBuiltIns),
|
||||
Scope(scopeOwnerSymbol),
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET
|
||||
) {}
|
||||
|
||||
//TODO: delete file on next kotlin dependency update
|
||||
internal fun IrExpression.isNullConst() = this is IrConst<*> && this.kind == IrConstKind.Null
|
||||
|
||||
@@ -506,6 +517,26 @@ fun IrBuilderWithScope.irCallOp(
|
||||
fun IrBuilderWithScope.irSetVar(variable: IrVariable, value: IrExpression) =
|
||||
irSetVar(variable.symbol, value)
|
||||
|
||||
fun IrBuilderWithScope.irCatch(type: IrType) =
|
||||
IrCatchImpl(
|
||||
startOffset, endOffset,
|
||||
WrappedVariableDescriptor().let { descriptor ->
|
||||
IrVariableImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
|
||||
IrVariableSymbolImpl(descriptor),
|
||||
Name.identifier("e"),
|
||||
type,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
).apply {
|
||||
descriptor.bind(this)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Binds the arguments explicitly represented in the IR to the parameters of the accessed function.
|
||||
* The arguments are to be evaluated in the same order as they appear in the resulting list.
|
||||
|
||||
@@ -2882,6 +2882,10 @@ kotlinNativeInterop {
|
||||
defFile 'interop/basics/ctypes.def'
|
||||
}
|
||||
|
||||
ccallbacksAndVarargs {
|
||||
defFile 'interop/basics/ccallbacksAndVarargs.def'
|
||||
}
|
||||
|
||||
if (isMac()) {
|
||||
objcSmoke {
|
||||
defFile 'interop/objc/objcSmoke.def'
|
||||
@@ -2982,6 +2986,12 @@ task interop_types(type: RunInteropKonanTest) {
|
||||
interop = 'ctypes'
|
||||
}
|
||||
|
||||
task interop_callbacksAndVarargs(type: RunInteropKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
source = "interop/basics/callbacksAndVarargs.kt"
|
||||
interop = 'ccallbacksAndVarargs'
|
||||
}
|
||||
|
||||
task interop_echo_server(type: RunInteropKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
if (!isMac()) {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
import kotlin.test.*
|
||||
import kotlinx.cinterop.*
|
||||
import ccallbacksAndVarargs.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
assertEquals(42, getX(staticCFunction { -> cValue<S> { x = 42 } }))
|
||||
applyCallback(cValue { x = 17 }, staticCFunction { it: CValue<S> ->
|
||||
assertEquals(17, it.useContents { x })
|
||||
})
|
||||
|
||||
assertEquals(66, makeS(66, 111).useContents { x })
|
||||
assertEquals(E.ONE, makeE(1))
|
||||
|
||||
getVarargs(
|
||||
0,
|
||||
true,
|
||||
2.toByte(),
|
||||
Short.MIN_VALUE,
|
||||
42,
|
||||
Long.MAX_VALUE,
|
||||
3.14f,
|
||||
2.71,
|
||||
0x1234ABCDL.toCPointer<COpaque>(),
|
||||
UByte.MAX_VALUE,
|
||||
22.toUShort(),
|
||||
111u,
|
||||
ULong.MAX_VALUE,
|
||||
E.TWO,
|
||||
cValue<S> { x = 15 }
|
||||
).useContents {
|
||||
assertEquals(1, a1)
|
||||
assertEquals(2.toByte(), a2)
|
||||
assertEquals(Short.MIN_VALUE, a3)
|
||||
assertEquals(42, a4)
|
||||
assertEquals(Long.MAX_VALUE, a5)
|
||||
assertEquals(3.14f, a6)
|
||||
assertEquals(2.71, a7)
|
||||
assertEquals(0x1234ABCDL, a8.toLong())
|
||||
assertEquals(UByte.MAX_VALUE, a9)
|
||||
assertEquals(22.toUShort(), a10)
|
||||
assertEquals(111u, a11)
|
||||
assertEquals(ULong.MAX_VALUE, a12)
|
||||
assertEquals(E.TWO, a13)
|
||||
assertEquals(15, a14.x)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
|
||||
---
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
struct S {
|
||||
int x;
|
||||
};
|
||||
|
||||
static int getX(struct S (*callback)(void)) {
|
||||
return callback().x;
|
||||
}
|
||||
|
||||
static void applyCallback(struct S s, void (*callback)(struct S)) {
|
||||
callback(s);
|
||||
}
|
||||
|
||||
static struct S makeS(int x, ...) {
|
||||
return (struct S){ x };
|
||||
}
|
||||
|
||||
enum E {
|
||||
ZERO, ONE, TWO
|
||||
};
|
||||
|
||||
static enum E makeE(int ordinal, ...) {
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
struct Args {
|
||||
char a1;
|
||||
char a2;
|
||||
short a3;
|
||||
int a4;
|
||||
long long a5;
|
||||
float a6;
|
||||
double a7;
|
||||
void* a8;
|
||||
unsigned char a9;
|
||||
unsigned short a10;
|
||||
unsigned int a11;
|
||||
unsigned long long a12;
|
||||
enum E a13;
|
||||
struct S a14;
|
||||
};
|
||||
|
||||
static struct Args getVarargs(int ignore, ...) {
|
||||
va_list args;
|
||||
va_start(args, ignore);
|
||||
|
||||
struct Args result = {
|
||||
va_arg(args, char),
|
||||
va_arg(args, char),
|
||||
va_arg(args, short),
|
||||
va_arg(args, int),
|
||||
va_arg(args, long long),
|
||||
va_arg(args, double),
|
||||
va_arg(args, double),
|
||||
va_arg(args, void*),
|
||||
va_arg(args, unsigned char),
|
||||
va_arg(args, unsigned short),
|
||||
va_arg(args, unsigned int),
|
||||
va_arg(args, unsigned long long),
|
||||
va_arg(args, enum E),
|
||||
va_arg(args, struct S)
|
||||
};
|
||||
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
+16
-24
@@ -36,10 +36,10 @@ llvmVersion = 6.0.1
|
||||
# Mac OS X.
|
||||
llvmHome.macos_x64 = clang-llvm-6.0.1-darwin-macos
|
||||
targetToolchain.macos_x64 = target-toolchain-8-macos_x64
|
||||
libffiDir.macos_x64 = libffi-3.2.1-3-darwin-macos
|
||||
|
||||
arch.macos_x64 = x86_64
|
||||
targetSysRoot.macos_x64 = target-sysroot-8-macos_x64
|
||||
libffiDir.macos_x64 = libffi-3.2.1-3-darwin-macos
|
||||
llvmLtoFlags.macos_x64 =
|
||||
llvmLtoOptFlags.macos_x64 = -O3 -function-sections
|
||||
llvmLtoNooptFlags.macos_x64 = -O1
|
||||
@@ -65,7 +65,7 @@ target-toolchain-8-macos_x64.default = \
|
||||
# Apple's 32-bit iOS.
|
||||
targetToolchain.macos_x64-ios_arm32 = target-toolchain-8-macos_x64
|
||||
dependencies.macos_x64-ios_arm32 = \
|
||||
libffi-3.2.1-3-darwin-ios \
|
||||
libffi-3.2.1-3-darwin-macos \
|
||||
clang-llvm-6.0.1-darwin-macos
|
||||
|
||||
target-sysroot-8-ios_arm32.default = \
|
||||
@@ -75,7 +75,6 @@ arch.ios_arm32 = armv7
|
||||
entrySelector.ios_arm32 = -alias _Konan_main _main
|
||||
# Shared with 64-bit version.
|
||||
targetSysRoot.ios_arm32 = target-sysroot-8-ios_arm64
|
||||
libffiDir.ios_arm32 = libffi-3.2.1-3-darwin-ios
|
||||
llvmLtoFlags.ios_arm32 =
|
||||
llvmLtoOptFlags.ios_arm32 = -O3 -function-sections
|
||||
linkerNoDebugFlags.ios_arm32 = -S
|
||||
@@ -91,7 +90,7 @@ osVersionMin.ios_arm32 = 9.0
|
||||
# Apple's 64-bit iOS.
|
||||
targetToolchain.macos_x64-ios_arm64 = target-toolchain-8-macos_x64
|
||||
dependencies.macos_x64-ios_arm64 = \
|
||||
libffi-3.2.1-3-darwin-ios \
|
||||
libffi-3.2.1-3-darwin-macos \
|
||||
clang-llvm-6.0.1-darwin-macos
|
||||
|
||||
target-sysroot-8-ios_arm64.default = \
|
||||
@@ -100,7 +99,6 @@ target-sysroot-8-ios_arm64.default = \
|
||||
arch.ios_arm64 = arm64
|
||||
entrySelector.ios_arm64 = -alias _Konan_main _main
|
||||
targetSysRoot.ios_arm64 = target-sysroot-8-ios_arm64
|
||||
libffiDir.ios_arm64 = libffi-3.2.1-3-darwin-ios
|
||||
llvmLtoFlags.ios_arm64 =
|
||||
llvmLtoOptFlags.ios_arm64 = -O3 -function-sections
|
||||
linkerNoDebugFlags.ios_arm64 = -S
|
||||
@@ -116,7 +114,7 @@ osVersionMin.ios_arm64 = 9.0
|
||||
# Apple's iOS simulator.
|
||||
targetToolchain.macos_x64-ios_x64 = target-toolchain-8-macos_x64
|
||||
dependencies.macos_x64-ios_x64 = \
|
||||
libffi-3.2.1-2-darwin-ios_sim \
|
||||
libffi-3.2.1-3-darwin-macos \
|
||||
clang-llvm-6.0.1-darwin-macos
|
||||
|
||||
target-sysroot-8-ios_x64.default = \
|
||||
@@ -125,7 +123,6 @@ target-sysroot-8-ios_x64.default = \
|
||||
arch.ios_x64 = x86_64
|
||||
entrySelector.ios_x64 = -alias _Konan_main _main
|
||||
targetSysRoot.ios_x64 = target-sysroot-8-ios_x64
|
||||
libffiDir.ios_x64 = libffi-3.2.1-2-darwin-ios_sim
|
||||
llvmLtoFlags.ios_x64 =
|
||||
llvmLtoOptFlags.ios_x64 = -O3 -function-sections
|
||||
llvmLtoNooptFlags.ios_x64 = -O1
|
||||
@@ -142,10 +139,10 @@ osVersionMin.ios_x64 = 9.0
|
||||
llvmHome.linux_x64 = clang-llvm-6.0.1-linux-x86-64
|
||||
gccToolchain.linux_x64 = target-gcc-toolchain-3-linux-x86-64
|
||||
targetToolchain.linux_x64 = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu
|
||||
libffiDir.linux_x64 = libffi-3.2.1-2-linux-x86-64
|
||||
|
||||
quadruple.linux_x64 = x86_64-unknown-linux-gnu
|
||||
targetSysRoot.linux_x64 = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot
|
||||
libffiDir.linux_x64 = libffi-3.2.1-2-linux-x86-64
|
||||
# targetSysroot-relative.
|
||||
libGcc.linux_x64 = ../../lib/gcc/x86_64-unknown-linux-gnu/4.8.5
|
||||
llvmLtoFlags.linux_x64 =
|
||||
@@ -174,8 +171,7 @@ dependencies.linux_x64-linux_arm32_hfp = \
|
||||
clang-llvm-6.0.1-linux-x86-64 \
|
||||
target-gcc-toolchain-3-linux-x86-64 \
|
||||
target-sysroot-1-raspberrypi \
|
||||
libffi-3.2.1-2-linux-x86-64 \
|
||||
libffi-3.2.1-2-raspberrypi
|
||||
libffi-3.2.1-2-linux-x86-64
|
||||
|
||||
quadruple.linux_arm32_hfp = armv6-unknown-linux-gnueabihf
|
||||
entrySelector.linux_arm32_hfp = --defsym main=Konan_main
|
||||
@@ -184,7 +180,6 @@ linkerDynamicFlags.linux_arm32_hfp = -shared
|
||||
linkerOptimizationFlags.linux_arm32_hfp = --gc-sections
|
||||
targetSysRoot.linux_arm32_hfp = target-sysroot-1-raspberrypi
|
||||
# We could reuse host toolchain here.
|
||||
libffiDir.linux_arm32_hfp = libffi-3.2.1-2-raspberrypi
|
||||
linkerKonanFlags.linux_arm32_hfp = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \
|
||||
--defsym __cxa_demangle=Konan_cxa_demangle
|
||||
# targetSysroot-relative.
|
||||
@@ -207,8 +202,7 @@ dependencies.linux_x64-linux_mips32 = \
|
||||
target-gcc-toolchain-2-linux-mips \
|
||||
target-gcc-toolchain-3-linux-x86-64 \
|
||||
target-sysroot-2-mips \
|
||||
libffi-3.2.1-2-linux-x86-64 \
|
||||
libffi-3.2.1-2-linux-mips
|
||||
libffi-3.2.1-2-linux-x86-64
|
||||
|
||||
quadruple.linux_mips32 = mips-unknown-linux-gnu
|
||||
entrySelector.linux_mips32 = --defsym main=Konan_main
|
||||
@@ -216,7 +210,6 @@ linkerOptimizationFlags.linux_mips32 = --gc-sections
|
||||
linkerDynamicFlags.linux_mips32 = -shared
|
||||
targetSysRoot.linux_mips32 = target-sysroot-2-mips
|
||||
# We could reuse host toolchain here.
|
||||
libffiDir.linux_mips32 = libffi-3.2.1-2-linux-mips
|
||||
linkerKonanFlags.linux_mips32 = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \
|
||||
--defsym __cxa_demangle=Konan_cxa_demangle -z notext
|
||||
# targetSysroot-relative.
|
||||
@@ -236,15 +229,13 @@ dependencies.linux_x64-linux_mipsel32 = \
|
||||
target-gcc-toolchain-2-linux-mips \
|
||||
target-gcc-toolchain-3-linux-x86-64 \
|
||||
target-sysroot-2-mipsel \
|
||||
libffi-3.2.1-2-linux-x86-64 \
|
||||
libffi-3.2.1-2-linux-mipsel
|
||||
libffi-3.2.1-2-linux-x86-64
|
||||
quadruple.linux_mipsel32 = mipsel-unknown-linux-gnu
|
||||
entrySelector.linux_mipsel32 = --defsym main=Konan_main
|
||||
linkerOptimizationFlags.linux_mipsel32 = --gc-sections
|
||||
linkerDynamicFlags.linux_mipsel32 = -shared
|
||||
targetSysRoot.linux_mipsel32 = target-sysroot-2-mipsel
|
||||
# We could reuse host toolchain here.
|
||||
libffiDir.linux_mipsel32 = libffi-3.2.1-2-linux-mipsel
|
||||
linkerKonanFlags.linux_mipsel32 = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread \
|
||||
--defsym __cxa_demangle=Konan_cxa_demangle -z notext
|
||||
# targetSysroot-relative.
|
||||
@@ -264,13 +255,13 @@ dependencies.macos_x64-android_arm32 = \
|
||||
clang-llvm-6.0.1-darwin-macos \
|
||||
target-sysroot-21-android_arm32 \
|
||||
target-toolchain-21-osx-android_arm32 \
|
||||
libffi-3.2.1-2-android_arm32
|
||||
libffi-3.2.1-3-darwin-macos
|
||||
targetToolchain.linux_x64-android_arm32 = target-toolchain-21-linux-android_arm32
|
||||
dependencies.linux_x64-android_arm32 = \
|
||||
clang-llvm-6.0.1-linux-x86-64 \
|
||||
target-sysroot-21-android_arm32 \
|
||||
target-toolchain-21-linux-android_arm32 \
|
||||
libffi-3.2.1-2-android_arm32
|
||||
libffi-3.2.1-2-linux-x86-64
|
||||
|
||||
quadruple.android_arm32 = arm-linux-androideabi
|
||||
entrySelector.android_arm32 = -Wl,--defsym -Wl,main=Konan_main
|
||||
@@ -279,7 +270,6 @@ llvmLtoOptFlags.android_arm32 = -O3 -function-sections
|
||||
linkerNoDebugFlags.android_arm32 = -Wl,-S
|
||||
llvmLtoNooptFlags.android_arm32 = -O1
|
||||
targetSysRoot.android_arm32 = target-sysroot-21-android_arm32
|
||||
libffiDir.android_arm32 = libffi-3.2.1-2-android_arm32
|
||||
linkerKonanFlags.android_arm32 = -lm -latomic -lstdc++ -landroid
|
||||
|
||||
# Android ARM64, based on NDK for android-21.
|
||||
@@ -289,29 +279,28 @@ dependencies.macos_x64-android_arm64 = \
|
||||
clang-llvm-6.0.1-darwin-macos \
|
||||
target-sysroot-21-android_arm64 \
|
||||
target-toolchain-21-osx-android_arm64 \
|
||||
libffi-3.2.1-2-android_arm64
|
||||
libffi-3.2.1-3-darwin-macos
|
||||
targetToolchain.linux_x64-android_arm64 = target-toolchain-21-linux-android_arm64
|
||||
dependencies.linux_x64-android_arm64 = \
|
||||
clang-llvm-6.0.1-linux-x86-64 \
|
||||
target-sysroot-21-android_arm64 \
|
||||
target-toolchain-21-linux-android_arm64 \
|
||||
libffi-3.2.1-2-android_arm64
|
||||
libffi-3.2.1-2-linux-x86-64
|
||||
|
||||
quadruple.android_arm64 = aarch64-linux-android
|
||||
entrySelector.android_arm64 = -Wl,--defsym -Wl,main=Konan_main
|
||||
llvmLtoFlags.android_arm64 = -emulated-tls -relocation-model=pic
|
||||
targetSysRoot.android_arm64 = target-sysroot-21-android_arm64
|
||||
libffiDir.android_arm64 = libffi-3.2.1-2-android_arm64
|
||||
linkerKonanFlags.android_arm64 = -lm -latomic -lstdc++ -landroid
|
||||
linkerNoDebugFlags.android_arm64 = -Wl,-S
|
||||
|
||||
# Windows x86-64, based on mingw-w64.
|
||||
llvmHome.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1
|
||||
targetToolchain.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1
|
||||
libffiDir.mingw_x64 = libffi-3.2.1-mingw-w64-x86-64
|
||||
|
||||
quadruple.mingw_x64 = x86_64-w64-mingw32
|
||||
targetSysRoot.mingw_x64 = msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1
|
||||
libffiDir.mingw_x64 = libffi-3.2.1-mingw-w64-x86-64
|
||||
# For using with Universal Windows Platform (UWP) we need to use this slower option.
|
||||
# See https://youtrack.jetbrains.com/issue/KT-27654.
|
||||
# TODO: remove, once fixed in mingw, check with the bug testcase.
|
||||
@@ -333,18 +322,21 @@ dependencies.mingw_x64 = \
|
||||
# WebAssembly 32-bit.
|
||||
targetToolchain.macos_x64-wasm32 = target-toolchain-3-macos-wasm
|
||||
dependencies.macos_x64-wasm32 = \
|
||||
libffi-3.2.1-3-darwin-macos \
|
||||
clang-llvm-6.0.1-darwin-macos \
|
||||
target-sysroot-2-wasm \
|
||||
target-toolchain-3-macos-wasm
|
||||
|
||||
targetToolchain.linux_x64-wasm32 = target-toolchain-2-linux-wasm
|
||||
dependencies.linux_x64-wasm32 = \
|
||||
libffi-3.2.1-2-linux-x86-64 \
|
||||
clang-llvm-6.0.1-linux-x86-64 \
|
||||
target-sysroot-2-wasm \
|
||||
target-toolchain-2-linux-wasm
|
||||
|
||||
targetToolchain.mingw_x64-wasm32 = target-toolchain-2-mingw-wasm
|
||||
dependencies.mingw_x64-wasm32 = \
|
||||
libffi-3.2.1-mingw-w64-x86-64 \
|
||||
msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 \
|
||||
target-sysroot-2-wasm \
|
||||
target-toolchain-2-mingw-wasm
|
||||
|
||||
@@ -25,6 +25,7 @@ boardSpecificClangFlags.zephyr_stm32f4_disco = -mabi=aapcs -mthumb -mcpu=cortex-
|
||||
|
||||
targetToolchain.linux_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-linux/arm-none-eabi
|
||||
dependencies.linux_x64-zephyr_stm32f4_disco = \
|
||||
libffi-3.2.1-2-linux-x86-64 \
|
||||
clang-llvm-6.0.1-linux-x86-64 \
|
||||
gcc-arm-none-eabi-7-2017-q4-major-linux \
|
||||
target-sysroot-2-wasm
|
||||
@@ -32,11 +33,13 @@ dependencies.linux_x64-zephyr_stm32f4_disco = \
|
||||
targetToolchain.macos_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-mac/arm-none-eabi
|
||||
dependencies.macos_x64-zephyr_stm32f4_disco = \
|
||||
gcc-arm-none-eabi-7-2017-q4-major-mac \
|
||||
libffi-3.2.1-3-darwin-macos \
|
||||
clang-llvm-6.0.1-darwin-macos \
|
||||
target-sysroot-2-wasm
|
||||
|
||||
targetToolchain.mingw_x64-zephyr_stm32f4_disco = gcc-arm-none-eabi-7-2017-q4-major-win32/arm-none-eabi
|
||||
dependencies.mingw_x64-zephyr_stm32f4_disco = \
|
||||
libffi-3.2.1-mingw-w64-x86-64 \
|
||||
msys2-mingw-w64-x86_64-gcc-7.3.0-clang-llvm-lld-6.0.1 \
|
||||
gcc-arm-none-eabi-7-2017-q4-major-win32 \
|
||||
target-sysroot-2-wasm
|
||||
|
||||
@@ -23,8 +23,6 @@ targetList.each { targetName ->
|
||||
if (!isWindows())
|
||||
compilerArgs '-fPIC'
|
||||
includeRuntime(delegate)
|
||||
if (rootProject.hasProperty("${targetName}LibffiDir"))
|
||||
compilerArgs '-I' + project.file(rootProject.ext.get("${targetName}LibffiDir") + "/include")
|
||||
linkerArgs project.file("../common/build/$targetName/hash.bc").path
|
||||
}
|
||||
|
||||
|
||||
@@ -18,85 +18,11 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef KONAN_NO_FFI
|
||||
#include <ffi.h>
|
||||
#endif
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Types.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
typedef int FfiTypeKind;
|
||||
|
||||
#ifndef KONAN_NO_FFI
|
||||
// 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;
|
||||
const FfiTypeKind FFI_TYPE_KIND_UINT8 = 8;
|
||||
const FfiTypeKind FFI_TYPE_KIND_UINT16 = 9;
|
||||
const FfiTypeKind FFI_TYPE_KIND_UINT32 = 10;
|
||||
const FfiTypeKind FFI_TYPE_KIND_UINT64 = 11;
|
||||
|
||||
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;
|
||||
case FFI_TYPE_KIND_UINT8: return &ffi_type_uint8;
|
||||
case FFI_TYPE_KIND_UINT16: return &ffi_type_uint16;
|
||||
case FFI_TYPE_KIND_UINT32: return &ffi_type_uint32;
|
||||
case FFI_TYPE_KIND_UINT64: return &ffi_type_uint64;
|
||||
|
||||
default: assert(false); return nullptr;
|
||||
}
|
||||
}
|
||||
#endif // KONAN_NO_FFI
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
void Kotlin_Interop_callFunctionPointer(void* codePtr, void* returnValuePtr, FfiTypeKind returnTypeKind,
|
||||
void** arguments, intptr_t* argumentTypeKinds,
|
||||
int totalArgumentsNumber, int variadicArgumentsNumber) {
|
||||
#ifdef KONAN_NO_FFI
|
||||
RuntimeAssert(false, "Vararg calls are not supported on this platform");
|
||||
#else
|
||||
ffi_type** argumentTypes = (ffi_type**)argumentTypeKinds;
|
||||
// In-place convertion:
|
||||
for (int i = 0; i < totalArgumentsNumber; ++i) {
|
||||
argumentTypes[i] = convertFfiTypeKindToType((FfiTypeKind) argumentTypeKinds[i]);
|
||||
}
|
||||
ffi_type* returnType = convertFfiTypeKindToType(returnTypeKind);
|
||||
|
||||
ffi_cif cif;
|
||||
if (variadicArgumentsNumber < 0) {
|
||||
// Non-variadic.
|
||||
ffi_prep_cif(&cif, FFI_DEFAULT_ABI, totalArgumentsNumber, returnType, argumentTypes);
|
||||
} else {
|
||||
int fixedArgumentsNumber = totalArgumentsNumber - variadicArgumentsNumber;
|
||||
ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI,
|
||||
fixedArgumentsNumber, totalArgumentsNumber,
|
||||
returnType, argumentTypes);
|
||||
}
|
||||
|
||||
ffi_call(&cif, (void (*)())codePtr, returnValuePtr, arguments);
|
||||
#endif
|
||||
}
|
||||
|
||||
KNativePtr Kotlin_Interop_createStablePointer(KRef any) {
|
||||
return CreateStablePointer(any);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,10 @@ open class Command(initialCommand: List<String>) {
|
||||
/**
|
||||
* If withErrors is true then output from error stream will be added
|
||||
*/
|
||||
fun getOutputLines(withErrors: Boolean = false): List<String> {
|
||||
fun getOutputLines(withErrors: Boolean = false): List<String> =
|
||||
getResult(withErrors, handleError = true).outputLines
|
||||
|
||||
fun getResult(withErrors: Boolean, handleError: Boolean = false): Result {
|
||||
log()
|
||||
|
||||
val outputFile = createTempFile()
|
||||
@@ -80,7 +83,7 @@ open class Command(initialCommand: List<String>) {
|
||||
|
||||
builder.redirectInput(Redirect.INHERIT)
|
||||
builder.redirectError(Redirect.INHERIT)
|
||||
builder.redirectOutput(ProcessBuilder.Redirect.to(outputFile))
|
||||
builder.redirectOutput(Redirect.to(outputFile))
|
||||
.redirectErrorStream(withErrors)
|
||||
// Note: getting process output could be done without redirecting to temporary file,
|
||||
// however this would require managing a thread to read `process.inputStream` because
|
||||
@@ -88,14 +91,16 @@ open class Command(initialCommand: List<String>) {
|
||||
|
||||
val process = builder.start()
|
||||
val code = process.waitFor()
|
||||
handleExitCode(code)
|
||||
if (handleError) handleExitCode(code)
|
||||
|
||||
return outputFile.readLines()
|
||||
return Result(code, outputFile.readLines())
|
||||
} finally {
|
||||
outputFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
class Result(val exitCode: Int, val outputLines: List<String>)
|
||||
|
||||
private fun handleExitCode(code: Int) {
|
||||
if (code != 0) throw KonanExternalToolFailure("The ${command[0]} command returned non-zero exit code: $code.", command[0])
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ interface Configurables : TargetableExternalStorage {
|
||||
|
||||
val llvmHome get() = hostString("llvmHome")
|
||||
val llvmVersion get() = hostString("llvmVersion")
|
||||
val libffiDir get() = hostString("libffiDir")
|
||||
|
||||
// TODO: Delegate to a map?
|
||||
val llvmLtoNooptFlags get() = targetList("llvmLtoNooptFlags")
|
||||
@@ -37,7 +38,6 @@ interface Configurables : TargetableExternalStorage {
|
||||
val linkerDynamicFlags get() = targetList("linkerDynamicFlags")
|
||||
val llvmDebugOptFlags get() = targetList("llvmDebugOptFlags")
|
||||
val targetSysRoot get() = targetString("targetSysRoot")
|
||||
val libffiDir get() = targetString("libffiDir")
|
||||
|
||||
// Notice: these ones are host-target.
|
||||
val targetToolchain get() = hostTargetString("targetToolchain")
|
||||
@@ -45,7 +45,6 @@ interface Configurables : TargetableExternalStorage {
|
||||
val absoluteTargetSysRoot get() = absolute(targetSysRoot)
|
||||
val absoluteTargetToolchain get() = absolute(targetToolchain)
|
||||
val absoluteLlvmHome get() = absolute(llvmHome)
|
||||
val absoluteLibffiDir get() = absolute(libffiDir)
|
||||
}
|
||||
|
||||
interface NonAppleConfigurables : Configurables {
|
||||
|
||||
@@ -44,9 +44,6 @@ abstract class LinkerFlags(val configurables: Configurables)
|
||||
else -> error("Don't know libLTO location for this platform.")
|
||||
}
|
||||
|
||||
val targetLibffi = configurables.libffiDir?.let { listOf("${configurables.absoluteLibffiDir}/lib/libffi.a") }
|
||||
?: emptyList()
|
||||
|
||||
open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor.
|
||||
|
||||
abstract fun linkCommands(objectFiles: List<ObjectFile>, executable: ExecutableFile,
|
||||
|
||||
Reference in New Issue
Block a user