Unify interop with Objective-C in both directions
Also support casts to Objective-C types
This commit is contained in:
committed by
SvyatoslavScherbina
parent
38f896649c
commit
77139d58f8
@@ -22,44 +22,14 @@ interface ObjCObject
|
||||
interface ObjCClass : ObjCObject
|
||||
typealias ObjCObjectMeta = ObjCClass
|
||||
|
||||
abstract class ObjCObjectBase protected constructor() : ObjCObject {
|
||||
final override fun equals(other: Any?): Boolean {
|
||||
val thisAny: Any = this
|
||||
return thisAny.equals(other) // Call it virtually because ObjCObjectBase is a fake type.
|
||||
}
|
||||
final override fun hashCode(): Int = ObjCHashCode(this.rawPtr())
|
||||
final override fun toString(): String = ObjCToString(this.rawPtr())
|
||||
}
|
||||
@ExportTypeInfo("theForeignObjCObjectTypeInfo")
|
||||
internal open class ForeignObjCObject
|
||||
|
||||
abstract class ObjCObjectBase protected constructor() : ObjCObject
|
||||
abstract class ObjCObjectBaseMeta protected constructor() : ObjCObjectBase(), ObjCObjectMeta {}
|
||||
|
||||
fun optional(): Nothing = throw RuntimeException("Do not call me!!!")
|
||||
|
||||
/**
|
||||
* The runtime representation of any [ObjCObject].
|
||||
*/
|
||||
@ExportTypeInfo("theObjCPointerHolderTypeInfo")
|
||||
class ObjCPointerHolder(inline val rawPtr: NativePtr) {
|
||||
init {
|
||||
assert(rawPtr != nativeNullPtr)
|
||||
objc_retain(rawPtr)
|
||||
}
|
||||
|
||||
final override fun equals(other: Any?): Boolean =
|
||||
if (other is ObjCPointerHolder) {
|
||||
ObjCEquals(this.rawPtr, other.rawPtr)
|
||||
} else {
|
||||
other == this
|
||||
}
|
||||
|
||||
final override fun hashCode(): Int = ObjCHashCode(this.rawPtr)
|
||||
final override fun toString(): String = ObjCToString(this.rawPtr)
|
||||
}
|
||||
|
||||
@konan.internal.Intrinsic
|
||||
@konan.internal.ExportForCompiler
|
||||
private external fun ObjCObject.initFromPtr(ptr: NativePtr)
|
||||
|
||||
@konan.internal.Intrinsic
|
||||
external fun <T : ObjCObjectBase> T.initBy(constructorCall: T): T
|
||||
|
||||
@@ -72,19 +42,13 @@ private fun ObjCObjectBase.superInitCheck(superInitCallResult: ObjCObject?) {
|
||||
throw UnsupportedOperationException("Super initializer has replaced object")
|
||||
}
|
||||
|
||||
@Deprecated("Use plain Kotlin cast", ReplaceWith("this as T"), DeprecationLevel.WARNING)
|
||||
fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (this as T) // TODO: make private
|
||||
|
||||
inline fun <T : ObjCObject?> interpretObjCPointerOrNull(rawPtr: NativePtr): T? = if (rawPtr != nativeNullPtr) {
|
||||
ObjCPointerHolder(rawPtr).uncheckedCast<T>()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@SymbolName("Kotlin_Interop_refFromObjC")
|
||||
external fun <T : ObjCObject?> interpretObjCPointerOrNull(rawPtr: NativePtr): T?
|
||||
|
||||
inline fun <T : ObjCObject> interpretObjCPointer(rawPtr: NativePtr): T = if (rawPtr != nativeNullPtr) {
|
||||
ObjCPointerHolder(rawPtr).uncheckedCast<T>()
|
||||
} else {
|
||||
throw NullPointerException()
|
||||
}
|
||||
inline fun <T : ObjCObject> interpretObjCPointer(rawPtr: NativePtr): T = interpretObjCPointerOrNull<T>(rawPtr)!!
|
||||
|
||||
@SymbolName("Kotlin_Interop_refToObjC")
|
||||
external fun ObjCObject?.rawPtr(): NativePtr
|
||||
@@ -119,7 +83,7 @@ typealias ObjCBlockVar<T> = ObjCNotImplementedVar<T>
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ExternalObjCClass()
|
||||
annotation class ExternalObjCClass(val protocolGetter: String = "")
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@@ -155,18 +119,15 @@ private fun getObjCClassByName(name: NativePtr): NativePtr {
|
||||
}
|
||||
|
||||
@konan.internal.ExportForCompiler
|
||||
private fun <T : ObjCObject> allocObjCObject(clazz: NativePtr): T {
|
||||
private fun allocObjCObject(clazz: NativePtr): NativePtr {
|
||||
val rawResult = objc_allocWithZone(clazz)
|
||||
if (rawResult == nativeNullPtr) {
|
||||
throw OutOfMemoryError("Unable to allocate Objective-C object")
|
||||
}
|
||||
|
||||
val result = interpretObjCPointerOrNull<T>(rawResult)!!
|
||||
// `objc_allocWithZone` returns retained pointer. Balance it:
|
||||
objc_release(rawResult)
|
||||
// TODO: do not retain this pointer in `interpretObjCPointerOrNull` instead.
|
||||
// Note: `objc_allocWithZone` returns retained pointer, and thus it must be balanced by the caller.
|
||||
|
||||
return result
|
||||
return rawResult
|
||||
}
|
||||
|
||||
@konan.internal.Intrinsic
|
||||
|
||||
@@ -25,12 +25,14 @@ inline fun <R> autoreleasepool(block: () -> R): R {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: implement a checked cast instead.
|
||||
fun <T : ObjCObject> ObjCObject.reinterpret() = this.uncheckedCast<T>()
|
||||
@Deprecated("Use plain Kotlin cast", ReplaceWith("this as T"), DeprecationLevel.WARNING)
|
||||
fun <T : ObjCObject> ObjCObject.reinterpret() = @Suppress("DEPRECATION") this.uncheckedCast<T>()
|
||||
|
||||
// TODO: null checks
|
||||
var <T : ObjCObject?> ObjCObjectVar<T>.value: T
|
||||
get() = interpretObjCPointerOrNull<T>(nativeMemUtils.getNativePtr(this)).uncheckedCast<T>()
|
||||
@Suppress("DEPRECATION") get() =
|
||||
interpretObjCPointerOrNull<T>(nativeMemUtils.getNativePtr(this)).uncheckedCast<T>()
|
||||
|
||||
set(value) = nativeMemUtils.putNativePtr(this, value.rawPtr())
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,8 +92,8 @@ private tailrec fun convertArgument(
|
||||
|
||||
is CEnum -> convertArgument(argument.value, isVariadic, location, additionalPlacement)
|
||||
|
||||
is ObjCPointerHolder -> {
|
||||
location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer(argument.rawPtr)
|
||||
is ForeignObjCObject -> {
|
||||
location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer((argument as ObjCObject).rawPtr())
|
||||
FFI_TYPE_KIND_POINTER
|
||||
}
|
||||
|
||||
|
||||
+16
-1
@@ -470,8 +470,23 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
internal tailrec fun ObjCClass.isNSStringOrSubclass(): Boolean = when (this.name) {
|
||||
"NSMutableString", // fast path and handling for forward declarations.
|
||||
"NSString" -> true
|
||||
else -> {
|
||||
val baseClass = this.baseClass
|
||||
if (baseClass != null) {
|
||||
baseClass.isNSStringOrSubclass()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ObjCClass.isNSStringSubclass(): Boolean = this.baseClass?.isNSStringOrSubclass() == true
|
||||
|
||||
private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPointer): TypeMirror.ByValue {
|
||||
if (type is ObjCObjectPointer && type.def.name == "NSString") {
|
||||
if (type is ObjCObjectPointer && type.def.isNSStringOrSubclass()) {
|
||||
val info = TypeInfo.NSString(type)
|
||||
return objCMirror(KotlinTypes.string, info, type.isNullable)
|
||||
}
|
||||
|
||||
+52
-12
@@ -112,13 +112,13 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
||||
|
||||
if (method.nsConsumesSelf) {
|
||||
// TODO: do this later due to possible exceptions
|
||||
bodyGenerator.out("objc_retain($kniReceiverParameter.rawPtr())")
|
||||
bodyGenerator.out("objc_retain($kniReceiverParameter)")
|
||||
}
|
||||
|
||||
kotlinObjCBridgeParameters.add(kniReceiverParameter to KotlinTypes.objCObject.type)
|
||||
kotlinObjCBridgeParameters.add(kniReceiverParameter to KotlinTypes.nativePtr)
|
||||
nativeBridgeArguments.add(
|
||||
TypedKotlinValue(voidPtr,
|
||||
"getReceiverOrSuper($kniReceiverParameter.rawPtr(), $kniSuperClassParameter)"))
|
||||
"getReceiverOrSuper($kniReceiverParameter, $kniSuperClassParameter)"))
|
||||
|
||||
val kotlinParameterNames = method.getKotlinParameterNames()
|
||||
|
||||
@@ -289,11 +289,16 @@ private fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
|
||||
|
||||
abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||
private val container: ObjCClassOrProtocol,
|
||||
private val isMeta: Boolean) : KotlinStub {
|
||||
protected val metaContainerStub: ObjCContainerStub?
|
||||
) : KotlinStub {
|
||||
|
||||
private val isMeta: Boolean get() = metaContainerStub == null
|
||||
|
||||
private val methods: List<ObjCMethod>
|
||||
private val properties: List<ObjCProperty>
|
||||
|
||||
private val protocolGetter: String?
|
||||
|
||||
init {
|
||||
val superMethods = container.inheritedMethods(isMeta)
|
||||
|
||||
@@ -376,7 +381,26 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||
val supersString = supers.joinToString { it.render(stubGenerator.kotlinFile) }
|
||||
val classifier = stubGenerator.declarationMapper.getKotlinClassFor(container, isMeta)
|
||||
val name = stubGenerator.kotlinFile.declare(classifier)
|
||||
this.classHeader = "@ExternalObjCClass $keywords $name : $supersString"
|
||||
|
||||
val externalObjCClassAnnotationName = "@ExternalObjCClass"
|
||||
val externalObjCClassAnnotation: String
|
||||
|
||||
if (container is ObjCProtocol) {
|
||||
protocolGetter = if (metaContainerStub != null) {
|
||||
metaContainerStub.protocolGetter!!
|
||||
} else {
|
||||
val nativeBacked = object : NativeBacked {}
|
||||
// TODO: handle the case when protocol getter stub can't be compiled.
|
||||
genProtocolGetter(stubGenerator, nativeBacked, container)
|
||||
}
|
||||
|
||||
externalObjCClassAnnotation = externalObjCClassAnnotationName.applyToStrings(protocolGetter)
|
||||
} else {
|
||||
protocolGetter = null
|
||||
externalObjCClassAnnotation = externalObjCClassAnnotationName
|
||||
}
|
||||
|
||||
this.classHeader = "$externalObjCClassAnnotation $keywords $name : $supersString"
|
||||
}
|
||||
|
||||
open fun generateBody(context: StubGenerationContext): Sequence<String> {
|
||||
@@ -403,13 +427,10 @@ open class ObjCClassOrProtocolStub(
|
||||
) : ObjCContainerStub(
|
||||
stubGenerator,
|
||||
container,
|
||||
isMeta = false
|
||||
metaContainerStub = object : ObjCContainerStub(stubGenerator, container, metaContainerStub = null) {}
|
||||
) {
|
||||
private val metaClassStub =
|
||||
object : ObjCContainerStub(stubGenerator, container, isMeta = true) {}
|
||||
|
||||
override fun generate(context: StubGenerationContext) =
|
||||
metaClassStub.generate(context) + "" + super.generate(context)
|
||||
metaContainerStub!!.generate(context) + "" + super.generate(context)
|
||||
}
|
||||
|
||||
class ObjCProtocolStub(stubGenerator: StubGenerator, protocol: ObjCProtocol) :
|
||||
@@ -485,11 +506,11 @@ class ObjCPropertyStub(
|
||||
}
|
||||
val result = mutableListOf(
|
||||
"$modifiers$kind $receiver${property.name.asSimpleName()}: $kotlinType",
|
||||
" get() = ${getterStub.bridgeName}(nativeNullPtr, this)"
|
||||
" get() = ${getterStub.bridgeName}(nativeNullPtr, this.rawPtr())"
|
||||
)
|
||||
|
||||
property.setter?.let {
|
||||
result.add(" set(value) = ${setterStub!!.bridgeName}(nativeNullPtr, this, value)")
|
||||
result.add(" set(value) = ${setterStub!!.bridgeName}(nativeNullPtr, this.rawPtr(), value)")
|
||||
}
|
||||
|
||||
return result.asSequence()
|
||||
@@ -585,3 +606,22 @@ private fun NativeCodeBuilder.genMethodImp(
|
||||
return functionName
|
||||
}
|
||||
|
||||
private fun genProtocolGetter(
|
||||
stubGenerator: StubGenerator,
|
||||
nativeBacked: NativeBacked,
|
||||
protocol: ObjCProtocol
|
||||
): String {
|
||||
val functionName = "kniprot_" + stubGenerator.pkgName.replace('.', '_') + stubGenerator.nextUniqueId()
|
||||
|
||||
val builder = NativeCodeBuilder(stubGenerator.simpleBridgeGenerator.topLevelNativeScope)
|
||||
|
||||
with(builder) {
|
||||
out("Protocol* $functionName() {")
|
||||
out(" return @protocol(${protocol.name});")
|
||||
out("}")
|
||||
}
|
||||
|
||||
stubGenerator.simpleBridgeGenerator.insertNativeBridge(nativeBacked, emptyList(), builder.lines)
|
||||
|
||||
return functionName
|
||||
}
|
||||
|
||||
+3
-2
@@ -802,12 +802,12 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
nativeIndex.objCClasses.forEach {
|
||||
if (!it.isForwardDeclaration) {
|
||||
if (!it.isForwardDeclaration && !it.isNSStringSubclass()) {
|
||||
stubs.add(ObjCClassStub(this, it))
|
||||
}
|
||||
}
|
||||
|
||||
nativeIndex.objCCategories.mapTo(stubs) {
|
||||
nativeIndex.objCCategories.filter { !it.clazz.isNSStringSubclass() }.mapTo(stubs) {
|
||||
ObjCCategoryStub(this, it)
|
||||
}
|
||||
|
||||
@@ -886,6 +886,7 @@ class StubGenerator(
|
||||
add("MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED") // Workaround for multiple-inherited properties.
|
||||
add("EXTENSION_SHADOWED_BY_MEMBER") // For Objective-C categories represented as extensions.
|
||||
add("REDUNDANT_NULLABLE") // This warning appears due to Obj-C typedef nullability incomplete support.
|
||||
add("DEPRECATION") // For uncheckedCast.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-7
@@ -69,7 +69,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
val objCNamesProtocols = objCNames.child(Name.identifier("protocols"))
|
||||
}
|
||||
|
||||
private val packageScope = builtIns.builtInsModule.getPackage(FqNames.packageName).memberScope
|
||||
val packageScope = builtIns.builtInsModule.getPackage(FqNames.packageName).memberScope
|
||||
|
||||
val getPointerSize = packageScope.getContributedFunctions("getPointerSize").single()
|
||||
|
||||
@@ -161,12 +161,6 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
}.toMap()
|
||||
|
||||
val objCObject = packageScope.getContributedClass("ObjCObject")
|
||||
val objCPointerHolder = packageScope.getContributedClass("ObjCPointerHolder")
|
||||
|
||||
val objCPointerHolderValue = objCPointerHolder.unsubstitutedMemberScope
|
||||
.getContributedDescriptors().filterIsInstance<PropertyDescriptor>().single()
|
||||
|
||||
val objCObjectInitFromPtr = packageScope.getContributedFunctions("initFromPtr").single()
|
||||
|
||||
val allocObjCObject = packageScope.getContributedFunctions("allocObjCObject").single()
|
||||
|
||||
|
||||
+3
-5
@@ -25,11 +25,12 @@ import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
|
||||
private val interopPackageName = InteropBuiltIns.FqNames.packageName
|
||||
private val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject"))
|
||||
private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass"))
|
||||
private val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
|
||||
internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
|
||||
private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod"))
|
||||
private val objCBridgeFqName = interopPackageName.child(Name.identifier("ObjCBridge"))
|
||||
|
||||
@@ -38,10 +39,7 @@ fun ClassDescriptor.isObjCClass(): Boolean =
|
||||
this.containingDeclaration.fqNameSafe != interopPackageName
|
||||
|
||||
fun KotlinType.isObjCObjectType(): Boolean =
|
||||
TypeUtils.getClassDescriptor(this)
|
||||
?.getAllSuperClassifiers()
|
||||
?.any { it.fqNameSafe == objCObjectFqName }
|
||||
?: false
|
||||
(this.supertypes() + this).any { TypeUtils.getClassDescriptor(it)?.fqNameSafe == objCObjectFqName }
|
||||
|
||||
fun ClassDescriptor.isExternalObjCClass(): Boolean = this.isObjCClass() &&
|
||||
this.parentsWithSelf.filterIsInstance<ClassDescriptor>().any {
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ internal fun CommonBackendContext.reportCompilationError(message: String, irFile
|
||||
|
||||
internal fun CommonBackendContext.reportCompilationError(message: String) {
|
||||
report(null, null, message, true)
|
||||
throw KonanCompilationException()
|
||||
}
|
||||
|
||||
internal fun CommonBackendContext.reportCompilationWarning(message: String) {
|
||||
|
||||
+6
-9
@@ -108,10 +108,13 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
||||
|
||||
val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)
|
||||
|
||||
val interopGetObjCClass = symbolTable.referenceSimpleFunction(context.interopBuiltIns.getObjCClass)
|
||||
val interopObjCRelease = symbolTable.referenceSimpleFunction(
|
||||
context.interopBuiltIns.packageScope
|
||||
.getContributedFunctions(Name.identifier("objc_release"), NoLookupLocation.FROM_BACKEND)
|
||||
.single()
|
||||
)
|
||||
|
||||
val interopObjCObjectInitFromPtr =
|
||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitFromPtr)
|
||||
val interopGetObjCClass = symbolTable.referenceSimpleFunction(context.interopBuiltIns.getObjCClass)
|
||||
|
||||
val interopObjCObjectSuperInitCheck =
|
||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectSuperInitCheck)
|
||||
@@ -132,12 +135,6 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
||||
val interopCreateNSStringFromKString =
|
||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.CreateNSStringFromKString)
|
||||
|
||||
val objCPointerHolder = symbolTable.referenceClass(context.interopBuiltIns.objCPointerHolder)
|
||||
val objCPointerHolderValueGetter =
|
||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCPointerHolderValue.getter!!)
|
||||
|
||||
val allocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)
|
||||
|
||||
val objCExportTrapOnUndeclaredException =
|
||||
symbolTable.referenceSimpleFunction(context.builtIns.konanInternal.getContributedFunctions(
|
||||
Name.identifier("trapOnUndeclaredException"),
|
||||
|
||||
+1
@@ -47,6 +47,7 @@ internal fun IrFunction.isObjCClassMethod() = this.descriptor.isObjCClassMethod(
|
||||
internal fun IrFunction.canObjCClassMethodBeCalledVirtually(overridden: IrFunction) =
|
||||
this.descriptor.canObjCClassMethodBeCalledVirtually(overridden.descriptor)
|
||||
internal fun IrClass.isObjCClass() = this.descriptor.isObjCClass()
|
||||
internal fun IrClass.isObjCMetaClass() = this.descriptor.isObjCMetaClass()
|
||||
internal fun IrFunction.isExternalObjCClassMethod() = this.descriptor.isExternalObjCClassMethod()
|
||||
|
||||
internal val IrDeclaration.llvmSymbolOrigin get() = this.descriptor.llvmSymbolOrigin
|
||||
|
||||
+72
-7
@@ -24,6 +24,8 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.stdlibModule
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCDataGenerator
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
|
||||
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
@@ -85,18 +87,19 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
|
||||
fun typeInfoForAllocation(constructedClass: ClassDescriptor): LLVMValueRef {
|
||||
val descriptorForTypeInfo = if (constructedClass.isObjCClass()) {
|
||||
context.ir.symbols.objCPointerHolder.owner
|
||||
} else {
|
||||
constructedClass
|
||||
}
|
||||
return typeInfoValue(descriptorForTypeInfo)
|
||||
assert(!constructedClass.isObjCClass())
|
||||
return typeInfoValue(constructedClass)
|
||||
}
|
||||
|
||||
fun generateLocationInfo(locationInfo: LocationInfo): DILocationRef? {
|
||||
return LLVMCreateLocation(LLVMGetModuleContext(context.llvmModule), locationInfo.line, locationInfo.line, locationInfo.scope)
|
||||
}
|
||||
|
||||
val objCDataGenerator = when (context.config.target) {
|
||||
KonanTarget.IOS_ARM64, KonanTarget.IOS_X64, KonanTarget.MACOS_X64 -> ObjCDataGenerator(this)
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal sealed class ExceptionHandler {
|
||||
@@ -430,6 +433,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
return LLVMBlockAddress(function, bbLabel)!!
|
||||
}
|
||||
|
||||
fun not(arg: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildNot(builder, arg, name)!!
|
||||
fun and(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildAnd(builder, arg0, arg1, name)!!
|
||||
fun or(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildOr(builder, arg0, arg1, name)!!
|
||||
fun xor(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildXor(builder, arg0, arg1, name)!!
|
||||
@@ -613,7 +617,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
|
||||
val owner = descriptor.containingDeclaration as ClassDescriptor
|
||||
|
||||
val typeInfoPtr: LLVMValueRef = if (owner.isObjCClass()) {
|
||||
val typeInfoPtr: LLVMValueRef = if (descriptor.getObjCMethodInfo() != null) {
|
||||
call(context.llvm.getObjCKotlinTypeInfo, listOf(receiver))
|
||||
} else {
|
||||
val typeInfoOrMetaPtr = structGep(receiver, 0 /* typeInfoOrMeta_ */)
|
||||
@@ -655,6 +659,20 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
return codegen.theUnitInstanceRef.llvm
|
||||
}
|
||||
|
||||
if (descriptor.isCompanion) {
|
||||
val parent = descriptor.parent as IrClass
|
||||
if (parent.isObjCClass()) {
|
||||
// TODO: cache it too.
|
||||
|
||||
return call(
|
||||
codegen.llvmFunction(context.ir.symbols.interopInterpretObjCPointer.owner),
|
||||
listOf(getObjCClass(parent, exceptionHandler)),
|
||||
Lifetime.GLOBAL,
|
||||
exceptionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val objectPtr = codegen.getObjectInstanceStorage(descriptor, shared)
|
||||
val bbCurrent = currentBlock
|
||||
val bbInit= basicBlock("label_init", locationInfo)
|
||||
@@ -708,6 +726,53 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: get rid of exceptionHandler argument by ensuring that all called functions are non-throwing.
|
||||
fun getObjCClass(irClass: IrClass, exceptionHandler: ExceptionHandler): LLVMValueRef {
|
||||
assert(!irClass.isInterface)
|
||||
|
||||
return if (irClass.isExternalObjCClass()) {
|
||||
context.llvm.imports.add(irClass.llvmSymbolOrigin)
|
||||
|
||||
if (irClass.isObjCMetaClass()) {
|
||||
val name = irClass.name.asString().removeSuffix("Meta")
|
||||
|
||||
val objCClass = load(codegen.objCDataGenerator!!.genClassRef(name).llvm)
|
||||
|
||||
val getClass = context.llvm.externalFunction(
|
||||
"object_getClass",
|
||||
functionType(int8TypePtr, false, int8TypePtr),
|
||||
origin = context.standardLlvmSymbolsOrigin
|
||||
)
|
||||
|
||||
call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler)
|
||||
} else {
|
||||
load(codegen.objCDataGenerator!!.genClassRef(irClass.name.asString()).llvm)
|
||||
}
|
||||
} else {
|
||||
if (irClass.isObjCMetaClass()) {
|
||||
error("type-checking against Kotlin classes inheriting Objective-C meta-classes isn't supported yet")
|
||||
}
|
||||
|
||||
val objCDeclarations = context.llvmDeclarations.forClass(irClass).objCDeclarations!!
|
||||
val classPointerGlobal = objCDeclarations.classPointerGlobal.llvmGlobal
|
||||
|
||||
val storedClass = this.load(classPointerGlobal)
|
||||
|
||||
val storedClassIsNotNull = this.icmpNe(storedClass, kNullInt8Ptr)
|
||||
|
||||
return this.ifThenElse(storedClassIsNotNull, storedClass) {
|
||||
val newClass = call(
|
||||
context.llvm.createKotlinObjCClass,
|
||||
listOf(objCDeclarations.classInfoGlobal.llvmGlobal),
|
||||
exceptionHandler = exceptionHandler
|
||||
)
|
||||
|
||||
this.store(newClass, classPointerGlobal)
|
||||
newClass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resetDebugLocation() {
|
||||
if (!context.shouldContainDebugInfo()) return
|
||||
currentPositionHolder.resetBuilderDebugLocation()
|
||||
|
||||
+4
-9
@@ -417,8 +417,12 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
val getObjCKotlinTypeInfo by lazy { importRtFunction("GetObjCKotlinTypeInfo") }
|
||||
val missingInitImp by lazy { importRtFunction("MissingInitImp") }
|
||||
|
||||
val Kotlin_Interop_DoesObjectConformToProtocol by lazyRtFunction
|
||||
val Kotlin_Interop_IsObjectKindOfClass by lazyRtFunction
|
||||
|
||||
val Kotlin_ObjCExport_refToObjC by lazyRtFunction
|
||||
val Kotlin_ObjCExport_refFromObjC by lazyRtFunction
|
||||
val Kotlin_ObjCExport_CreateNSStringFromKString by lazyRtFunction
|
||||
val Kotlin_Interop_CreateNSArrayFromKList by lazyRtFunction
|
||||
val Kotlin_Interop_CreateNSMutableArrayFromKList by lazyRtFunction
|
||||
val Kotlin_Interop_CreateNSSetFromKSet by lazyRtFunction
|
||||
@@ -431,15 +435,6 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
val Kotlin_ObjCExport_RethrowExceptionAsNSError by lazyRtFunction
|
||||
val Kotlin_ObjCExport_RethrowNSErrorAsException by lazyRtFunction
|
||||
|
||||
val objCExportEnabled = if (context.config.produce.isNativeBinary) {
|
||||
// Note: this defines the global declared in runtime (if any).
|
||||
staticData.placeGlobal("objCExportEnabled", Int8(0), isExported = true).also {
|
||||
it.setConstant(true)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val tlsMode by lazy {
|
||||
when (target) {
|
||||
KonanTarget.WASM32,
|
||||
|
||||
+99
-42
@@ -340,7 +340,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
declaration.acceptChildrenVoid(this)
|
||||
|
||||
// Note: it is here because it also generates some bitcode.
|
||||
ObjCExport(context).produceObjCFramework()
|
||||
ObjCExport(codegen).produce()
|
||||
|
||||
codegen.objCDataGenerator?.finishModule()
|
||||
|
||||
appendLlvmUsed("llvm.used", context.llvm.usedFunctions + context.llvm.usedGlobals)
|
||||
appendLlvmUsed("llvm.compiler.used", context.llvm.compilerUsedGlobals)
|
||||
@@ -1247,8 +1249,24 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
assert(!KotlinBuiltIns.isPrimitiveType(dstDescriptor.defaultType) &&
|
||||
!KotlinBuiltIns.isPrimitiveType(value.argument.type))
|
||||
|
||||
val dstTypeInfo = codegen.typeInfoValue(dstDescriptor) // Get TypeInfo for dst type.
|
||||
val srcArg = evaluateExpression(value.argument) // Evaluate src expression.
|
||||
|
||||
if (dstDescriptor.defaultType.isObjCObjectType()) {
|
||||
with(functionGenerationContext) {
|
||||
ifThen(not(genInstanceOf(srcArg, dstDescriptor))) {
|
||||
callDirect(
|
||||
context.ir.symbols.ThrowTypeCastException.owner,
|
||||
emptyList(),
|
||||
Lifetime.GLOBAL
|
||||
)
|
||||
}
|
||||
}
|
||||
return srcArg
|
||||
}
|
||||
// Note: the code above would actually work for any classes.
|
||||
// However, the code generated below is shorter. Consider it to be a specialization.
|
||||
|
||||
val dstTypeInfo = codegen.typeInfoValue(dstDescriptor) // Get TypeInfo for dst type.
|
||||
val srcObjInfoPtr = functionGenerationContext.bitcast(codegen.kObjHeaderPtr, srcArg) // Cast src to ObjInfoPtr.
|
||||
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
|
||||
call(context.llvm.checkInstanceFunction, args) // Check if dst is subclass of src.
|
||||
@@ -1295,6 +1313,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun genInstanceOf(obj: LLVMValueRef, dstClass: IrClass): LLVMValueRef {
|
||||
if (dstClass.defaultType.isObjCObjectType()) {
|
||||
return genInstanceOfObjC(obj, dstClass)
|
||||
}
|
||||
|
||||
val dstTypeInfo = codegen.typeInfoValue(dstClass) // Get TypeInfo for dst type.
|
||||
val srcObjInfoPtr = functionGenerationContext.bitcast(codegen.kObjHeaderPtr, obj) // Cast src to ObjInfoPtr.
|
||||
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
|
||||
@@ -1303,11 +1325,57 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
return LLVMBuildTrunc(functionGenerationContext.builder, result, kInt1, "")!! // Truncate result to boolean
|
||||
}
|
||||
|
||||
private fun genInstanceOfObjC(obj: LLVMValueRef, dstClass: IrClass): LLVMValueRef {
|
||||
val objCObject = callDirect(
|
||||
context.ir.symbols.interopObjCObjectRawValueGetter.owner,
|
||||
listOf(obj),
|
||||
Lifetime.IRRELEVANT
|
||||
)
|
||||
|
||||
return if (dstClass.isObjCClass()) {
|
||||
if (dstClass.isInterface) {
|
||||
val isMeta = if (dstClass.isObjCMetaClass()) Int8(1) else Int8(0)
|
||||
call(
|
||||
context.llvm.Kotlin_Interop_DoesObjectConformToProtocol,
|
||||
listOf(
|
||||
objCObject,
|
||||
genGetObjCProtocol(dstClass),
|
||||
isMeta.llvm
|
||||
)
|
||||
)
|
||||
} else {
|
||||
call(
|
||||
context.llvm.Kotlin_Interop_IsObjectKindOfClass,
|
||||
listOf(objCObject, genGetObjCClass(dstClass))
|
||||
)
|
||||
}.let {
|
||||
functionGenerationContext.icmpNe(it, Int8(0).llvm)
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// e.g. ObjCObject, ObjCObjectBase etc.
|
||||
if (dstClass.isObjCMetaClass()) {
|
||||
val isClass = context.llvm.externalFunction(
|
||||
"object_isClass",
|
||||
functionType(int8Type, false, int8TypePtr),
|
||||
context.standardLlvmSymbolsOrigin
|
||||
)
|
||||
|
||||
call(isClass, listOf(objCObject)).let {
|
||||
functionGenerationContext.icmpNe(it, Int8(0).llvm)
|
||||
}
|
||||
} else {
|
||||
kTrue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateNotInstanceOf(value: IrTypeOperatorCall): LLVMValueRef {
|
||||
val instanceOfResult = evaluateInstanceOf(value)
|
||||
return LLVMBuildNot(functionGenerationContext.builder, instanceOfResult, "")!!
|
||||
return functionGenerationContext.not(instanceOfResult)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1399,7 +1467,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
return if (classDescriptor.isObjCClass()) {
|
||||
assert(classDescriptor.isKotlinObjCClass())
|
||||
|
||||
val objCPtr = callDirect(context.ir.symbols.objCPointerHolderValueGetter.owner,
|
||||
val objCPtr = callDirect(context.ir.symbols.interopObjCObjectRawValueGetter.owner,
|
||||
listOf(objectPtr), Lifetime.IRRELEVANT)
|
||||
|
||||
val objCDeclarations = context.llvmDeclarations.forClass(classDescriptor).objCDeclarations!!
|
||||
@@ -1992,9 +2060,19 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
assert(args.isEmpty())
|
||||
functionGenerationContext.allocArray(codegen.typeInfoValue(constructedClass), count = kImmZero,
|
||||
lifetime = resultLifetime(callee))
|
||||
} else if (constructedClass.isKotlinObjCClass()) {
|
||||
callDirect(context.ir.symbols.allocObjCObject.owner, listOf(genGetObjCClass(constructedClass)),
|
||||
resultLifetime(callee))
|
||||
} else if (constructedClass.isObjCClass()) {
|
||||
assert(constructedClass.isKotlinObjCClass()) // Calls to other ObjC class constructors must be lowered.
|
||||
val symbols = context.ir.symbols
|
||||
val rawPtr = callDirect(
|
||||
symbols.interopAllocObjCObject.owner,
|
||||
listOf(genGetObjCClass(constructedClass)),
|
||||
Lifetime.IRRELEVANT
|
||||
)
|
||||
|
||||
callDirect(symbols.interopInterpretObjCPointer.owner, listOf(rawPtr), resultLifetime(callee)).also {
|
||||
// Balance pointer retained by alloc:
|
||||
callDirect(symbols.interopObjCRelease.owner, listOf(rawPtr), Lifetime.IRRELEVANT)
|
||||
}
|
||||
} else {
|
||||
functionGenerationContext.allocInstance(constructedClass, resultLifetime(callee))
|
||||
}
|
||||
@@ -2066,10 +2144,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
}
|
||||
}
|
||||
|
||||
interop.objCObjectInitFromPtr -> {
|
||||
genObjCObjectInitFromPtr(args)
|
||||
}
|
||||
|
||||
interop.getObjCReceiverOrSuper -> {
|
||||
genGetObjCReceiverOrSuper(args)
|
||||
}
|
||||
@@ -2226,36 +2300,24 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
}
|
||||
|
||||
private fun genGetObjCClass(classDescriptor: ClassDescriptor): LLVMValueRef {
|
||||
assert(!classDescriptor.isInterface)
|
||||
return functionGenerationContext.getObjCClass(classDescriptor, currentCodeContext.exceptionHandler)
|
||||
}
|
||||
|
||||
return if (classDescriptor.isExternalObjCClass()) {
|
||||
context.llvm.imports.add(classDescriptor.llvmSymbolOrigin)
|
||||
private fun genGetObjCProtocol(irClass: IrClass): LLVMValueRef {
|
||||
// Note: this function will return the same result for Obj-C protocol and corresponding meta-class.
|
||||
|
||||
val lookUpFunction = context.llvm.externalFunction("Kotlin_Interop_getObjCClass",
|
||||
functionType(int8TypePtr, false, int8TypePtr),
|
||||
origin = context.standardLlvmSymbolsOrigin
|
||||
)
|
||||
assert(irClass.isInterface)
|
||||
assert(irClass.isExternalObjCClass())
|
||||
|
||||
call(lookUpFunction,
|
||||
listOf(codegen.staticData.cStringLiteral(classDescriptor.name.asString()).llvm))
|
||||
} else {
|
||||
val objCDeclarations = context.llvmDeclarations.forClass(classDescriptor).objCDeclarations!!
|
||||
val classPointerGlobal = objCDeclarations.classPointerGlobal.llvmGlobal
|
||||
val gen = functionGenerationContext
|
||||
|
||||
val storedClass = gen.load(classPointerGlobal)
|
||||
|
||||
val storedClassIsNotNull = gen.icmpNe(storedClass, kNullInt8Ptr)
|
||||
|
||||
return gen.ifThenElse(storedClassIsNotNull, storedClass) {
|
||||
val newClass = call(context.llvm.createKotlinObjCClass,
|
||||
listOf(objCDeclarations.classInfoGlobal.llvmGlobal))
|
||||
|
||||
gen.store(newClass, classPointerGlobal)
|
||||
newClass
|
||||
}
|
||||
}
|
||||
val annotation = irClass.annotations.findAnnotation(externalObjCClassFqName)!!
|
||||
val protocolGetterName = annotation.getStringValue("protocolGetter")
|
||||
val protocolGetter = context.llvm.externalFunction(
|
||||
protocolGetterName,
|
||||
functionType(int8TypePtr, false),
|
||||
irClass.llvmSymbolOrigin
|
||||
)
|
||||
|
||||
return call(protocolGetter, emptyList())
|
||||
}
|
||||
|
||||
private fun genGetObjCMessenger(args: List<LLVMValueRef>, isLU: Boolean): LLVMValueRef {
|
||||
@@ -2302,11 +2364,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
return gen.bitcast(int8TypePtr, messenger)
|
||||
}
|
||||
|
||||
private fun genObjCObjectInitFromPtr(args: List<LLVMValueRef>): LLVMValueRef {
|
||||
return callDirect(context.ir.symbols.objCPointerHolder.owner.constructors.single(),
|
||||
args, Lifetime.IRRELEVANT)
|
||||
}
|
||||
|
||||
private fun genGetObjCReceiverOrSuper(args: List<LLVMValueRef>): LLVMValueRef {
|
||||
val gen = functionGenerationContext
|
||||
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
internal open class ObjCCodeGenerator(val codegen: CodeGenerator) {
|
||||
val context = codegen.context
|
||||
|
||||
val dataGenerator = ObjCDataGenerator(codegen)
|
||||
val dataGenerator = codegen.objCDataGenerator!!
|
||||
|
||||
fun FunctionGenerationContext.genSelector(selector: String): LLVMValueRef {
|
||||
val selectorRef = dataGenerator.genSelectorRef(selector)
|
||||
|
||||
+1
-10
@@ -216,10 +216,6 @@ internal class ObjCExportCodeGenerator(
|
||||
|
||||
emitSortedAdapters(placedClassAdapters, "Kotlin_ObjCExport_sortedClassAdapters")
|
||||
emitSortedAdapters(placedInterfaceAdapters, "Kotlin_ObjCExport_sortedProtocolAdapters")
|
||||
|
||||
context.llvm.objCExportEnabled!!.setInitializer(Int8(1))
|
||||
|
||||
dataGenerator.finishModule() // TODO: move to appropriate place.
|
||||
}
|
||||
|
||||
private val impType = pointerType(functionType(int8TypePtr, true, int8TypePtr, int8TypePtr))
|
||||
@@ -408,7 +404,7 @@ private fun ObjCExportCodeGenerator.emitKotlinFunctionAdaptersToBlock() {
|
||||
private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
|
||||
setObjCExportTypeInfo(
|
||||
context.builtIns.string,
|
||||
constPointer(codegen.llvmFunction(context.ir.symbols.interopCreateNSStringFromKString.owner))
|
||||
constPointer(context.llvm.Kotlin_ObjCExport_CreateNSStringFromKString)
|
||||
)
|
||||
|
||||
setObjCExportTypeInfo(
|
||||
@@ -445,11 +441,6 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
|
||||
emitBoxConverter(it)
|
||||
}
|
||||
|
||||
setObjCExportTypeInfo(
|
||||
context.interopBuiltIns.objCPointerHolder,
|
||||
constPointer(codegen.llvmFunction(context.ir.symbols.objCPointerHolderValueGetter.owner))
|
||||
)
|
||||
|
||||
emitFunctionConverters()
|
||||
|
||||
emitKotlinFunctionAdaptersToBlock()
|
||||
|
||||
+50
-32
@@ -23,9 +23,10 @@ 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.allOverriddenDescriptors
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isReal
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
|
||||
@@ -80,16 +81,6 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
private fun IrBuilderWithScope.getObjCClass(classSymbol: IrClassSymbol): IrExpression {
|
||||
val classDescriptor = classSymbol.descriptor
|
||||
assert(!classDescriptor.isObjCMetaClass())
|
||||
|
||||
if (classDescriptor.isExternalObjCClass()) {
|
||||
val companionObject = classDescriptor.companionObjectDescriptor!!
|
||||
if (companionObject.unsubstitutedPrimaryConstructor != scope.scopeOwner) {
|
||||
// Optimization: get class pointer from companion object thus avoiding lookup by name.
|
||||
return irCall(symbols.interopObjCObjectRawValueGetter).apply {
|
||||
extensionReceiver = irGetObject(symbolTable.referenceClass(companionObject))
|
||||
}
|
||||
}
|
||||
}
|
||||
return irCall(symbols.interopGetObjCClass, listOf(classDescriptor.defaultType))
|
||||
}
|
||||
|
||||
@@ -349,6 +340,29 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
)
|
||||
}
|
||||
|
||||
val methodsOfAny =
|
||||
context.ir.symbols.any.owner.declarations.filterIsInstance<IrSimpleFunction>().toSet()
|
||||
|
||||
irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isReal }.forEach { method ->
|
||||
val overriddenMethodOfAny = method.allOverriddenDescriptors.firstOrNull {
|
||||
it in methodsOfAny
|
||||
}
|
||||
|
||||
if (overriddenMethodOfAny != null) {
|
||||
val correspondingObjCMethod = when (method.name.asString()) {
|
||||
"toString" -> "'description'"
|
||||
"hashCode" -> "'hash'"
|
||||
"equals" -> "'isEqual:'"
|
||||
else -> "corresponding Objective-C method"
|
||||
}
|
||||
|
||||
context.report(
|
||||
method,
|
||||
"can't override '${method.name}', override $correspondingObjCMethod instead",
|
||||
isError = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
|
||||
@@ -367,18 +381,10 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
if (classContainer is ClassDescriptor && classContainer.isObjCClass() &&
|
||||
constructedClassDescriptor == classContainer.companionObjectDescriptor) {
|
||||
|
||||
val outerConstructedClass = outerClasses[outerClasses.lastIndex - 1]
|
||||
assert (outerConstructedClass.descriptor == classContainer)
|
||||
// Note: it is actually not used; getting values of such objects is handled by code generator
|
||||
// in [FunctionGenerationContext.getObjectValue].
|
||||
|
||||
assert(expression.getArguments().isEmpty())
|
||||
|
||||
return builder.irBlock(expression) {
|
||||
+expression // Required for the IR to be valid, will be ignored in codegen.
|
||||
+irCall(symbols.interopObjCObjectInitFromPtr).apply {
|
||||
extensionReceiver = irGet(constructedClass.thisReceiver!!.symbol)
|
||||
putValueArgument(0, getObjCClass(outerConstructedClass.symbol))
|
||||
}
|
||||
}
|
||||
return expression
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +404,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
val initCall = builder.genLoweredObjCMethodCall(
|
||||
initMethodInfo,
|
||||
superQualifier = symbolTable.referenceClass(expression.descriptor.constructedClass),
|
||||
receiver = builder.irGet(constructedClass.thisReceiver!!.symbol),
|
||||
receiver = builder.getRawPtr(builder.irGet(constructedClass.thisReceiver!!.symbol)),
|
||||
arguments = initMethod.valueParameters.map { expression.getValueArgument(it)!! }
|
||||
)
|
||||
|
||||
@@ -451,14 +457,21 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
assert(expression.extensionReceiver == null)
|
||||
assert(expression.dispatchReceiver == null)
|
||||
|
||||
builder.at(expression)
|
||||
|
||||
return builder.genLoweredObjCMethodCall(
|
||||
initMethod.getExternalObjCMethodInfo()!!,
|
||||
superQualifier = null,
|
||||
receiver = builder.callAlloc(symbolTable.referenceClass(descriptor.constructedClass)),
|
||||
arguments = arguments
|
||||
)
|
||||
return builder.irBlock(expression) {
|
||||
val allocated = irTemporaryVar(callAlloc(symbolTable.referenceClass(descriptor.constructedClass)))
|
||||
val result = irTemporaryVar(
|
||||
genLoweredObjCMethodCall(
|
||||
initMethod.getExternalObjCMethodInfo()!!,
|
||||
superQualifier = null,
|
||||
receiver = irGet(allocated.symbol),
|
||||
arguments = arguments
|
||||
)
|
||||
)
|
||||
+irCall(symbols.interopObjCRelease).apply {
|
||||
putValueArgument(0, irGet(allocated.symbol)) // Balance pointer retained by alloc.
|
||||
}
|
||||
+irGet(result.symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,7 +506,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
return builder.genLoweredObjCMethodCall(
|
||||
methodInfo,
|
||||
superQualifier = expression.superQualifierSymbol,
|
||||
receiver = expression.dispatchReceiver ?: expression.extensionReceiver!!,
|
||||
receiver = builder.getRawPtr(expression.dispatchReceiver ?: expression.extensionReceiver!!),
|
||||
arguments = arguments
|
||||
)
|
||||
}
|
||||
@@ -519,6 +532,11 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
else -> expression
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.getRawPtr(receiver: IrExpression) =
|
||||
irCall(symbols.interopObjCObjectRawValueGetter).apply {
|
||||
extensionReceiver = receiver
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+42
-11
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
|
||||
import org.jetbrains.kotlin.backend.konan.isNativeBinary
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportCodeGenerator
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
@@ -29,19 +31,51 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
|
||||
internal class ObjCExport(val context: Context) {
|
||||
internal class ObjCExport(val codegen: CodeGenerator) {
|
||||
val context get() = codegen.context
|
||||
|
||||
private val target get() = context.config.target
|
||||
|
||||
internal fun produceObjCFramework() {
|
||||
if (context.config.produce != CompilerOutputKind.FRAMEWORK) return
|
||||
internal fun produce() {
|
||||
if (target != KonanTarget.IOS_ARM64 && target != KonanTarget.IOS_X64 && target != KonanTarget.MACOS_X64) return
|
||||
|
||||
val headerGenerator = ObjCExportHeaderGenerator(context)
|
||||
if (!context.config.produce.isNativeBinary) return // TODO: emit RTTI to the same modules as classes belong to.
|
||||
|
||||
val objCCodeGenerator: ObjCExportCodeGenerator
|
||||
val generatedClasses: Set<ClassDescriptor>
|
||||
val topLevelDeclarations: Map<FqName, List<CallableMemberDescriptor>>
|
||||
|
||||
if (context.config.produce == CompilerOutputKind.FRAMEWORK) {
|
||||
val headerGenerator = ObjCExportHeaderGenerator(context)
|
||||
produceFrameworkSpecific(headerGenerator)
|
||||
|
||||
generatedClasses = headerGenerator.generatedClasses
|
||||
topLevelDeclarations = headerGenerator.topLevel
|
||||
objCCodeGenerator = ObjCExportCodeGenerator(codegen, headerGenerator.namer, headerGenerator.mapper)
|
||||
} else {
|
||||
// TODO: refactor ObjCExport* to handle this case on a general basis.
|
||||
val mapper = object : ObjCExportMapper() {
|
||||
override fun getCategoryMembersFor(descriptor: ClassDescriptor): List<CallableMemberDescriptor> =
|
||||
emptyList()
|
||||
|
||||
override fun isSpecialMapped(descriptor: ClassDescriptor): Boolean =
|
||||
error("shouldn't reach here")
|
||||
|
||||
}
|
||||
|
||||
val namer = ObjCExportNamer(context, mapper)
|
||||
objCCodeGenerator = ObjCExportCodeGenerator(codegen, namer, mapper)
|
||||
|
||||
generatedClasses = emptySet()
|
||||
topLevelDeclarations = emptyMap()
|
||||
}
|
||||
|
||||
objCCodeGenerator.emitRtti(generatedClasses = generatedClasses, topLevel = topLevelDeclarations)
|
||||
}
|
||||
|
||||
private fun produceFrameworkSpecific(headerGenerator: ObjCExportHeaderGenerator) {
|
||||
headerGenerator.translateModule()
|
||||
|
||||
val namer = headerGenerator.namer
|
||||
val mapper = headerGenerator.mapper
|
||||
|
||||
val framework = File(context.config.outputFile)
|
||||
val frameworkContents = when (target) {
|
||||
KonanTarget.IOS_ARM64, KonanTarget.IOS_X64 -> framework
|
||||
@@ -79,9 +113,6 @@ internal class ObjCExport(val context: Context) {
|
||||
framework.child(child).createAsSymlink("Versions/Current/$child")
|
||||
}
|
||||
}
|
||||
|
||||
val objCCodeGenerator = ObjCExportCodeGenerator(CodeGenerator(context), namer, mapper)
|
||||
objCCodeGenerator.emitRtti(headerGenerator.generatedClasses, headerGenerator.topLevel)
|
||||
}
|
||||
|
||||
private fun emitInfoPlist(frameworkContents: File, name: String) {
|
||||
|
||||
+4
-1
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.kotlin.ir.util
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
||||
import org.jetbrains.kotlin.backend.konan.KonanCompilationException
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
@@ -358,7 +360,7 @@ tailrec fun IrDeclaration.getContainingFile(): IrFile? {
|
||||
}
|
||||
}
|
||||
|
||||
fun CommonBackendContext.report(declaration: IrDeclaration, message: String, isError: Boolean) {
|
||||
internal fun KonanBackendContext.report(declaration: IrDeclaration, message: String, isError: Boolean) {
|
||||
val irFile = declaration.getContainingFile()
|
||||
this.report(
|
||||
declaration,
|
||||
@@ -371,4 +373,5 @@ fun CommonBackendContext.report(declaration: IrDeclaration, message: String, isE
|
||||
},
|
||||
isError
|
||||
)
|
||||
if (isError) throw KonanCompilationException()
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
language = Objective-C
|
||||
headerFilter = **/smoke.h
|
||||
headers = Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h
|
||||
headerFilter = **/smoke.h Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import kotlinx.cinterop.*
|
||||
import objcSmoke.*
|
||||
import kotlin.test.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
autoreleasepool {
|
||||
@@ -8,6 +9,9 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
fun run() {
|
||||
testTypeOps()
|
||||
testConversions()
|
||||
|
||||
println(
|
||||
getSupplier(
|
||||
invoke1(42) { it * 2 }
|
||||
@@ -44,7 +48,7 @@ fun run() {
|
||||
}
|
||||
|
||||
// hashCode (directly):
|
||||
if (foo.hashCode() == foo.hash().toInt()) {
|
||||
if (foo.hashCode() == foo.hash().let { it.toInt() xor (it shr 32).toInt() }) {
|
||||
// toString (virtually):
|
||||
println(listOf(foo, pair).map { it.toString() }.min() == foo.description())
|
||||
}
|
||||
@@ -94,4 +98,57 @@ class MutablePairImpl(first: Int, second: Int) : NSObject(), MutablePairProtocol
|
||||
override fun update(index: Int, sub: Int) {
|
||||
elements[index] -= sub
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun testTypeOps() {
|
||||
assertTrue(99.asAny() is NSNumber)
|
||||
assertTrue(null.asAny() is NSNumber?)
|
||||
assertFalse(null.asAny() is NSNumber)
|
||||
assertFalse("".asAny() is NSNumber)
|
||||
assertTrue("bar".asAny() is NSString)
|
||||
|
||||
assertTrue(Foo.asAny() is FooMeta)
|
||||
assertTrue(Foo.asAny() is NSObjectMeta)
|
||||
assertTrue(Foo.asAny() is NSObject)
|
||||
assertFalse(Foo.asAny() is Foo)
|
||||
assertTrue(NSString.asAny() is NSCopyingProtocolMeta)
|
||||
assertFalse(NSString.asAny() is NSCopyingProtocol)
|
||||
assertTrue(NSValue.asAny() is NSObjectProtocolMeta)
|
||||
assertFalse(NSValue.asAny() is NSObjectProtocol) // Must be true, but not implemented properly yet.
|
||||
|
||||
assertEquals(3, ("foo" as NSString).length())
|
||||
assertEquals(4, ((1..4).joinToString("") as NSString).length())
|
||||
assertEquals(2, (listOf(0, 1) as NSArray).count())
|
||||
assertEquals(42L, (42 as NSNumber).longLongValue())
|
||||
|
||||
assertFails { "bar" as NSNumber }
|
||||
assertFails { 42 as NSArray }
|
||||
assertFails { listOf(1) as NSString }
|
||||
assertFails { NSObject() as Bar }
|
||||
assertFails { NSObject() as NSValue }
|
||||
|
||||
MutablePairImpl(1, 2).asAny() as MutablePairProtocol
|
||||
assertFails { MutablePairImpl(1, 2).asAny() as Foo }
|
||||
}
|
||||
|
||||
fun testConversions() {
|
||||
testMethodsOfAny(emptyList<Nothing>(), NSArray())
|
||||
testMethodsOfAny(listOf(1, "foo"), nsArrayOf(1, "foo"))
|
||||
testMethodsOfAny(42, NSNumber.numberWithInt(42), 17)
|
||||
}
|
||||
|
||||
fun testMethodsOfAny(kotlinObject: Any, equalNsObject: NSObject, otherObject: Any = Any()) {
|
||||
assertEquals(kotlinObject.hashCode(), equalNsObject.hashCode())
|
||||
assertEquals(kotlinObject.toString(), equalNsObject.toString())
|
||||
assertEquals(kotlinObject, equalNsObject)
|
||||
assertEquals(equalNsObject, kotlinObject)
|
||||
assertNotEquals(equalNsObject, otherObject)
|
||||
}
|
||||
|
||||
fun nsArrayOf(vararg elements: Any): NSArray = NSMutableArray().apply {
|
||||
elements.forEach {
|
||||
this.addObject(it as ObjCObject)
|
||||
}
|
||||
}
|
||||
|
||||
fun Any?.asAny(): Any? = this
|
||||
|
||||
@@ -418,12 +418,6 @@ inline void runDeallocationHooks(ObjHeader* obj) {
|
||||
if (obj->has_meta_object()) {
|
||||
ObjHeader::destroyMetaObject(&obj->typeInfoOrMeta_);
|
||||
}
|
||||
#if KONAN_OBJC_INTEROP
|
||||
if (obj->type_info() == theObjCPointerHolderTypeInfo) {
|
||||
void* objcPtr = *reinterpret_cast<void**>(obj + 1); // TODO: use more reliable layout description
|
||||
objc_release(objcPtr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void runDeallocationHooks(ContainerHeader* container) {
|
||||
@@ -828,7 +822,7 @@ void ObjHeader::destroyMetaObject(TypeInfo** location) {
|
||||
}
|
||||
|
||||
#ifdef KONAN_OBJC_INTEROP
|
||||
Kotlin_ObjCExport_releaseAssociatedObject(meta->associatedObject);
|
||||
Kotlin_ObjCExport_releaseAssociatedObject(meta->associatedObject_);
|
||||
#endif
|
||||
|
||||
konanFreeMemory(meta);
|
||||
|
||||
@@ -246,7 +246,7 @@ struct MetaObjHeader {
|
||||
ObjHeader* counter_;
|
||||
|
||||
#ifdef KONAN_OBJC_INTEROP
|
||||
void* associatedObject;
|
||||
void* associatedObject_;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -12,11 +12,12 @@ extern "C" id objc_retain(id self);
|
||||
extern "C" void objc_release(id self);
|
||||
|
||||
inline static id GetAssociatedObject(ObjHeader* obj) {
|
||||
return (id)obj->meta_object()->associatedObject;
|
||||
return (id)obj->meta_object()->associatedObject_;
|
||||
}
|
||||
|
||||
// Note: this function shall not be used on shared objects.
|
||||
inline static void SetAssociatedObject(ObjHeader* obj, id value) {
|
||||
obj->meta_object()->associatedObject = (void*)value;
|
||||
obj->meta_object()->associatedObject_ = (void*)value;
|
||||
}
|
||||
|
||||
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#import "Types.h"
|
||||
#import "Memory.h"
|
||||
#include "Natives.h"
|
||||
|
||||
#if KONAN_OBJC_INTEROP
|
||||
|
||||
@@ -36,9 +37,6 @@
|
||||
#import "Utils.h"
|
||||
#import "Exceptions.h"
|
||||
|
||||
// Note: defined by a compiler-generated bitcode.
|
||||
extern "C" const uint8_t objCExportEnabled;
|
||||
|
||||
struct ObjCToKotlinMethodAdapter {
|
||||
const char* selector;
|
||||
const char* encoding;
|
||||
@@ -111,6 +109,22 @@ inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* type
|
||||
|
||||
static Class getOrCreateClass(const TypeInfo* typeInfo);
|
||||
static void initializeClass(Class clazz);
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
|
||||
|
||||
static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject) {
|
||||
if (!obj->container()->permanentOrFrozen()) {
|
||||
SetAssociatedObject(obj, associatedObject);
|
||||
return associatedObject;
|
||||
} else {
|
||||
void* old = __sync_val_compare_and_swap(&obj->meta_object()->associatedObject_, nullptr, (void*)associatedObject);
|
||||
if (old == nullptr) {
|
||||
return associatedObject;
|
||||
} else {
|
||||
Kotlin_ObjCExport_releaseAssociatedObject((void*)associatedObject);
|
||||
return (id)old;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@interface KotlinBase : NSObject <ConvertibleToKotlin, NSCopying>
|
||||
@end;
|
||||
@@ -154,13 +168,14 @@ static void initializeClass(Class clazz);
|
||||
KotlinBase* result = [super allocWithZone:nil];
|
||||
// TODO: should we call NSObject.init ?
|
||||
UpdateRef(&result->kotlinObj, obj);
|
||||
[result autorelease];
|
||||
|
||||
if (!obj->permanent()) {
|
||||
SetAssociatedObject(obj, result);
|
||||
return AtomicSetAssociatedObject(obj, result);
|
||||
} else {
|
||||
// TODO: permanent objects should probably be supported as custom types.
|
||||
return result;
|
||||
}
|
||||
// TODO: permanent objects should probably be supported as custom types.
|
||||
|
||||
return [result autorelease];
|
||||
}
|
||||
|
||||
-(instancetype)retain {
|
||||
@@ -194,7 +209,7 @@ static void initializeClass(Class clazz);
|
||||
|
||||
@end;
|
||||
|
||||
extern "C" void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject) {
|
||||
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject) {
|
||||
if (associatedObject != nullptr) {
|
||||
[((id)associatedObject) releaseAsAssociatedObject];
|
||||
}
|
||||
@@ -210,6 +225,27 @@ extern "C" id Kotlin_ObjCExport_convertUnit(ObjHeader* unitInstance) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
extern "C" id objc_retainBlock(id self);
|
||||
extern "C" id objc_retainAutoreleaseReturnValue(id self);
|
||||
|
||||
extern "C" id Kotlin_ObjCExport_CreateNSStringFromKString(ObjHeader* str) {
|
||||
KChar* utf16Chars = CharArrayAddressOfElementAt(str->array(), 0);
|
||||
auto numBytes = str->array()->count_ * sizeof(KChar);
|
||||
|
||||
if (str->permanent()) {
|
||||
return [[[NSString alloc] initWithBytesNoCopy:utf16Chars
|
||||
length:numBytes
|
||||
encoding:NSUTF16LittleEndianStringEncoding
|
||||
freeWhenDone:NO] autorelease];
|
||||
} else {
|
||||
// TODO: consider making NSString subclass to avoid copying here.
|
||||
NSString* result = [[NSString alloc] initWithBytes:utf16Chars
|
||||
length:numBytes
|
||||
encoding:NSUTF16LittleEndianStringEncoding];
|
||||
|
||||
return objc_retainAutoreleaseReturnValue(AtomicSetAssociatedObject(str, result));
|
||||
}
|
||||
}
|
||||
static const ObjCTypeAdapter* findAdapterByName(
|
||||
const char* name,
|
||||
const ObjCTypeAdapter** sortedAdapters,
|
||||
@@ -315,9 +351,6 @@ static void initializeClass(Class clazz) {
|
||||
@interface NSObject (NSObjectToKotlin) <ConvertibleToKotlin>
|
||||
@end;
|
||||
|
||||
extern "C" id objc_retainBlock(id self);
|
||||
extern "C" id objc_retainAutoreleaseReturnValue(id self);
|
||||
|
||||
@implementation NSObject (NSObjectToKotlin)
|
||||
-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT {
|
||||
const TypeInfo* typeInfo = getOrCreateTypeInfo(object_getClass(self));
|
||||
@@ -494,16 +527,19 @@ static const TypeInfo* getFunctionTypeInfoForBlock(id block) {
|
||||
|
||||
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj);
|
||||
|
||||
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj) {
|
||||
template <bool retainAutorelease>
|
||||
static ALWAYS_INLINE id Kotlin_ObjCExport_refToObjCImpl(ObjHeader* obj) {
|
||||
if (obj == nullptr) return nullptr;
|
||||
|
||||
if (obj->has_meta_object()) {
|
||||
id associatedObject = GetAssociatedObject(obj);
|
||||
if (associatedObject != nullptr) {
|
||||
return objc_retainAutoreleaseReturnValue(associatedObject);
|
||||
return retainAutorelease ? objc_retainAutoreleaseReturnValue(associatedObject) : associatedObject;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: propagate [retainAutorelease] to the code below.
|
||||
|
||||
convertReferenceToObjC converter = (convertReferenceToObjC)obj->type_info()->writableInfo_->objCExport.convert;
|
||||
if (converter != nullptr) {
|
||||
return converter(obj);
|
||||
@@ -512,14 +548,18 @@ extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj) {
|
||||
return Kotlin_ObjCExport_refToObjC_slowpath(obj);
|
||||
}
|
||||
|
||||
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj) {
|
||||
// TODO: in some cases (e.g. when converting a bridge argument) performing retain-autorelease is not necessary.
|
||||
return Kotlin_ObjCExport_refToObjCImpl<true>(obj);
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE id Kotlin_Interop_refToObjC(ObjHeader* obj) {
|
||||
if (obj == nullptr) {
|
||||
return nullptr;
|
||||
} else if (!objCExportEnabled || obj->type_info() == theObjCPointerHolderTypeInfo) {
|
||||
return *reinterpret_cast<id*>(obj + 1); // First field.
|
||||
} else {
|
||||
return Kotlin_ObjCExport_refToObjC(obj);
|
||||
}
|
||||
return Kotlin_ObjCExport_refToObjCImpl<false>(obj);
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE OBJ_GETTER(Kotlin_Interop_refFromObjC, id obj) {
|
||||
// TODO: consider removing this function.
|
||||
RETURN_RESULT_OF(Kotlin_ObjCExport_refFromObjC, obj);
|
||||
}
|
||||
|
||||
extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj) {
|
||||
@@ -679,6 +719,16 @@ static const TypeInfo* getMostSpecificKotlinClass(const TypeInfo* typeInfo) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static int getVtableSize(const TypeInfo* typeInfo) {
|
||||
for (const TypeInfo* current = typeInfo; current != nullptr; current = current->superType_) {
|
||||
auto typeAdapter = getTypeAdapter(current);
|
||||
if (typeAdapter != nullptr) return typeAdapter->kotlinVtableSize;
|
||||
}
|
||||
|
||||
RuntimeAssert(false, "");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void insertOrReplace(KStdVector<MethodTableRecord>& methodTable, MethodNameHash nameSignature, void* entryPoint) {
|
||||
MethodTableRecord record = {nameSignature, entryPoint};
|
||||
|
||||
@@ -704,7 +754,7 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType) {
|
||||
const ObjCTypeAdapter* superTypeAdapter = getTypeAdapter(superType);
|
||||
|
||||
const void * const * superVtable = nullptr;
|
||||
int superVtableSize = getTypeAdapter(getMostSpecificKotlinClass(superType))->kotlinVtableSize;
|
||||
int superVtableSize = getVtableSize(superType);
|
||||
|
||||
const MethodTableRecord* superMethodTable = nullptr;
|
||||
int superMethodTableSize = 0;
|
||||
@@ -802,7 +852,7 @@ static const TypeInfo* getOrCreateTypeInfo(Class clazz) {
|
||||
Class superClass = class_getSuperclass(clazz);
|
||||
|
||||
const TypeInfo* superType = superClass == nullptr ?
|
||||
theAnyTypeInfo :
|
||||
theForeignObjCObjectTypeInfo :
|
||||
getOrCreateTypeInfo(superClass);
|
||||
|
||||
LockGuard<SimpleMutex> lockGuard(typeInfoCreationMutex);
|
||||
@@ -920,9 +970,14 @@ static void checkLoadedOnce() {
|
||||
|
||||
#else
|
||||
|
||||
extern "C" ALWAYS_INLINE id Kotlin_Interop_refToObjC(ObjHeader* obj) {
|
||||
extern "C" ALWAYS_INLINE void* Kotlin_Interop_refToObjC(ObjHeader* obj) {
|
||||
RuntimeAssert(false, "Unavailable operation");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" ALWAYS_INLINE OBJ_GETTER(Kotlin_Interop_refFromObjC, void* obj) {
|
||||
RuntimeAssert(false, "Unavailable operation");
|
||||
RETURN_OBJ(nullptr);
|
||||
}
|
||||
|
||||
#endif // KONAN_OBJC_INTEROP
|
||||
|
||||
@@ -43,10 +43,12 @@ static inline void SetKotlinTypeInfo(Class clazz, const TypeInfo* typeInfo) {
|
||||
GetKotlinClassData(clazz)->typeInfo = typeInfo;
|
||||
}
|
||||
|
||||
const TypeInfo* GetObjCKotlinTypeInfo(const ObjHeader* obj) RUNTIME_NOTHROW;
|
||||
const TypeInfo* GetObjCKotlinTypeInfo(ObjHeader* obj) RUNTIME_NOTHROW;
|
||||
|
||||
const TypeInfo* GetObjCKotlinTypeInfo(const ObjHeader* obj) {
|
||||
void* objcPtr = *reinterpret_cast<void * const *>(obj + 1); // TODO: use more reliable layout description
|
||||
const TypeInfo* GetObjCKotlinTypeInfo(ObjHeader* obj) {
|
||||
RuntimeAssert(obj->has_meta_object(), "");
|
||||
void* objcPtr = obj->meta_object()->associatedObject_;
|
||||
RuntimeAssert(objcPtr != nullptr, "");
|
||||
Class clazz = object_getClass(reinterpret_cast<id>(objcPtr));
|
||||
return GetKotlinClassData(clazz)->typeInfo;
|
||||
}
|
||||
|
||||
@@ -44,18 +44,22 @@ namespace {
|
||||
|
||||
extern "C" {
|
||||
|
||||
id Kotlin_Interop_CreateNSStringFromKString(const ObjHeader* str) {
|
||||
id Kotlin_ObjCExport_CreateNSStringFromKString(ObjHeader* str);
|
||||
|
||||
id Kotlin_Interop_CreateNSStringFromKString(ObjHeader* str) {
|
||||
// Note: this function is just a bit specialized [Kotlin_Interop_refToObjC].
|
||||
if (str == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const KChar* utf16Chars = CharArrayAddressOfElementAt(str->array(), 0);
|
||||
if (str->has_meta_object()) {
|
||||
void* associatedObject = str->meta_object()->associatedObject_;
|
||||
if (associatedObject != nullptr) {
|
||||
return (id)associatedObject;
|
||||
}
|
||||
}
|
||||
|
||||
NSString* result = [[[getNSStringClass() alloc] initWithBytes:utf16Chars
|
||||
length:str->array()->count_*sizeof(KChar)
|
||||
encoding:NSUTF16LittleEndianStringEncoding] autorelease];
|
||||
|
||||
return result;
|
||||
return Kotlin_ObjCExport_CreateNSStringFromKString(str);
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, NSString* str) {
|
||||
@@ -78,7 +82,7 @@ OBJ_GETTER(Kotlin_Interop_ObjCToString, id <NSObject> ptr) {
|
||||
}
|
||||
|
||||
KInt Kotlin_Interop_ObjCHashCode(id <NSObject> ptr) {
|
||||
KLong hash = ptr.hash;
|
||||
uint64_t hash = ptr.hash;
|
||||
return (KInt)(hash ^ (hash >> 32));
|
||||
}
|
||||
|
||||
@@ -149,6 +153,18 @@ KRef Kotlin_Interop_unwrapKotlinObjectHolder(id holder) {
|
||||
return [((KotlinObjectHolder*)holder) ref];
|
||||
}
|
||||
|
||||
KBoolean Kotlin_Interop_DoesObjectConformToProtocol(id obj, void* prot, BOOL isMeta) {
|
||||
BOOL objectIsClass = class_isMetaClass(object_getClass(obj));
|
||||
if ((isMeta && !objectIsClass) || (!isMeta && objectIsClass)) return false;
|
||||
// TODO: handle root classes properly.
|
||||
|
||||
return [((id<NSObject>)obj) conformsToProtocol:(Protocol*)prot];
|
||||
}
|
||||
|
||||
KBoolean Kotlin_Interop_IsObjectKindOfClass(id obj, void* cls) {
|
||||
return [((id<NSObject>)obj) isKindOfClass:(Class)cls];
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#else // KONAN_OBJC_INTEROP
|
||||
|
||||
@@ -83,7 +83,7 @@ extern const TypeInfo* theBooleanArrayTypeInfo;
|
||||
extern const TypeInfo* theStringTypeInfo;
|
||||
extern const TypeInfo* theThrowableTypeInfo;
|
||||
extern const TypeInfo* theUnitTypeInfo;
|
||||
extern const TypeInfo* theObjCPointerHolderTypeInfo;
|
||||
extern const TypeInfo* theForeignObjCObjectTypeInfo;
|
||||
|
||||
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
|
||||
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
|
||||
|
||||
Reference in New Issue
Block a user