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
@ExportTypeInfo("theForeignObjCObjectTypeInfo")
@kotlin.native.internal.Frozen
internal open class ForeignObjCObject : kotlin.native.internal.ObjCObjectWrapper
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.konan.binaryTypeIsReference
import org.jetbrains.kotlin.backend.konan.isObjCClass
import org.jetbrains.kotlin.backend.konan.serialization.isExported
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.builtins.getFunctionalClassKind
@@ -241,8 +242,9 @@ private val frozenAnnotation = FqName("kotlin.native.internal.Frozen")
internal val DeclarationDescriptor.isFrozen: Boolean
get() = this.annotations.hasAnnotation(frozenAnnotation) ||
// RTTI is used for non-reference type box:
this is org.jetbrains.kotlin.descriptors.ClassDescriptor && !this.defaultType.binaryTypeIsReference()
(this is org.jetbrains.kotlin.descriptors.ClassDescriptor
// RTTI is used for non-reference type box or Objective-C object wrapper:
&& (!this.defaultType.binaryTypeIsReference() || this.isObjCClass()))
internal val FunctionDescriptor.isIntrinsic: Boolean
get() = this.annotations.hasAnnotation(intrinsicAnnotation)
@@ -19,7 +19,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
private fun flagsFromClass(classDescriptor: ClassDescriptor): Int {
var result = 0
if (classDescriptor.isFrozen)
result = result or 1 /* TF_IMMUTABLE */
result = result or TF_IMMUTABLE
return result
}
@@ -291,7 +291,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
// TODO: extract more code common with generate().
fun generateSyntheticInterfaceImpl(
descriptor: ClassDescriptor,
methodImpls: Map<FunctionDescriptor, ConstPointer>
methodImpls: Map<FunctionDescriptor, ConstPointer>,
immutable: Boolean = false
): ConstPointer {
assert(descriptor.isInterface)
@@ -346,7 +347,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fields = fieldsPtr, fieldsCount = fieldsCount,
packageName = reflectionInfo.packageName,
relativeName = reflectionInfo.relativeName,
flags = flagsFromClass(descriptor),
flags = flagsFromClass(descriptor) or (if (immutable) TF_IMMUTABLE else 0),
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
writableTypeInfo = writableTypeInfo
), 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(
irInterface,
mapOf(invokeMethod to invokeImpl)
mapOf(invokeMethod to invokeImpl),
immutable = true
)
}
@@ -8,6 +8,7 @@
package conversions
import kotlin.native.concurrent.isFrozen
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@@ -236,3 +237,6 @@ fun IC2?.getValueOrNull2() = this?.value
fun IC3.getValue3() = 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)
}
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 --------
class ValuesTests : TestProvider {
@@ -453,6 +474,7 @@ class ValuesTests : TestProvider {
TestCase(name: "TestDataClass", method: withAutorelease(testDataClass)),
TestCase(name: "TestCompanionObj", method: withAutorelease(testCompanionObj)),
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*));
result->typeInfo_ = result;
result->flags_ = 0;
MakeGlobalHash(nullptr, 0, &result->name_);
result->instanceSize_ = superType->instanceSize_;
result->superType_ = superType;
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(
superType->implementedInterfaces_, superType->implementedInterfaces_ + superType->implementedInterfacesCount_