Generate C wrapper instead of bridge for all functions. (#3153)
[C Interop] Generate C wrapper instead of bridge for all functions.
This commit is contained in:
+8
-3
@@ -469,6 +469,11 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
Language.OBJECTIVE_C -> supplier()
|
||||
}
|
||||
|
||||
// We omit `const` qualifier for IntegerType and FloatingType to make `CBridgeGen` simpler.
|
||||
// See KT-28102.
|
||||
private fun String.dropConstQualifier() =
|
||||
substringAfterLast("const ")
|
||||
|
||||
private fun convertUnqualifiedPrimitiveType(type: CValue<CXType>): Type = when (type.kind) {
|
||||
CXTypeKind.CXType_Char_U, CXTypeKind.CXType_Char_S -> {
|
||||
assert(type.getSize() == 1L)
|
||||
@@ -479,19 +484,19 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
||||
CXTypeKind.CXType_UInt, CXTypeKind.CXType_ULong, CXTypeKind.CXType_ULongLong -> IntegerType(
|
||||
size = type.getSize().toInt(),
|
||||
isSigned = false,
|
||||
spelling = clang_getTypeSpelling(type).convertAndDispose()
|
||||
spelling = clang_getTypeSpelling(type).convertAndDispose().dropConstQualifier()
|
||||
)
|
||||
|
||||
CXTypeKind.CXType_SChar, CXTypeKind.CXType_Short,
|
||||
CXTypeKind.CXType_Int, CXTypeKind.CXType_Long, CXTypeKind.CXType_LongLong -> IntegerType(
|
||||
size = type.getSize().toInt(),
|
||||
isSigned = true,
|
||||
spelling = clang_getTypeSpelling(type).convertAndDispose()
|
||||
spelling = clang_getTypeSpelling(type).convertAndDispose().dropConstQualifier()
|
||||
)
|
||||
|
||||
CXTypeKind.CXType_Float, CXTypeKind.CXType_Double -> FloatingType(
|
||||
size = type.getSize().toInt(),
|
||||
spelling = clang_getTypeSpelling(type).convertAndDispose()
|
||||
spelling = clang_getTypeSpelling(type).convertAndDispose().dropConstQualifier()
|
||||
)
|
||||
|
||||
CXTypeKind.CXType_Bool -> CBoolType
|
||||
|
||||
+2
-1
@@ -264,7 +264,8 @@ open class BoolType: PrimitiveType
|
||||
object CBoolType : BoolType()
|
||||
|
||||
object ObjCBoolType : BoolType()
|
||||
|
||||
// We omit `const` qualifier for IntegerType and FloatingType to make `CBridgeGen` simpler.
|
||||
// See KT-28102.
|
||||
data class IntegerType(val size: Int, val isSigned: Boolean, val spelling: String) : PrimitiveType
|
||||
|
||||
// TODO: floating type is not actually defined entirely by its size.
|
||||
|
||||
+48
-2
@@ -6,6 +6,8 @@ package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.ObjCProtocol
|
||||
import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
class BridgeBuilderResult(
|
||||
@@ -16,6 +18,8 @@ class BridgeBuilderResult(
|
||||
val excludedStubs: Set<StubIrElement>
|
||||
)
|
||||
|
||||
private data class CCalleeWrapper(val name: String, val lines: List<String>)
|
||||
|
||||
/**
|
||||
* Generates [NativeBridges] and corresponding function bodies and property accessors.
|
||||
*/
|
||||
@@ -68,6 +72,12 @@ class StubIrBridgeBuilder(
|
||||
private val excludedStubs = mutableSetOf<StubIrElement>()
|
||||
|
||||
private val bridgeGeneratingVisitor = object : StubIrVisitor<StubContainer?, Unit> {
|
||||
|
||||
private var currentFunctionWrapperId = 0
|
||||
|
||||
private fun generateFunctionWrapperName(functionName: String) =
|
||||
"${functionName}_wrapper${currentFunctionWrapperId++}"
|
||||
|
||||
override fun visitClass(element: ClassStub, owner: StubContainer?) {
|
||||
element.annotations.filterIsInstance<AnnotationStub.ObjC.ExternalClass>().firstOrNull()?.let {
|
||||
if (it.protocolGetter.isNotEmpty() && element.origin is StubOrigin.ObjCProtocol) {
|
||||
@@ -104,14 +114,49 @@ class StubIrBridgeBuilder(
|
||||
val cCallAnnotation = function.annotations.firstIsInstanceOrNull<AnnotationStub.CCall.Symbol>()
|
||||
?: return
|
||||
val cCallSymbolName = cCallAnnotation.symbolName
|
||||
val (wrapperName, wrapperLines) = generateCCalleeWrapper(origin)
|
||||
simpleBridgeGenerator.insertNativeBridge(
|
||||
function,
|
||||
emptyList(),
|
||||
listOf("extern const void* $cCallSymbolName __asm(${cCallSymbolName.quoteAsKotlinLiteral()});",
|
||||
"extern const void* $cCallSymbolName = &${origin.function.name};")
|
||||
listOf(
|
||||
*wrapperLines.toTypedArray(),
|
||||
"const void* $cCallSymbolName __asm(${cCallSymbolName.quoteAsKotlinLiteral()});",
|
||||
"const void* $cCallSymbolName = &$wrapperName;"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Some functions don't have an address (e.g. macros-based or builtins).
|
||||
* To solve this problem we generate a wrapper function.
|
||||
*/
|
||||
private fun generateCCalleeWrapper(origin: StubOrigin.Function): CCalleeWrapper =
|
||||
if (origin.function.isVararg) {
|
||||
CCalleeWrapper(origin.function.name, emptyList())
|
||||
} else {
|
||||
val function = origin.function
|
||||
val wrapperName = generateFunctionWrapperName(function.name)
|
||||
|
||||
val returnType = function.returnType.getStringRepresentation()
|
||||
val parameters = function.parameters.mapIndexed { index, parameter ->
|
||||
"p$index" to parameter.type.getStringRepresentation()
|
||||
}
|
||||
val callExpression = "${function.name}(${parameters.joinToString { it.first }});"
|
||||
val wrapperBody = if (function.returnType.unwrapTypedefs() is VoidType) {
|
||||
callExpression
|
||||
} else {
|
||||
"return $callExpression"
|
||||
}
|
||||
|
||||
val alwaysInline = "__attribute__((always_inline))"
|
||||
val lines = listOf(
|
||||
"$alwaysInline $returnType $wrapperName(${parameters.joinToString { "${it.second} ${it.first}" }}) {",
|
||||
wrapperBody,
|
||||
"}"
|
||||
)
|
||||
CCalleeWrapper(wrapperName, lines)
|
||||
}
|
||||
|
||||
override fun visitProperty(element: PropertyStub, owner: StubContainer?) {
|
||||
try {
|
||||
when (val kind = element.kind) {
|
||||
@@ -226,6 +271,7 @@ class StubIrBridgeBuilder(
|
||||
}
|
||||
|
||||
private fun generateBridgeBody(function: FunctionStub) {
|
||||
assert(context.platform == KotlinPlatform.JVM) { "Function ${function.name} was not marked as external." }
|
||||
assert(function.origin is StubOrigin.Function) { "Can't create bridge for ${function.name}" }
|
||||
val origin = function.origin as StubOrigin.Function
|
||||
val bodyGenerator = KotlinCodeBuilder(scope = kotlinFile)
|
||||
|
||||
+6
-4
@@ -349,13 +349,15 @@ internal class FunctionStubBuilder(
|
||||
|
||||
val annotations: List<AnnotationStub>
|
||||
val mustBeExternal: Boolean
|
||||
if (!func.isVararg || platform != KotlinPlatform.NATIVE) {
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
annotations = emptyList()
|
||||
mustBeExternal = false
|
||||
} else {
|
||||
val type = WrapperStubType(KotlinTypes.any.makeNullable())
|
||||
parameters += FunctionParameterStub("variadicArguments", type, isVararg = true)
|
||||
annotations = listOf(AnnotationStub.CCall.Symbol(context.generateNextUniqueId("knifunptr_")))
|
||||
if (func.isVararg) {
|
||||
val type = WrapperStubType(KotlinTypes.any.makeNullable())
|
||||
parameters += FunctionParameterStub("variadicArguments", type, isVararg = true)
|
||||
}
|
||||
annotations = listOf(AnnotationStub.CCall.Symbol(context.generateNextUniqueId("knifunptr_") + "_${func.name}"))
|
||||
mustBeExternal = true
|
||||
}
|
||||
val functionStub = FunctionStub(
|
||||
|
||||
+16
-15
@@ -217,30 +217,31 @@ class StubIrTextEmitter(
|
||||
override fun visitFunction(element: FunctionStub, owner: StubContainer?) {
|
||||
if (element in bridgeBuilderResult.excludedStubs) return
|
||||
|
||||
val modality = renderMemberModality(element.modality, owner)
|
||||
val header = run {
|
||||
val parameters = element.parameters.joinToString(prefix = "(", postfix = ")") { renderFunctionParameter(it) }
|
||||
val receiver = element.receiver?.let { renderFunctionReceiver(it) + "." } ?: ""
|
||||
val typeParameters = renderTypeParameters(element.typeParameters)
|
||||
val modality = renderMemberModality(element.modality, owner)
|
||||
"${modality}fun$typeParameters $receiver${element.name.asSimpleName()}$parameters: ${renderStubType(element.returnType)}"
|
||||
}
|
||||
if (!nativeBridges.isSupported(element)) {
|
||||
sequenceOf(
|
||||
annotationForUnableToImport,
|
||||
"$header = throw UnsupportedOperationException()"
|
||||
).forEach(out)
|
||||
return
|
||||
}
|
||||
element.annotations.forEach {
|
||||
out(renderAnnotation(it))
|
||||
}
|
||||
val parameters = element.parameters.joinToString(prefix = "(", postfix = ")") { renderFunctionParameter(it) }
|
||||
val receiver = element.receiver?.let { renderFunctionReceiver(it) + "." } ?: ""
|
||||
val typeParameters = renderTypeParameters(element.typeParameters)
|
||||
val header = "${modality}fun$typeParameters $receiver${element.name.asSimpleName()}$parameters: ${renderStubType(element.returnType)}"
|
||||
when {
|
||||
element.external -> out("external $header")
|
||||
element.isOptionalObjCMethod() -> out("$header = optional()")
|
||||
owner != null && owner.isInterface -> out(header)
|
||||
else -> if (nativeBridges.isSupported(element)) {
|
||||
block(header) {
|
||||
functionBridgeBodies.getValue(element).forEach(out)
|
||||
}
|
||||
} else {
|
||||
sequenceOf(
|
||||
annotationForUnableToImport,
|
||||
"$header = throw UnsupportedOperationException()"
|
||||
).forEach(out)
|
||||
else -> block(header) {
|
||||
functionBridgeBodies.getValue(element).forEach(out)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun visitProperty(element: PropertyStub, owner: StubContainer?) {
|
||||
|
||||
+3
-2
@@ -38,7 +38,8 @@ fun Type.getStringRepresentation(): String = when (this) {
|
||||
is IntegerType -> this.spelling
|
||||
is FloatingType -> this.spelling
|
||||
|
||||
is PointerType, is ArrayType -> "void*"
|
||||
is PointerType -> getPointerTypeStringRepresentation(this.pointeeType)
|
||||
is ArrayType -> getPointerTypeStringRepresentation(this.elemType)
|
||||
|
||||
is RecordType -> this.decl.spelling
|
||||
|
||||
@@ -58,7 +59,7 @@ fun Type.getStringRepresentation(): String = when (this) {
|
||||
is ObjCBlockPointer -> "id"
|
||||
}
|
||||
|
||||
else -> throw kotlin.NotImplementedError()
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun getPointerTypeStringRepresentation(pointee: Type): String =
|
||||
|
||||
Reference in New Issue
Block a user