From c42a53bcc9c785ee60e696b1b67c5e48b5eea465 Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov <1580082+sbogolepov@users.noreply.github.com> Date: Mon, 7 Oct 2019 10:00:17 +0300 Subject: [PATCH] [C Interop] Use CCall annotation for global getters and setters. (#3420) --- .../kotlinx/cinterop/internal/Annotations.kt | 6 +- .../native/interop/gen/CWrapperGenerator.kt | 82 ++++++++++++++ .../interop/gen/SimpleBridgeGeneratorImpl.kt | 6 +- .../native/interop/gen/StubIrBridgeBuilder.kt | 100 +++++++----------- .../native/interop/gen/StubIrBuilder.kt | 72 +++++++++---- .../interop/gen/StubIrElementBuilders.kt | 45 +++++--- 6 files changed, 209 insertions(+), 102 deletions(-) create mode 100644 Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/CWrapperGenerator.kt diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/internal/Annotations.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/internal/Annotations.kt index c21d5c01aea..9a6efc50cb2 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/internal/Annotations.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/internal/Annotations.kt @@ -4,7 +4,11 @@ package kotlinx.cinterop.internal @Retention(AnnotationRetention.BINARY) annotation class CStruct(val spelling: String) -@Target(AnnotationTarget.FUNCTION) +@Target( + AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.PROPERTY_SETTER +) @Retention(AnnotationRetention.BINARY) public annotation class CCall(val id: String) { @Target(AnnotationTarget.VALUE_PARAMETER) diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/CWrapperGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/CWrapperGenerator.kt new file mode 100644 index 00000000000..5bd8a8f2fa5 --- /dev/null +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/CWrapperGenerator.kt @@ -0,0 +1,82 @@ +package org.jetbrains.kotlin.native.interop.gen + +import org.jetbrains.kotlin.native.interop.indexer.FunctionDecl +import org.jetbrains.kotlin.native.interop.indexer.GlobalDecl +import org.jetbrains.kotlin.native.interop.indexer.VoidType +import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs + +internal data class CCalleeWrapper(val lines: List) + +/** + * Some functions don't have an address (e.g. macros-based or builtins). + * To solve this problem we generate a wrapper function. + */ +internal class CWrappersGenerator(private val context: StubIrContext) { + + private var currentFunctionWrapperId = 0 + + private val packageName = + context.configuration.pkgName.replace(INVALID_CLANG_IDENTIFIER_REGEX, "_") + + private fun generateFunctionWrapperName(functionName: String): String { + return "${packageName}_${functionName}_wrapper${currentFunctionWrapperId++}" + } + + private fun bindSymbolToFunction(symbol: String, function: String): List = listOf( + "const void* $symbol __asm(${symbol.quoteAsKotlinLiteral()});", + "const void* $symbol = &$function;" + ) + + private data class Parameter(val type: String, val name: String) + + private fun createWrapper( + symbolName: String, + wrapperName: String, + returnType: String, + parameters: List, + body: String + ): List = listOf( + "__attribute__((always_inline))", + "$returnType $wrapperName(${parameters.joinToString { "${it.type} ${it.name}" }}) {", + body, + "}", + *bindSymbolToFunction(symbolName, wrapperName).toTypedArray() + ) + + fun generateCCalleeWrapper(function: FunctionDecl, symbolName: String): CCalleeWrapper = + if (function.isVararg) { + CCalleeWrapper(bindSymbolToFunction(symbolName, function.name)) + } else { + val wrapperName = generateFunctionWrapperName(function.name) + + val returnType = function.returnType.getStringRepresentation() + val parameters = function.parameters.mapIndexed { index, parameter -> + Parameter(parameter.type.getStringRepresentation(), "p$index") + } + val callExpression = "${function.name}(${parameters.joinToString { it.name }});" + val wrapperBody = if (function.returnType.unwrapTypedefs() is VoidType) { + callExpression + } else { + "return $callExpression" + } + val wrapper = createWrapper(symbolName, wrapperName, returnType, parameters, wrapperBody) + CCalleeWrapper(wrapper) + } + + fun generateCGlobalGetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper { + val wrapperName = generateFunctionWrapperName("${globalDecl.name}_getter") + val returnType = globalDecl.type.getStringRepresentation() + val wrapperBody = "return ${globalDecl.name};" + val wrapper = createWrapper(symbolName, wrapperName, returnType, emptyList(), wrapperBody) + return CCalleeWrapper(wrapper) + } + + fun generateCGlobalSetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper { + val wrapperName = generateFunctionWrapperName("${globalDecl.name}_setter") + val globalType = globalDecl.type.getStringRepresentation() + val parameter = Parameter(globalType, "p1") + val wrapperBody = "${globalDecl.name} = ${parameter.name};" + val wrapper = createWrapper(symbolName, wrapperName, "void", listOf(parameter), wrapperBody) + return CCalleeWrapper(wrapper) + } +} diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt index f8f88202118..374bb44cdee 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt @@ -21,6 +21,8 @@ import org.jetbrains.kotlin.native.interop.indexer.CompilationWithPCH import org.jetbrains.kotlin.native.interop.indexer.Language import org.jetbrains.kotlin.native.interop.indexer.mapFragmentIsCompilable +internal val INVALID_CLANG_IDENTIFIER_REGEX = "[^a-zA-Z1-9_]".toRegex() + class SimpleBridgeGeneratorImpl( private val platform: KotlinPlatform, private val pkgName: String, @@ -246,8 +248,4 @@ class SimpleBridgeGeneratorImpl( nativeBacked !in excludedClients } } - - companion object { - internal val INVALID_CLANG_IDENTIFIER_REGEX = "[^a-zA-Z1-9_]".toRegex() - } } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBridgeBuilder.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBridgeBuilder.kt index 1602c6d62a6..fd75d03c1b9 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBridgeBuilder.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBridgeBuilder.kt @@ -4,11 +4,8 @@ */ package org.jetbrains.kotlin.native.interop.gen -import org.jetbrains.kotlin.native.interop.gen.SimpleBridgeGeneratorImpl.Companion.INVALID_CLANG_IDENTIFIER_REGEX 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.native.interop.indexer.* import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull class BridgeBuilderResult( @@ -19,8 +16,6 @@ class BridgeBuilderResult( val excludedStubs: Set ) -private data class CCalleeWrapper(val name: String, val lines: List) - /** * Generates [NativeBridges] and corresponding function bodies and property accessors. */ @@ -30,6 +25,8 @@ class StubIrBridgeBuilder( private val globalAddressExpressions = mutableMapOf, KotlinExpression>() + private val wrapperGenerator = CWrappersGenerator(context) + private fun getGlobalAddressExpression(cGlobalName: String, accessor: PropertyAccessor) = globalAddressExpressions.getOrPut(Pair(cGlobalName, accessor)) { simpleBridgeGenerator.kotlinToNative( @@ -74,13 +71,6 @@ class StubIrBridgeBuilder( private val bridgeGeneratingVisitor = object : StubIrVisitor { - private var currentFunctionWrapperId = 0 - - private fun generateFunctionWrapperName(packageName: String, functionName: String): String { - val validPackageName = packageName.replace(INVALID_CLANG_IDENTIFIER_REGEX, "_") - return "${validPackageName}_${functionName}_wrapper${currentFunctionWrapperId++}" - } - override fun visitClass(element: ClassStub, owner: StubContainer?) { element.annotations.filterIsInstance().firstOrNull()?.let { if (it.protocolGetter.isNotEmpty() && element.origin is StubOrigin.ObjCProtocol) { @@ -116,50 +106,10 @@ class StubIrBridgeBuilder( ?: return val cCallAnnotation = function.annotations.firstIsInstanceOrNull() ?: return - val cCallSymbolName = cCallAnnotation.symbolName - val (wrapperName, wrapperLines) = generateCCalleeWrapper(origin) - simpleBridgeGenerator.insertNativeBridge( - function, - emptyList(), - listOf( - *wrapperLines.toTypedArray(), - "const void* $cCallSymbolName __asm(${cCallSymbolName.quoteAsKotlinLiteral()});", - "const void* $cCallSymbolName = &$wrapperName;" - ) - ) + val wrapper = wrapperGenerator.generateCCalleeWrapper(origin.function, cCallAnnotation.symbolName) + simpleBridgeGenerator.insertNativeBridge(function, emptyList(), wrapper.lines) } - /** - * 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(context.configuration.pkgName, 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) { @@ -185,14 +135,11 @@ class StubIrBridgeBuilder( override fun visitPropertyAccessor(accessor: PropertyAccessor, owner: StubContainer?) { when (accessor) { is PropertyAccessor.Getter.SimpleGetter -> { - if (accessor in builderResult.bridgeGenerationComponents.getterToBridgeInfo) { - val extra = builderResult.bridgeGenerationComponents.getterToBridgeInfo.getValue(accessor) - val typeInfo = extra.typeInfo - val expression = if (extra.isArray) { - val getAddressExpression = getGlobalAddressExpression(extra.cGlobalName, accessor) - typeInfo.argFromBridged(getAddressExpression, kotlinFile, nativeBacked = accessor) + "!!" - } else { - typeInfo.argFromBridged(simpleBridgeGenerator.kotlinToNative( + when (accessor) { + in builderResult.bridgeGenerationComponents.getterToBridgeInfo -> { + val extra = builderResult.bridgeGenerationComponents.getterToBridgeInfo.getValue(accessor) + val typeInfo = extra.typeInfo + propertyAccessorBridgeBodies[accessor] = typeInfo.argFromBridged(simpleBridgeGenerator.kotlinToNative( nativeBacked = accessor, returnType = typeInfo.bridgedType, kotlinValues = emptyList(), @@ -201,7 +148,12 @@ class StubIrBridgeBuilder( typeInfo.cToBridged(expr = extra.cGlobalName) }, kotlinFile, nativeBacked = accessor) } - propertyAccessorBridgeBodies[accessor] = expression + in builderResult.bridgeGenerationComponents.arrayGetterInfo -> { + val extra = builderResult.bridgeGenerationComponents.arrayGetterInfo.getValue(accessor) + val typeInfo = extra.typeInfo + val getAddressExpression = getGlobalAddressExpression(extra.cGlobalName, accessor) + propertyAccessorBridgeBodies[accessor] = typeInfo.argFromBridged(getAddressExpression, kotlinFile, nativeBacked = accessor) + "!!" + } } } @@ -245,6 +197,26 @@ class StubIrBridgeBuilder( val getAddressExpression = getGlobalAddressExpression(accessor.cGlobalName, accessor) propertyAccessorBridgeBodies[accessor] = getAddressExpression } + + is PropertyAccessor.Getter.ExternalGetter -> { + if (accessor in builderResult.wrapperGenerationComponents.getterToWrapperInfo) { + val extra = builderResult.wrapperGenerationComponents.getterToWrapperInfo.getValue(accessor) + val cCallAnnotation = accessor.annotations.firstIsInstanceOrNull() + ?: error("external getter for ${extra.global.name} wasn't marked with @CCall") + val wrapper = wrapperGenerator.generateCGlobalGetter(extra.global, cCallAnnotation.symbolName) + simpleBridgeGenerator.insertNativeBridge(accessor, emptyList(), wrapper.lines) + } + } + + is PropertyAccessor.Setter.ExternalSetter -> { + if (accessor in builderResult.wrapperGenerationComponents.setterToWrapperInfo) { + val extra = builderResult.wrapperGenerationComponents.setterToWrapperInfo.getValue(accessor) + val cCallAnnotation = accessor.annotations.firstIsInstanceOrNull() + ?: error("external setter for ${extra.global.name} wasn't marked with @CCall") + val wrapper = wrapperGenerator.generateCGlobalSetter(extra.global, cCallAnnotation.symbolName) + simpleBridgeGenerator.insertNativeBridge(accessor, emptyList(), wrapper.lines) + } + } } } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBuilder.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBuilder.kt index 20276e384e5..0298beb12b7 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBuilder.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBuilder.kt @@ -8,25 +8,22 @@ import org.jetbrains.kotlin.native.interop.gen.jvm.InteropConfiguration import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform import org.jetbrains.kotlin.native.interop.indexer.* +/** + * Components that are not passed via StubIr but required for bridge generation. + */ +class BridgeGenerationInfo(val cGlobalName: String, val typeInfo: TypeInfo) + /** * Additional components that are required to generate bridges. + * TODO: Metadata-based interop should not depend on these components. */ interface BridgeGenerationComponents { - class GlobalSetterBridgeInfo( - val cGlobalName: String, - val typeInfo: TypeInfo - ) + val setterToBridgeInfo: Map - class GlobalGetterBridgeInfo( - val cGlobalName: String, - val typeInfo: TypeInfo, - val isArray: Boolean - ) + val getterToBridgeInfo: Map - val setterToBridgeInfo: Map - - val getterToBridgeInfo: Map + val arrayGetterInfo: Map val enumToTypeMirror: Map @@ -35,13 +32,15 @@ interface BridgeGenerationComponents { val cStringParameters: Set } -class BridgeGenerationComponentsBuilder( - val getterToBridgeInfo: MutableMap = mutableMapOf(), - val setterToBridgeInfo: MutableMap = mutableMapOf(), - val enumToTypeMirror: MutableMap = mutableMapOf(), - val wCStringParameters: MutableSet = mutableSetOf(), - val cStringParameters: MutableSet = mutableSetOf() -) { +class BridgeGenerationComponentsBuilder { + + val getterToBridgeInfo = mutableMapOf() + val setterToBridgeInfo = mutableMapOf() + val arrayGetterBridgeInfo = mutableMapOf() + val enumToTypeMirror = mutableMapOf() + val wCStringParameters = mutableSetOf() + val cStringParameters = mutableSetOf() + fun build(): BridgeGenerationComponents = object : BridgeGenerationComponents { override val getterToBridgeInfo = this@BridgeGenerationComponentsBuilder.getterToBridgeInfo.toMap() @@ -57,6 +56,31 @@ class BridgeGenerationComponentsBuilder( override val cStringParameters: Set = this@BridgeGenerationComponentsBuilder.cStringParameters.toSet() + + override val arrayGetterInfo: Map = + this@BridgeGenerationComponentsBuilder.arrayGetterBridgeInfo.toMap() + } +} + +/** + * Components that are not passed via StubIr but required for generation of wrappers. + */ +class WrapperGenerationInfo(val global: GlobalDecl) + +interface WrapperGenerationComponents { + val getterToWrapperInfo: Map + val setterToWrapperInfo: Map +} + +class WrapperGenerationComponentsBuilder { + + val getterToWrapperInfo = mutableMapOf() + val setterToWrapperInfo = mutableMapOf() + + fun build(): WrapperGenerationComponents = object : WrapperGenerationComponents { + override val getterToWrapperInfo = this@WrapperGenerationComponentsBuilder.getterToWrapperInfo.toMap() + + override val setterToWrapperInfo = this@WrapperGenerationComponentsBuilder.setterToWrapperInfo.toMap() } } @@ -86,6 +110,8 @@ interface StubsBuildingContext { val bridgeComponentsBuilder: BridgeGenerationComponentsBuilder + val wrapperComponentsBuilder: WrapperGenerationComponentsBuilder + fun getKotlinClassFor(objCClassOrProtocol: ObjCClassOrProtocol, isMeta: Boolean = false): Classifier fun getKotlinClassForPointed(structDecl: StructDecl): Classifier @@ -210,6 +236,8 @@ class StubsBuildingContextImpl( override val bridgeComponentsBuilder = BridgeGenerationComponentsBuilder() + override val wrapperComponentsBuilder = WrapperGenerationComponentsBuilder() + override fun getKotlinClassFor(objCClassOrProtocol: ObjCClassOrProtocol, isMeta: Boolean): Classifier { return declarationMapper.getKotlinClassFor(objCClassOrProtocol, isMeta) } @@ -223,7 +251,8 @@ class StubsBuildingContextImpl( data class StubIrBuilderResult( val stubs: SimpleStubContainer, val declarationMapper: DeclarationMapper, - val bridgeGenerationComponents: BridgeGenerationComponents + val bridgeGenerationComponents: BridgeGenerationComponents, + val wrapperGenerationComponents: WrapperGenerationComponents ) /** @@ -285,7 +314,8 @@ class StubIrBuilder(private val context: StubIrContext) { return StubIrBuilderResult( stubs, buildingContext.declarationMapper, - buildingContext.bridgeComponentsBuilder.build() + buildingContext.bridgeComponentsBuilder.build(), + buildingContext.wrapperComponentsBuilder.build() ) } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrElementBuilders.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrElementBuilders.kt index 2230698a8b0..62cd39d9b3f 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrElementBuilders.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrElementBuilders.kt @@ -110,9 +110,8 @@ internal class StructStubBuilder( val signed = field.type.isIntegerTypeSigned() val readBits = PropertyAccessor.Getter.ReadBits(field.offset, field.size, signed) val writeBits = PropertyAccessor.Setter.WriteBits(field.offset, field.size) - // TODO: Use something instead of [GlobalGetterBridgeInfo]. - context.bridgeComponentsBuilder.getterToBridgeInfo[readBits] = BridgeGenerationComponents.GlobalGetterBridgeInfo("", typeInfo, false) - context.bridgeComponentsBuilder.setterToBridgeInfo[writeBits] = BridgeGenerationComponents.GlobalSetterBridgeInfo("", typeInfo) + context.bridgeComponentsBuilder.getterToBridgeInfo[readBits] = BridgeGenerationInfo("", typeInfo) + context.bridgeComponentsBuilder.setterToBridgeInfo[writeBits] = BridgeGenerationInfo("", typeInfo) val kind = PropertyStub.Kind.Var(readBits, writeBits) PropertyStub(field.name, WrapperStubType(kotlinType), kind) } @@ -357,7 +356,7 @@ internal class FunctionStubBuilder( val type = WrapperStubType(KotlinTypes.any.makeNullable()) parameters += FunctionParameterStub("variadicArguments", type, isVararg = true) } - annotations = listOf(AnnotationStub.CCall.Symbol(context.generateNextUniqueId("knifunptr_") + "_${func.name}")) + annotations = listOf(AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${func.name}")) mustBeExternal = true } val functionStub = FunctionStub( @@ -443,22 +442,44 @@ internal class GlobalStubBuilder( if (unwrappedType is ArrayType) { kotlinType = (mirror as TypeMirror.ByValue).valueType val getter = PropertyAccessor.Getter.SimpleGetter() - val extra = BridgeGenerationComponents.GlobalGetterBridgeInfo(global.name, mirror.info, isArray = true) - context.bridgeComponentsBuilder.getterToBridgeInfo[getter] = extra + val extra = BridgeGenerationInfo(global.name, mirror.info) + context.bridgeComponentsBuilder.arrayGetterBridgeInfo[getter] = extra kind = PropertyStub.Kind.Val(getter) } else { when (mirror) { is TypeMirror.ByValue -> { kotlinType = mirror.argType - val getter = PropertyAccessor.Getter.SimpleGetter() - val getterExtra = BridgeGenerationComponents.GlobalGetterBridgeInfo(global.name, mirror.info, isArray = false) - context.bridgeComponentsBuilder.getterToBridgeInfo[getter] = getterExtra + val getter = when (context.platform) { + KotlinPlatform.JVM -> { + PropertyAccessor.Getter.SimpleGetter().also { + val getterExtra = BridgeGenerationInfo(global.name, mirror.info) + context.bridgeComponentsBuilder.getterToBridgeInfo[it] = getterExtra + } + } + KotlinPlatform.NATIVE -> { + val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.name}_getter") + PropertyAccessor.Getter.ExternalGetter(listOf(cCallAnnotation)).also { + context.wrapperComponentsBuilder.getterToWrapperInfo[it] = WrapperGenerationInfo(global) + } + } + } kind = if (global.isConst) { PropertyStub.Kind.Val(getter) } else { - val setter = PropertyAccessor.Setter.SimpleSetter() - val setterExtra = BridgeGenerationComponents.GlobalSetterBridgeInfo(global.name, mirror.info) - context.bridgeComponentsBuilder.setterToBridgeInfo[setter] = setterExtra + val setter = when (context.platform) { + KotlinPlatform.JVM -> { + PropertyAccessor.Setter.SimpleSetter().also { + val setterExtra = BridgeGenerationInfo(global.name, mirror.info) + context.bridgeComponentsBuilder.setterToBridgeInfo[it] = setterExtra + } + } + KotlinPlatform.NATIVE -> { + val cCallAnnotation = AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${global.name}_setter") + PropertyAccessor.Setter.ExternalSetter(listOf(cCallAnnotation)).also { + context.wrapperComponentsBuilder.setterToWrapperInfo[it] = WrapperGenerationInfo(global) + } + } + } PropertyStub.Kind.Var(getter, setter) } }