From 5a7e3e20b4b8ce65715b11c7f55893267dbeb88e Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 28 Aug 2017 12:46:00 +0300 Subject: [PATCH] Support exposing Kotlin methods as Objective-C actions and outlets --- .../kotlin/kotlinx/cinterop/ObjectiveCImpl.kt | 10 +- .../kotlinx/cinterop/ObjectiveCUtils.kt | 18 +- .../kotlin/backend/konan/InteropUtils.kt | 9 + .../kotlin/backend/konan/ObjCInterop.kt | 21 ++ .../jetbrains/kotlin/backend/konan/ir/Ir.kt | 6 + .../kotlin/backend/konan/llvm/ContextUtils.kt | 4 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 2 +- .../llvm/KotlinObjCClassInfoGenerator.kt | 39 ++- .../backend/konan/lower/InteropLowering.kt | 225 +++++++++++++++++- 9 files changed, 312 insertions(+), 22 deletions(-) diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt index af4e8fa344e..187926d5af0 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt @@ -62,7 +62,11 @@ inline fun interpretObjCPointerOrNull(rawPtr: NativePtr): T? = null } -inline fun interpretObjCPointer(rawPtr: NativePtr): T? = interpretObjCPointerOrNull(rawPtr)!! +inline fun interpretObjCPointer(rawPtr: NativePtr): T = if (rawPtr != nativeNullPtr) { + ObjCPointerHolder(rawPtr).uncheckedCast() +} else { + throw NullPointerException() +} inline val ObjCObject.rawPtr: NativePtr get() = (this.uncheckedCast()).rawPtr inline val ObjCObject?.rawPtr: NativePtr get() = if (this != null) { @@ -105,6 +109,10 @@ annotation class ObjCConstructor(val initSelector: String) @Retention(AnnotationRetention.BINARY) annotation class InteropStubs() +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +private annotation class ObjCMethodImp(val selector: String, val encoding: String) + @konan.internal.ExportForCompiler private fun allocObjCObject(clazz: NativePtr): T { val rawResult = objc_allocWithZone(clazz) diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCUtils.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCUtils.kt index b6a027d2f89..9ff90565ae9 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCUtils.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCUtils.kt @@ -28,4 +28,20 @@ inline fun autoreleasepool(block: () -> R): R { // TODO: null checks var ObjCObjectVar.value: T get() = interpretObjCPointerOrNull(nativeMemUtils.getNativePtr(this)).uncheckedCast() - set(value) = nativeMemUtils.putNativePtr(this, value.rawPtr) \ No newline at end of file + set(value) = nativeMemUtils.putNativePtr(this, value.rawPtr) + +/** + * Makes Kotlin method in Objective-C class accessible through Objective-C dispatch + * to be used as action sent by control in UIKit or AppKit. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +annotation class ObjCAction + +/** + * Makes Kotlin property in Objective-C class settable through Objective-C dispatch + * to be used as IB outlet. + */ +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.SOURCE) +annotation class ObjCOutlet diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt index 867e6a44d6c..42970fe6a15 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt @@ -152,6 +152,15 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) { val getObjCMessenger = packageScope.getContributedFunctions("getMessenger").single() val getObjCMessengerLU = packageScope.getContributedFunctions("getMessengerLU").single() + val interpretObjCPointerOrNull = packageScope.getContributedFunctions("interpretObjCPointerOrNull").single() + val interpretObjCPointer = packageScope.getContributedFunctions("interpretObjCPointer").single() + + val objCAction = packageScope.getContributedClassifier("ObjCAction") as ClassDescriptor + + val objCOutlet = packageScope.getContributedClassifier("ObjCOutlet") as ClassDescriptor + + val objCMethodImp = packageScope.getContributedClassifier("ObjCMethodImp") as ClassDescriptor + } private fun MemberScope.getContributedVariables(name: String) = diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt index 64274a1c156..8ab5a800dfc 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt @@ -22,6 +22,8 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition import org.jetbrains.kotlin.resolve.descriptorUtil.* +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils private val interopPackageName = InteropBuiltIns.FqNames.packageName private val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject")) @@ -34,6 +36,12 @@ fun ClassDescriptor.isObjCClass(): Boolean = this.getAllSuperClassifiers().any { it.fqNameSafe == objCObjectFqName } && this.containingDeclaration.fqNameSafe != interopPackageName +fun KotlinType.isObjCObjectType(): Boolean = + TypeUtils.getClassDescriptor(this) + ?.getAllSuperClassifiers() + ?.any { it.fqNameSafe == objCObjectFqName } + ?: false + fun ClassDescriptor.isExternalObjCClass(): Boolean = this.isObjCClass() && this.parentsWithSelf.filterIsInstance().any { it.annotations.findAnnotation(externalObjCClassFqName) != null @@ -153,4 +161,17 @@ class ObjCOverridabilityCondition : ExternalOverridabilityCondition { return true } +} + +fun inferObjCSelector(descriptor: FunctionDescriptor): String = if (descriptor.valueParameters.isEmpty()) { + descriptor.name.asString() +} else { + buildString { + append(descriptor.name) + append(':') + descriptor.valueParameters.drop(1).forEach { + append(it.name) + append(':') + } + } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index 360a8c39b9c..f5e4eb3c0b5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -65,6 +65,12 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym symbolTable.referenceSimpleFunction(function) } + val interopInterpretObjCPointer = + symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretObjCPointer) + + val interopInterpretObjCPointerOrNull = + symbolTable.referenceSimpleFunction(context.interopBuiltIns.interpretObjCPointerOrNull) + val getNativeNullPtr = symbolTable.referenceSimpleFunction(context.builtIns.getNativeNullPtr) val boxFunctions = ValueType.values().associate { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 9252103c16c..6e5408066ef 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -176,10 +176,10 @@ internal interface ContextUtils : RuntimeAware { /** * Address of entry point of [llvmFunction]. */ - val FunctionDescriptor.entryPointAddress: ConstValue + val FunctionDescriptor.entryPointAddress: ConstPointer get() { val result = LLVMConstBitCast(this.llvmFunction, int8TypePtr)!! - return constValue(result) + return constPointer(result) } val ClassDescriptor.typeInfoPtr: ConstPointer diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index e4c377439de..772abfa1e6b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -176,7 +176,7 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid { generator.generate(descriptor) if (descriptor.isKotlinObjCClass()) { - kotlinObjCClassInfoGenerator.generate(descriptor) + kotlinObjCClassInfoGenerator.generate(declaration) } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt index d2bde7ba82b..d942041bc6b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt @@ -20,24 +20,28 @@ import llvm.LLVMStoreSizeOfType import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.ObjCMethodInfo import org.jetbrains.kotlin.backend.konan.descriptors.contributedMethods +import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.isFinalClass +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces internal class KotlinObjCClassInfoGenerator(override val context: Context) : ContextUtils { - fun generate(descriptor: ClassDescriptor) { + fun generate(irClass: IrClass) { + val descriptor = irClass.descriptor assert(descriptor.isFinalClass) val objCLLvmDeclarations = context.llvmDeclarations.forClass(descriptor).objCDeclarations!! - val instanceMethods = descriptor.generateMethodDescs() + val instanceMethods = descriptor.generateOverridingMethodDescs() + irClass.generateImpMethodDescs() val companionObjectDescriptor = descriptor.companionObjectDescriptor - val classMethods = companionObjectDescriptor?.generateMethodDescs() ?: emptyList() + val classMethods = companionObjectDescriptor?.generateOverridingMethodDescs() ?: emptyList() val superclassName = descriptor.getSuperClassNotAny()!!.name.asString() val protocolNames = descriptor.getSuperInterfaces().map { it.name.asString().removeSuffix("Protocol") } @@ -79,16 +83,37 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con objCLLvmDeclarations.bodyOffsetGlobal.setInitializer(Int32(0)) } + private fun createMethodDesc(selector: String, encoding: String, imp: ConstPointer) = Struct( + runtime.objCMethodDescription, + staticData.cStringLiteral(selector), + staticData.cStringLiteral(encoding), + imp + ) + private fun generateMethodDesc(info: ObjCMethodInfo): ConstValue { - return Struct(runtime.objCMethodDescription, - staticData.cStringLiteral(info.selector), - staticData.cStringLiteral(info.encoding), + return createMethodDesc( + info.selector, + info.encoding, constPointer(context.llvm.externalFunction(info.imp, functionType(voidType))).bitcast(int8TypePtr) ) } - private fun ClassDescriptor.generateMethodDescs(): List = + private fun ClassDescriptor.generateOverridingMethodDescs(): List = this.unsubstitutedMemberScope.contributedMethods.filter { it.kind.isReal && it !is ConstructorDescriptor }.mapNotNull { it.getObjCMethodInfo() }.map { generateMethodDesc(it) } + + private fun IrClass.generateImpMethodDescs(): List = this.declarations + .filterIsInstance() + .mapNotNull { + val annotation = + it.descriptor.annotations.findAnnotation(context.interopBuiltIns.objCMethodImp.fqNameSafe) ?: + return@mapNotNull null + + createMethodDesc( + annotation.getStringValue("selector"), + annotation.getStringValue("encoding"), + it.descriptor.entryPointAddress + ) + } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt index 0f473de0b57..1e6c8d762d7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt @@ -18,36 +18,41 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.descriptors.allParameters -import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer -import org.jetbrains.kotlin.backend.common.lower.at -import org.jetbrains.kotlin.backend.common.lower.irBlock +import org.jetbrains.kotlin.backend.common.lower.* 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.descriptors.getStringValue import org.jetbrains.kotlin.backend.konan.descriptors.isInterface +import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl 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.util.defaultType -import org.jetbrains.kotlin.ir.util.functions -import org.jetbrains.kotlin.ir.util.getArguments +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.OverridingUtil +import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf +import org.jetbrains.kotlin.types.typeUtil.isUnit internal class InteropLoweringPart1(val context: Context) : IrBuildingTransformer(context), FileLoweringPass { @@ -87,7 +92,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme override fun visitClass(declaration: IrClass): IrStatement { if (declaration.descriptor.isKotlinObjCClass()) { - checkKotlinObjCClass(declaration) + lowerKotlinObjCClass(declaration) } outerClasses.push(declaration) @@ -98,6 +103,204 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme } } + private fun lowerKotlinObjCClass(irClass: IrClass) { + checkKotlinObjCClass(irClass) + + val interop = context.interopBuiltIns + + irClass.declarations.mapNotNull { + when { + it is IrSimpleFunction && it.descriptor.annotations.hasAnnotation(interop.objCAction) -> + generateActionImp(it) + + it is IrProperty && it.descriptor.annotations.hasAnnotation(interop.objCOutlet) -> + generateOutletSetterImp(it) + + else -> null + } + }.let { irClass.declarations.addAll(it) } + } + + private fun generateActionImp(function: IrSimpleFunction): IrSimpleFunction { + val action = "@${context.interopBuiltIns.objCAction.name}" + + function.extensionReceiverParameter?.let { + context.reportCompilationError("$action method must not have extension receiver", + currentFile, it) + } + + function.valueParameters.forEach { + val kotlinType = it.descriptor.type + if (!kotlinType.isObjCObjectType()) { + context.reportCompilationError("Unexpected $action method parameter type: $kotlinType\n" + + "Only Objective-C object types are supported here", + currentFile, it) + } + } + + val returnType = function.descriptor.returnType!! + + if (!returnType.isUnit()) { + context.reportCompilationError("Unexpected $action method return type: $returnType\n" + + "Only 'Unit' is supported here", + currentFile, function + ) + } + + return generateFunctionImp(inferObjCSelector(function.descriptor), function) + } + + private fun generateOutletSetterImp(property: IrProperty): IrSimpleFunction { + val descriptor = property.descriptor + + val outlet = "@${context.interopBuiltIns.objCOutlet.name}" + + if (!descriptor.isVar) { + context.reportCompilationError("$outlet property must be var", + currentFile, property) + } + + property.getter?.extensionReceiverParameter?.let { + context.reportCompilationError("$outlet must not have extension receiver", + currentFile, it) + } + + val type = descriptor.type + if (!type.isObjCObjectType()) { + context.reportCompilationError("Unexpected $outlet type: $type\n" + + "Only Objective-C object types are supported here", + currentFile, property) + } + + val name = descriptor.name.asString() + val selector = "set${name.capitalize()}:" + + return generateFunctionImp(selector, property.setter!!) + } + + private fun getMethodSignatureEncoding(function: IrFunction): String { + assert(function.extensionReceiverParameter == null) + assert(function.valueParameters.all { it.type.isObjCObjectType() }) + assert(function.descriptor.returnType!!.isUnit()) + + // Note: these values are valid for x86_64 and arm64. + return when (function.valueParameters.size) { + 0 -> "v16@0:8" + 1 -> "v24@0:8@16" + 2 -> "v32@0:8@16@24" + else -> context.reportCompilationError("Only 0, 1 or 2 parameters are supported here", + currentFile, function + ) + } + } + + private fun generateFunctionImp(selector: String, function: IrFunction): IrSimpleFunction { + val signatureEncoding = getMethodSignatureEncoding(function) + + val returnType = function.descriptor.returnType!! + assert(returnType.isUnit()) + + val nativePtrType = context.builtIns.nativePtr.defaultType + + val parameterTypes = mutableListOf(nativePtrType) // id self + + parameterTypes.add(nativePtrType) // SEL _cmd + + function.valueParameters.mapTo(parameterTypes) { + when { + it.descriptor.type.isObjCObjectType() -> nativePtrType + else -> TODO() + } + } + + // Annotations to be detected in KotlinObjCClassInfoGenerator: + val annotations = createObjCMethodImpAnnotations(selector = selector, encoding = signatureEncoding) + + val newDescriptor = SimpleFunctionDescriptorImpl.create( + function.descriptor.containingDeclaration, + annotations, + ("imp:" + selector).synthesizedName, + CallableMemberDescriptor.Kind.SYNTHESIZED, + SourceElement.NO_SOURCE + ) + + val valueParameters = parameterTypes.mapIndexed { index, it -> + ValueParameterDescriptorImpl( + newDescriptor, + null, + index, + Annotations.EMPTY, + Name.identifier("p$index"), + it, + false, + false, + false, + null, + SourceElement.NO_SOURCE + ) + } + + newDescriptor.initialize( + null, null, + emptyList(), + valueParameters, + returnType, + Modality.FINAL, + Visibilities.PRIVATE + ) + + val newFunction = IrFunctionImpl( + function.startOffset, function.endOffset, + IrDeclarationOrigin.DEFINED, + newDescriptor + ).apply { createParameterDeclarations() } + + val builder = context.createIrBuilder(newFunction.symbol) + newFunction.body = builder.irBlockBody(newFunction) { + +irCall(function.symbol).apply { + dispatchReceiver = interpretObjCPointer( + irGet(newFunction.valueParameters[0].symbol), + function.dispatchReceiverParameter!!.type + ) + + function.valueParameters.forEachIndexed { index, parameter -> + putValueArgument(index, + interpretObjCPointer( + irGet(newFunction.valueParameters[index + 2].symbol), + parameter.type + ) + ) + } + } + } + + return newFunction + } + + private fun IrBuilderWithScope.interpretObjCPointer(expression: IrExpression, type: KotlinType): IrExpression { + val callee: IrFunctionSymbol = if (TypeUtils.isNullableType(type)) { + symbols.interopInterpretObjCPointerOrNull + } else { + symbols.interopInterpretObjCPointer + } + + return irCall(callee, listOf(type)).apply { + putValueArgument(0, expression) + } + } + + private fun createObjCMethodImpAnnotations(selector: String, encoding: String): Annotations { + val annotation = AnnotationDescriptorImpl( + context.interopBuiltIns.objCMethodImp.defaultType, + mapOf("selector" to selector, "encoding" to encoding) + .mapKeys { Name.identifier(it.key) } + .mapValues { StringValue(it.value, context.builtIns) }, + SourceElement.NO_SOURCE + ) + + return AnnotationsImpl(listOf(annotation)) + } + private fun checkKotlinObjCClass(irClass: IrClass) { val kind = irClass.descriptor.kind if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) { @@ -597,4 +800,6 @@ private fun IrBuilder.irFloat(value: Float) = IrConstImpl.float(startOffset, endOffset, context.builtIns.floatType, value) private fun IrBuilder.irDouble(value: Double) = - IrConstImpl.double(startOffset, endOffset, context.builtIns.doubleType, value) \ No newline at end of file + IrConstImpl.double(startOffset, endOffset, context.builtIns.doubleType, value) + +private fun Annotations.hasAnnotation(descriptor: ClassDescriptor) = this.hasAnnotation(descriptor.fqNameSafe)