Make Objective-C objects shareable in Kotlin

This commit is contained in:
Svyatoslav Scherbina
2018-11-19 17:04:50 +03:00
committed by SvyatoslavScherbina
parent df91304d54
commit 77897ba712
7 changed files with 45 additions and 7 deletions
@@ -26,6 +26,7 @@ interface ObjCClassOf<T : ObjCObject> : ObjCClass // TODO: T should be added to
typealias ObjCObjectMeta = ObjCClass typealias ObjCObjectMeta = ObjCClass
@ExportTypeInfo("theForeignObjCObjectTypeInfo") @ExportTypeInfo("theForeignObjCObjectTypeInfo")
@kotlin.native.internal.Frozen
internal open class ForeignObjCObject : kotlin.native.internal.ObjCObjectWrapper internal open class ForeignObjCObject : kotlin.native.internal.ObjCObjectWrapper
abstract class ObjCObjectBase protected constructor() : ObjCObject { abstract class ObjCObjectBase protected constructor() : ObjCObject {
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.binaryTypeIsReference import org.jetbrains.kotlin.backend.konan.binaryTypeIsReference
import org.jetbrains.kotlin.backend.konan.isObjCClass
import org.jetbrains.kotlin.backend.konan.serialization.isExported import org.jetbrains.kotlin.backend.konan.serialization.isExported
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.builtins.getFunctionalClassKind import org.jetbrains.kotlin.builtins.getFunctionalClassKind
@@ -241,8 +242,9 @@ private val frozenAnnotation = FqName("kotlin.native.internal.Frozen")
internal val DeclarationDescriptor.isFrozen: Boolean internal val DeclarationDescriptor.isFrozen: Boolean
get() = this.annotations.hasAnnotation(frozenAnnotation) || get() = this.annotations.hasAnnotation(frozenAnnotation) ||
// RTTI is used for non-reference type box: (this is org.jetbrains.kotlin.descriptors.ClassDescriptor
this is org.jetbrains.kotlin.descriptors.ClassDescriptor && !this.defaultType.binaryTypeIsReference() // RTTI is used for non-reference type box or Objective-C object wrapper:
&& (!this.defaultType.binaryTypeIsReference() || this.isObjCClass()))
internal val FunctionDescriptor.isIntrinsic: Boolean internal val FunctionDescriptor.isIntrinsic: Boolean
get() = this.annotations.hasAnnotation(intrinsicAnnotation) get() = this.annotations.hasAnnotation(intrinsicAnnotation)
@@ -19,7 +19,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
private fun flagsFromClass(classDescriptor: ClassDescriptor): Int { private fun flagsFromClass(classDescriptor: ClassDescriptor): Int {
var result = 0 var result = 0
if (classDescriptor.isFrozen) if (classDescriptor.isFrozen)
result = result or 1 /* TF_IMMUTABLE */ result = result or TF_IMMUTABLE
return result return result
} }
@@ -291,7 +291,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
// TODO: extract more code common with generate(). // TODO: extract more code common with generate().
fun generateSyntheticInterfaceImpl( fun generateSyntheticInterfaceImpl(
descriptor: ClassDescriptor, descriptor: ClassDescriptor,
methodImpls: Map<FunctionDescriptor, ConstPointer> methodImpls: Map<FunctionDescriptor, ConstPointer>,
immutable: Boolean = false
): ConstPointer { ): ConstPointer {
assert(descriptor.isInterface) assert(descriptor.isInterface)
@@ -346,7 +347,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fields = fieldsPtr, fieldsCount = fieldsCount, fields = fieldsPtr, fieldsCount = fieldsCount,
packageName = reflectionInfo.packageName, packageName = reflectionInfo.packageName,
relativeName = reflectionInfo.relativeName, relativeName = reflectionInfo.relativeName,
flags = flagsFromClass(descriptor), flags = flagsFromClass(descriptor) or (if (immutable) TF_IMMUTABLE else 0),
extendedInfo = NullPointer(runtime.extendedTypeInfoType), extendedInfo = NullPointer(runtime.extendedTypeInfoType),
writableTypeInfo = writableTypeInfo writableTypeInfo = writableTypeInfo
), vtable) ), vtable)
@@ -376,3 +377,5 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
} }
} }
} }
private const val TF_IMMUTABLE = 1
@@ -403,7 +403,8 @@ private fun ObjCExportCodeGenerator.generateKotlinFunctionAdapterToBlock(numberO
return rttiGenerator.generateSyntheticInterfaceImpl( return rttiGenerator.generateSyntheticInterfaceImpl(
irInterface, irInterface,
mapOf(invokeMethod to invokeImpl) mapOf(invokeMethod to invokeImpl),
immutable = true
) )
} }
@@ -8,6 +8,7 @@
package conversions package conversions
import kotlin.native.concurrent.isFrozen
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@@ -236,3 +237,6 @@ fun IC2?.getValueOrNull2() = this?.value
fun IC3.getValue3() = value fun IC3.getValue3() = value
fun IC3?.getValueOrNull3() = this?.value fun IC3?.getValueOrNull3() = this?.value
fun isFrozen(obj: Any): Boolean = obj.isFrozen
fun kotlinLambda(block: (Any) -> Any): Any = block
@@ -420,6 +420,27 @@ func testInlineClasses() throws {
try assertEquals(actual: ValuesKt.getValueOrNull3(ic3N), expected: nil) try assertEquals(actual: ValuesKt.getValueOrNull3(ic3N), expected: nil)
} }
class TestSharedIImpl : NSObject, I {
func iFun() -> String {
return "TestSharedIImpl::iFun"
}
}
func testShared() throws {
func assertFrozen(_ obj: AnyObject) throws {
try assertTrue(ValuesKt.isFrozen(obj: obj), "isFrozen(\(obj))")
}
func assertNotFrozen(_ obj: AnyObject) throws {
try assertFalse(ValuesKt.isFrozen(obj: obj), "isFrozen(\(obj))")
}
try assertFrozen(NSObject())
try assertFrozen(TestSharedIImpl())
try assertFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject)
try assertNotFrozen(FinalClassExtOpen())
}
// -------- Execution of the test -------- // -------- Execution of the test --------
class ValuesTests : TestProvider { class ValuesTests : TestProvider {
@@ -453,6 +474,7 @@ class ValuesTests : TestProvider {
TestCase(name: "TestDataClass", method: withAutorelease(testDataClass)), TestCase(name: "TestDataClass", method: withAutorelease(testDataClass)),
TestCase(name: "TestCompanionObj", method: withAutorelease(testCompanionObj)), TestCase(name: "TestCompanionObj", method: withAutorelease(testCompanionObj)),
TestCase(name: "TestInlineClasses", method: withAutorelease(testInlineClasses)), TestCase(name: "TestInlineClasses", method: withAutorelease(testInlineClasses)),
TestCase(name: "TestShared", method: withAutorelease(testShared)),
] ]
} }
} }
+6 -1
View File
@@ -669,11 +669,16 @@ static const TypeInfo* createTypeInfo(
TypeInfo* result = (TypeInfo*)konanAllocMemory(sizeof(TypeInfo) + vtable.size() * sizeof(void*)); TypeInfo* result = (TypeInfo*)konanAllocMemory(sizeof(TypeInfo) + vtable.size() * sizeof(void*));
result->typeInfo_ = result; result->typeInfo_ = result;
result->flags_ = 0;
MakeGlobalHash(nullptr, 0, &result->name_); MakeGlobalHash(nullptr, 0, &result->name_);
result->instanceSize_ = superType->instanceSize_; result->instanceSize_ = superType->instanceSize_;
result->superType_ = superType; result->superType_ = superType;
result->objOffsets_ = superType->objOffsets_; result->objOffsets_ = superType->objOffsets_;
result->objOffsetsCount_ = superType->objOffsetsCount_; result->objOffsetsCount_ = superType->objOffsetsCount_; // So TF_IMMUTABLE can also be inherited:
if ((superType->flags_ & TF_IMMUTABLE) != 0) {
result->flags_ |= TF_IMMUTABLE;
}
KStdVector<const TypeInfo*> implementedInterfaces( KStdVector<const TypeInfo*> implementedInterfaces(
superType->implementedInterfaces_, superType->implementedInterfaces_ + superType->implementedInterfacesCount_ superType->implementedInterfaces_, superType->implementedInterfaces_ + superType->implementedInterfacesCount_