Implement @ExportObjCClass

This commit is contained in:
Svyatoslav Scherbina
2017-08-29 15:01:48 +03:00
committed by SvyatoslavScherbina
parent aece25344f
commit 324f54ba1e
6 changed files with 110 additions and 21 deletions
@@ -45,3 +45,14 @@ annotation class ObjCAction
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
annotation class ObjCOutlet
/**
* Makes Kotlin subclass of Objective-C class visible for runtime lookup
* after Kotlin `main` function gets invoked.
*
* Note: runtime lookup can be forced even when the class is referenced statically from
* Objective-C source code by adding `__attribute__((objc_runtime_visible))` to its `@interface`.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class ExportObjCClass(val name: String = "")
@@ -43,9 +43,9 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val nullableInteropValueTypes = listOf(ValueType.C_POINTER, ValueType.NATIVE_POINTED)
private val nativePointed = packageScope.getContributedClassifier(nativePointedName) as ClassDescriptor
private val nativePointed = packageScope.getContributedClass(nativePointedName)
val cPointer = this.packageScope.getContributedClassifier(cPointerName) as ClassDescriptor
val cPointer = this.packageScope.getContributedClass(cPointerName)
val cPointerRawValue = cPointer.unsubstitutedMemberScope.getContributedVariables("rawValue").single()
@@ -70,7 +70,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val typeOf = packageScope.getContributedFunctions("typeOf").single()
val nativeMemUtils = packageScope.getContributedClassifier("nativeMemUtils") as ClassDescriptor
val nativeMemUtils = packageScope.getContributedClass("nativeMemUtils")
private val primitives = listOf(
builtIns.byte, builtIns.short, builtIns.int, builtIns.long,
@@ -94,8 +94,8 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val workerPackageScope = builtIns.builtInsModule.getPackage(FqName("konan.worker")).memberScope
val scheduleFunction = (workerPackageScope.getContributedClassifier("Worker") as ClassDescriptor).
unsubstitutedMemberScope.getContributedFunctions("schedule").single()
val scheduleFunction = workerPackageScope.getContributedClass("Worker")
.unsubstitutedMemberScope.getContributedFunctions("schedule").single()
val scheduleImplFunction = workerPackageScope.getContributedFunctions("scheduleImpl").single()
@@ -128,8 +128,8 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
packageScope.getContributedFunctions(name).single()
}.toMap()
val objCObject = packageScope.getContributedClassifier("ObjCObject") as ClassDescriptor
val objCPointerHolder = packageScope.getContributedClassifier("ObjCPointerHolder") as ClassDescriptor
val objCObject = packageScope.getContributedClass("ObjCObject")
val objCPointerHolder = packageScope.getContributedClass("ObjCPointerHolder")
val objCPointerHolderValue = objCPointerHolder.unsubstitutedMemberScope
.getContributedDescriptors().filterIsInstance<PropertyDescriptor>().single()
@@ -157,19 +157,21 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val objCObjectSuperInitCheck = packageScope.getContributedFunctions("superInitCheck").single()
val objCObjectInitBy = packageScope.getContributedFunctions("initBy").single()
val objCAction = packageScope.getContributedClassifier("ObjCAction") as ClassDescriptor
val objCAction = packageScope.getContributedClass("ObjCAction")
val objCOutlet = packageScope.getContributedClassifier("ObjCOutlet") as ClassDescriptor
val objCOutlet = packageScope.getContributedClass("ObjCOutlet")
val objCMethodImp = packageScope.getContributedClassifier("ObjCMethodImp") as ClassDescriptor
val objCMethodImp = packageScope.getContributedClass("ObjCMethodImp")
val exportObjCClass = packageScope.getContributedClass("ExportObjCClass")
}
private fun MemberScope.getContributedVariables(name: String) =
this.getContributedVariables(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
private fun MemberScope.getContributedClassifier(name: String) =
this.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
private fun MemberScope.getContributedClass(name: String): ClassDescriptor =
this.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BUILTINS) as ClassDescriptor
private fun MemberScope.getContributedFunctions(name: String) =
this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
@@ -310,9 +311,11 @@ internal val FunctionDescriptor.needsInlining: Boolean
internal val FunctionDescriptor.needsSerializedIr: Boolean
get() = (this.needsInlining && this.isExported())
fun AnnotationDescriptor.getStringValue(name: String): String {
val constantValue = this.allValueArguments.entries.single {
fun AnnotationDescriptor.getStringValueOrNull(name: String): String? {
val constantValue = this.allValueArguments.entries.atMostOne {
it.key.asString() == name
}.value
return constantValue.value as String
}?.value
return constantValue?.value as String?
}
fun AnnotationDescriptor.getStringValue(name: String): String = this.getStringValueOrNull(name)!!
@@ -21,6 +21,7 @@ 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.descriptors.getStringValueOrNull
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
@@ -49,11 +50,7 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
val bodySize =
LLVMStoreSizeOfType(llvmTargetData, context.llvmDeclarations.forClass(descriptor).bodyType).toInt()
val className = if (descriptor.isExported()) {
staticData.cStringLiteral(descriptor.fqNameSafe.asString())
} else {
NullPointer(int8Type) // Generate as anonymous.
}
val className = selectClassName(descriptor)?.let { staticData.cStringLiteral(it) } ?: NullPointer(int8Type)
val info = Struct(runtime.kotlinObjCClassInfo,
className,
@@ -83,6 +80,19 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
objCLLvmDeclarations.bodyOffsetGlobal.setInitializer(Int32(0))
}
private fun selectClassName(descriptor: ClassDescriptor): String? {
val exportObjCClassAnnotation =
descriptor.annotations.findAnnotation(context.interopBuiltIns.exportObjCClass.fqNameSafe)
return if (exportObjCClassAnnotation != null) {
exportObjCClassAnnotation.getStringValueOrNull("name") ?: descriptor.name.asString()
} else if (descriptor.isExported()) {
descriptor.fqNameSafe.asString()
} else {
null // Generate as anonymous.
}
}
private fun createMethodDesc(selector: String, encoding: String, imp: ConstPointer) = Struct(
runtime.objCMethodDescription,
staticData.cStringLiteral(selector),
@@ -61,9 +61,14 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
lateinit var currentFile: IrFile
private val topLevelInitializers = mutableListOf<IrExpression>()
override fun lower(irFile: IrFile) {
currentFile = irFile
irFile.transformChildrenVoid(this)
topLevelInitializers.forEach { irFile.addTopLevelInitializer(it) }
topLevelInitializers.clear()
}
private fun IrBuilderWithScope.callAlloc(classSymbol: IrClassSymbol): IrExpression {
@@ -119,6 +124,11 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
else -> null
}
}.let { irClass.declarations.addAll(it) }
if (irClass.descriptor.annotations.hasAnnotation(interop.exportObjCClass.fqNameSafe)) {
val irBuilder = context.createIrBuilder(currentFile.symbol).at(irClass)
topLevelInitializers.add(irBuilder.getObjCClass(irClass.symbol))
}
}
private fun generateActionImp(function: IrSimpleFunction): IrSimpleFunction {
@@ -16,10 +16,63 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.KotlinType
//TODO: delete file on next kotlin dependency update
internal fun IrExpression.isNullConst() = this is IrConst<*> && this.kind == IrConstKind.Null
private var topLevelInitializersCounter = 0
internal fun IrFile.addTopLevelInitializer(expression: IrExpression) {
val fieldDescriptor = PropertyDescriptorImpl.create(
this.packageFragmentDescriptor,
Annotations.EMPTY,
Modality.FINAL,
Visibilities.PRIVATE,
false,
"topLevelInitializer${topLevelInitializersCounter++}".synthesizedName,
CallableMemberDescriptor.Kind.DECLARATION,
SourceElement.NO_SOURCE,
false,
false,
false,
false,
false,
false
)
val builtIns = fieldDescriptor.builtIns
fieldDescriptor.setType(builtIns.unitType, emptyList(), null, null as KotlinType?)
fieldDescriptor.initialize(null, null)
val irField = IrFieldImpl(
expression.startOffset, expression.endOffset,
IrDeclarationOrigin.DEFINED, fieldDescriptor
)
val initializer = IrTypeOperatorCallImpl(
expression.startOffset, expression.endOffset, builtIns.unitType,
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, builtIns.unitType, expression
)
irField.initializer = IrExpressionBodyImpl(expression.startOffset, expression.endOffset, initializer)
this.declarations.add(irField)
}