Implement @ExportObjCClass
This commit is contained in:
committed by
SvyatoslavScherbina
parent
aece25344f
commit
324f54ba1e
@@ -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 = "")
|
||||
|
||||
+14
-12
@@ -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)
|
||||
+7
-4
@@ -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)!!
|
||||
|
||||
+15
-5
@@ -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),
|
||||
|
||||
+10
@@ -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 {
|
||||
|
||||
+53
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user