From c32fa6313b5dba6454fddf6f3a98b5347f8f9759 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Thu, 10 Oct 2019 16:01:01 +0300 Subject: [PATCH] Move all Objective-C global names to objc.bc --- runtime/src/main/cpp/ObjCExport.h | 11 + runtime/src/main/cpp/ObjCExport.mm | 244 ++---------------- runtime/src/main/cpp/ObjCExportPrivate.h | 30 +++ runtime/src/main/cpp/ObjCInteropUtils.mm | 84 +----- .../src/main/cpp/ObjCInteropUtilsPrivate.h | 23 ++ runtime/src/objc/cpp/ObjCExportClasses.mm | 234 +++++++++++++++++ .../src/objc/cpp/ObjCInteropUtilsClasses.mm | 118 +++++++++ 7 files changed, 447 insertions(+), 297 deletions(-) create mode 100644 runtime/src/main/cpp/ObjCExportPrivate.h create mode 100644 runtime/src/main/cpp/ObjCInteropUtilsPrivate.h create mode 100644 runtime/src/objc/cpp/ObjCExportClasses.mm create mode 100644 runtime/src/objc/cpp/ObjCInteropUtilsClasses.mm diff --git a/runtime/src/main/cpp/ObjCExport.h b/runtime/src/main/cpp/ObjCExport.h index c681409dbe4..28c52d73238 100644 --- a/runtime/src/main/cpp/ObjCExport.h +++ b/runtime/src/main/cpp/ObjCExport.h @@ -20,6 +20,17 @@ inline static void SetAssociatedObject(ObjHeader* obj, id value) { obj->meta_object()->associatedObject_ = (void*)value; } +inline static id AtomicCompareAndSwapAssociatedObject(ObjHeader* obj, id expectedValue, id newValue) { + id* location = reinterpret_cast(&obj->meta_object()->associatedObject_); + return __sync_val_compare_and_swap(location, expectedValue, newValue); +} + +inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* typeInfo, id associatedObject) { + ObjHeader* result = AllocInstance(typeInfo, OBJ_RESULT); + SetAssociatedObject(result, associatedObject); + return result; +} + extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj); extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj); diff --git a/runtime/src/main/cpp/ObjCExport.mm b/runtime/src/main/cpp/ObjCExport.mm index 13283d8ee61..ee4ea6c9865 100644 --- a/runtime/src/main/cpp/ObjCExport.mm +++ b/runtime/src/main/cpp/ObjCExport.mm @@ -14,10 +14,8 @@ * limitations under the License. */ -#import "Atomic.h" #import "Types.h" #import "Memory.h" -#import "MemorySharedRefs.hpp" #include "Natives.h" #if KONAN_OBJC_INTEROP @@ -32,11 +30,13 @@ #import #import #import +#import #import #import #import #import "ObjCExport.h" +#import "ObjCExportPrivate.h" #import "MemoryPrivate.hpp" #import "Runtime.h" #import "Utils.h" @@ -99,7 +99,7 @@ struct WritableTypeInfo { static char associatedTypeInfoKey; -static const TypeInfo* getAssociatedTypeInfo(Class clazz) { +extern "C" const TypeInfo* Kotlin_ObjCExport_getAssociatedTypeInfo(Class clazz) { return (const TypeInfo*)[objc_getAssociatedObject(clazz, &associatedTypeInfoKey) pointerValue]; } @@ -111,12 +111,6 @@ extern "C" id Kotlin_ObjCExport_GetAssociatedObject(ObjHeader* obj) { return GetAssociatedObject(obj); } -inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* typeInfo, id associatedObject) { - ObjHeader* result = AllocInstance(typeInfo, OBJ_RESULT); - SetAssociatedObject(result, associatedObject); - return result; -} - extern "C" OBJ_GETTER(Kotlin_ObjCExport_AllocInstanceWithAssociatedObject, const TypeInfo* typeInfo, id associatedObject) RUNTIME_NOTHROW; @@ -129,122 +123,12 @@ static Class getOrCreateClass(const TypeInfo* typeInfo); static void initializeClass(Class clazz); extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject); -static inline id AtomicCompareAndSwapAssociatedObject(ObjHeader* obj, id expectedValue, id newValue) { - id* location = reinterpret_cast(&obj->meta_object()->associatedObject_); - return __sync_val_compare_and_swap(location, expectedValue, newValue); -} - extern "C" id objc_retainAutoreleaseReturnValue(id self); -extern "C" id objc_autoreleaseReturnValue(id self); - -@interface NSObject (NSObjectPrivateMethods) -// Implemented for NSObject in libobjc/NSObject.mm --(BOOL)_tryRetain; -@end; - -@interface KotlinBase : NSObject -@end; - -static void initializeObjCExport(); - -@implementation KotlinBase { - BackRefFromAssociatedObject refHolder; -} - --(KRef)toKotlin:(KRef*)OBJ_RESULT { - RETURN_OBJ(refHolder.ref()); -} - -+(void)initialize { - if (self == [KotlinBase class]) { - initializeObjCExport(); - } - initializeClass(self); -} - -+(instancetype)allocWithZone:(NSZone*)zone { - Kotlin_initRuntimeIfNeeded(); - - KotlinBase* result = [super allocWithZone:zone]; - - const TypeInfo* typeInfo = getAssociatedTypeInfo(self); - if (typeInfo == nullptr) { - [NSException raise:NSGenericException - format:@"%s is not allocatable or +[KotlinBase initialize] method wasn't called on it", - class_getName(object_getClass(self))]; - } - - if (typeInfo->instanceSize_ < 0) { - [NSException raise:NSGenericException - format:@"%s must be allocated and initialized with a factory method", - class_getName(object_getClass(self))]; - } - ObjHolder holder; - AllocInstanceWithAssociatedObject(typeInfo, result, holder.slot()); - result->refHolder.initAndAddRef(holder.obj()); - return result; -} - -+(instancetype)createWrapper:(ObjHeader*)obj { - KotlinBase* candidate = [super allocWithZone:nil]; - // TODO: should we call NSObject.init ? - candidate->refHolder.initAndAddRef(obj); - - if (!obj->permanent()) { // TODO: permanent objects should probably be supported as custom types. - if (!obj->container()->shareable()) { - SetAssociatedObject(obj, candidate); - } else { - id old = AtomicCompareAndSwapAssociatedObject(obj, nullptr, candidate); - if (old != nullptr) { - candidate->refHolder.releaseRef(); - [candidate releaseAsAssociatedObject]; - return objc_retainAutoreleaseReturnValue(old); - } - } - } - - return objc_autoreleaseReturnValue(candidate); -} - --(instancetype)retain { - if (refHolder.permanent()) { // TODO: consider storing `isPermanent` to self field. - [super retain]; - } else { - refHolder.addRef(); - } - return self; -} - --(BOOL)_tryRetain { - if (refHolder.permanent()) { - return [super _tryRetain]; - } else { - return refHolder.tryAddRef(); - } -} - --(oneway void)release { - if (refHolder.permanent()) { - [super release]; - } else { - refHolder.releaseRef(); - } -} - --(void)releaseAsAssociatedObject { - [super release]; -} - -- (instancetype)copyWithZone:(NSZone *)zone { - // TODO: write documentation. - return [self retain]; -} - -@end; extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject) { if (associatedObject != nullptr) { - [((id)associatedObject) releaseAsAssociatedObject]; + auto msgSend = reinterpret_cast(&objc_msgSend); + msgSend(associatedObject, Kotlin_ObjCExport_releaseAsAssociatedObjectSelector); } } @@ -353,7 +237,7 @@ static void addProtocolForInterface(Class clazz, const TypeInfo* interfaceInfo) } extern "C" const TypeInfo* Kotlin_ObjCInterop_getTypeInfoForClass(Class clazz) { - const TypeInfo* candidate = getAssociatedTypeInfo(clazz); + const TypeInfo* candidate = Kotlin_ObjCExport_getAssociatedTypeInfo(clazz); if (candidate != nullptr && (candidate->flags_ & TF_OBJC_DYNAMIC) == 0) { return candidate; @@ -370,7 +254,7 @@ extern "C" const TypeInfo* Kotlin_ObjCInterop_getTypeInfoForProtocol(Protocol* p static const TypeInfo* getOrCreateTypeInfo(Class clazz); -static void initializeClass(Class clazz) { +extern "C" void Kotlin_ObjCExport_initializeClass(Class clazz) { const ObjCTypeAdapter* typeAdapter = findClassAdapter(clazz); if (typeAdapter == nullptr) { getOrCreateTypeInfo(clazz); @@ -405,19 +289,21 @@ static void initializeClass(Class clazz) { } -static ALWAYS_INLINE OBJ_GETTER(convertUnmappedObjCObject, id obj) { +extern "C" ALWAYS_INLINE OBJ_GETTER(Kotlin_ObjCExport_convertUnmappedObjCObject, id obj) { const TypeInfo* typeInfo = getOrCreateTypeInfo(object_getClass(obj)); RETURN_RESULT_OF(AllocInstanceWithAssociatedObject, typeInfo, objc_retain(obj)); } +// Initialized by [ObjCExportClasses.mm]. +extern "C" SEL Kotlin_ObjCExport_toKotlinSelector = nullptr; +extern "C" SEL Kotlin_ObjCExport_releaseAsAssociatedObjectSelector = nullptr; + static OBJ_GETTER(blockToKotlinImp, id self, SEL cmd); static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd); static OBJ_GETTER(SwiftObject_toKotlinImp, id self, SEL cmd); static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd); -static void checkLoadedOnce(); - static void initTypeAdaptersFrom(const ObjCTypeAdapter** adapters, int count) { for (int index = 0; index < count; ++index) { const ObjCTypeAdapter* adapter = adapters[index]; @@ -435,15 +321,15 @@ static void initTypeAdapters() { initTypeAdaptersFrom(Kotlin_ObjCExport_sortedProtocolAdapters, Kotlin_ObjCExport_sortedProtocolAdaptersNum); } -static void initializeObjCExport() { +extern "C" void Kotlin_ObjCExport_initialize() { initTypeAdapters(); - SEL toKotlinSelector = @selector(toKotlin:); + SEL toKotlinSelector = Kotlin_ObjCExport_toKotlinSelector; Method toKotlinMethod = class_getClassMethod([NSObject class], toKotlinSelector); RuntimeAssert(toKotlinMethod != nullptr, ""); const char* toKotlinTypeEncoding = method_getTypeEncoding(toKotlinMethod); - SEL releaseAsAssociatedObjectSelector = @selector(releaseAsAssociatedObject); + SEL releaseAsAssociatedObjectSelector = Kotlin_ObjCExport_releaseAsAssociatedObjectSelector; Method releaseAsAssociatedObjectMethod = class_getClassMethod([NSObject class], releaseAsAssociatedObjectSelector); RuntimeAssert(releaseAsAssociatedObjectMethod != nullptr, ""); const char* releaseAsAssociatedObjectTypeEncoding = method_getTypeEncoding(releaseAsAssociatedObjectMethod); @@ -477,99 +363,21 @@ static void initializeObjCExport() { } } -@interface NSObject (NSObjectToKotlin) -@end; - -@implementation NSObject (NSObjectToKotlin) --(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT { - RETURN_RESULT_OF(convertUnmappedObjCObject, self); -} - --(void)releaseAsAssociatedObject { - objc_release(self); -} - -+(void)load { - static dispatch_once_t onceToken = 0; - dispatch_once(&onceToken, ^{ - checkLoadedOnce(); - }); -} -@end; - static OBJ_GETTER(SwiftObject_toKotlinImp, id self, SEL cmd) { - RETURN_RESULT_OF(convertUnmappedObjCObject, self); + RETURN_RESULT_OF(Kotlin_ObjCExport_convertUnmappedObjCObject, self); } static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd) { objc_release(self); } -@interface NSString (NSStringToKotlin) -@end; -@implementation NSString (NSStringToKotlin) --(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT { - RETURN_RESULT_OF(Kotlin_Interop_CreateKStringFromNSString, self); -} -@end; - -extern "C" { - -OBJ_GETTER(Kotlin_boxBoolean, KBoolean value); -OBJ_GETTER(Kotlin_boxByte, KByte value); -OBJ_GETTER(Kotlin_boxShort, KShort value); -OBJ_GETTER(Kotlin_boxInt, KInt value); -OBJ_GETTER(Kotlin_boxLong, KLong value); -OBJ_GETTER(Kotlin_boxUByte, KUByte value); -OBJ_GETTER(Kotlin_boxUShort, KUShort value); -OBJ_GETTER(Kotlin_boxUInt, KUInt value); -OBJ_GETTER(Kotlin_boxULong, KULong value); -OBJ_GETTER(Kotlin_boxFloat, KFloat value); -OBJ_GETTER(Kotlin_boxDouble, KDouble value); - -} +extern "C" OBJ_GETTER(Kotlin_boxBoolean, KBoolean value); static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd) { RETURN_RESULT_OF(Kotlin_boxBoolean, self.boolValue); } -@interface NSNumber (NSNumberToKotlin) -@end; - -@implementation NSNumber (NSNumberToKotlin) --(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT { - const char* type = self.objCType; - - // TODO: the code below makes some assumption on char, short, int and long sizes. - - switch (type[0]) { - case 'c': RETURN_RESULT_OF(Kotlin_boxByte, self.charValue); - case 's': RETURN_RESULT_OF(Kotlin_boxShort, self.shortValue); - case 'i': RETURN_RESULT_OF(Kotlin_boxInt, self.intValue); - case 'q': RETURN_RESULT_OF(Kotlin_boxLong, self.longLongValue); - case 'C': RETURN_RESULT_OF(Kotlin_boxUByte, self.unsignedCharValue); - case 'S': RETURN_RESULT_OF(Kotlin_boxUShort, self.unsignedShortValue); - case 'I': RETURN_RESULT_OF(Kotlin_boxUInt, self.unsignedIntValue); - case 'Q': RETURN_RESULT_OF(Kotlin_boxULong, self.unsignedLongLongValue); - case 'f': RETURN_RESULT_OF(Kotlin_boxFloat, self.floatValue); - case 'd': RETURN_RESULT_OF(Kotlin_boxDouble, self.doubleValue); - - default: RETURN_RESULT_OF(convertUnmappedObjCObject, self); - } -} -@end; - -@interface NSDecimalNumber (NSDecimalNumberToKotlin) -@end; - -@implementation NSDecimalNumber (NSDecimalNumberToKotlin) -// Overrides [NSNumber toKotlin:] implementation. --(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT { - RETURN_RESULT_OF(convertUnmappedObjCObject, self); -} -@end; - struct Block_descriptor_1; // Based on https://clang.llvm.org/docs/Block-ABI-Apple.html and libclosure source. @@ -694,8 +502,8 @@ extern "C" OBJ_GETTER(Kotlin_Interop_CreateObjCObjectHolder, id obj) { extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj) { if (obj == nullptr) RETURN_OBJ(nullptr); - id convertible = (id)obj; - return [convertible toKotlin:OBJ_RESULT]; + auto msgSend = reinterpret_cast(&objc_msgSend); + RETURN_RESULT_OF(msgSend, obj, Kotlin_ObjCExport_toKotlinSelector); } static id convertKotlinObject(ObjHeader* obj) { @@ -1100,7 +908,7 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType) { static SimpleMutex typeInfoCreationMutex; static const TypeInfo* getOrCreateTypeInfo(Class clazz) { - const TypeInfo* result = getAssociatedTypeInfo(clazz); + const TypeInfo* result = Kotlin_ObjCExport_getAssociatedTypeInfo(clazz); if (result != nullptr) { return result; } @@ -1113,7 +921,7 @@ static const TypeInfo* getOrCreateTypeInfo(Class clazz) { LockGuard lockGuard(typeInfoCreationMutex); - result = getAssociatedTypeInfo(clazz); // double-checking. + result = Kotlin_ObjCExport_getAssociatedTypeInfo(clazz); // double-checking. if (result == nullptr) { result = createTypeInfo(clazz, superType); setAssociatedTypeInfo(clazz, result); @@ -1214,16 +1022,6 @@ extern "C" void Kotlin_ObjCExport_AbstractMethodCalled(id self, SEL selector) { class_getName(object_getClass(self)), sel_getName(selector)]; } -static void checkLoadedOnce() { - Class marker = objc_allocateClassPair([NSObject class], "KotlinFrameworkLoadedOnceMarker", 0); - if (marker == nullptr) { - [NSException raise:NSGenericException - format:@"Only one Kotlin framework can be loaded currently"]; - } else { - objc_registerClassPair(marker); - } -} - #else extern "C" ALWAYS_INLINE void* Kotlin_Interop_refToObjC(ObjHeader* obj) { diff --git a/runtime/src/main/cpp/ObjCExportPrivate.h b/runtime/src/main/cpp/ObjCExportPrivate.h new file mode 100644 index 00000000000..37f9f1509bb --- /dev/null +++ b/runtime/src/main/cpp/ObjCExportPrivate.h @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#ifndef RUNTIME_OBJCEXPORTPRIVATE_H +#define RUNTIME_OBJCEXPORTPRIVATE_H + +#if KONAN_OBJC_INTEROP + +#import + +#import "Types.h" +#import "Memory.h" +#import "ObjCExport.h" + +@interface KotlinBase : NSObject ++(instancetype)createWrapper:(ObjHeader*)obj; +@end; + +extern "C" void Kotlin_ObjCExport_initializeClass(Class clazz); +extern "C" void Kotlin_ObjCExport_initialize(void); +extern "C" const TypeInfo* Kotlin_ObjCExport_getAssociatedTypeInfo(Class clazz); +extern "C" OBJ_GETTER(Kotlin_ObjCExport_convertUnmappedObjCObject, id obj); +extern "C" SEL Kotlin_ObjCExport_toKotlinSelector; +extern "C" SEL Kotlin_ObjCExport_releaseAsAssociatedObjectSelector; + +#endif // KONAN_OBJC_INTEROP + +#endif // RUNTIME_OBJCEXPORTPRIVATE_H diff --git a/runtime/src/main/cpp/ObjCInteropUtils.mm b/runtime/src/main/cpp/ObjCInteropUtils.mm index 7cc4c5395f4..e1eeaf86951 100644 --- a/runtime/src/main/cpp/ObjCInteropUtils.mm +++ b/runtime/src/main/cpp/ObjCInteropUtils.mm @@ -22,7 +22,7 @@ #import #import #import "Memory.h" -#import "MemorySharedRefs.hpp" +#import "ObjCInteropUtilsPrivate.h" namespace { Class nsStringClass = nullptr; @@ -97,49 +97,16 @@ id MissingInitImp(id self, SEL _cmd) { return nullptr; } -// TODO: rework the interface to reduce the number of virtual calls -// in Kotlin_Interop_createKotlinObjectHolder and Kotlin_Interop_unwrapKotlinObjectHolder -@interface KotlinObjectHolder : NSObject --(id)initWithRef:(KRef)ref; --(KRef)ref; -@end; - -@implementation KotlinObjectHolder { - KRefSharedHolder refHolder; -}; - --(id)initWithRef:(KRef)ref { - if (self = [super init]) { - refHolder.init(ref); - } - return self; -} - --(KRef)ref { - return refHolder.ref(); -} - --(void)dealloc { - refHolder.dispose(); - [super dealloc]; -} - -@end; +// Initialized in [ObjCInteropUtilsClasses.mm]. +id (*Kotlin_Interop_createKotlinObjectHolder_ptr)(KRef any) = nullptr; +KRef (*Kotlin_Interop_unwrapKotlinObjectHolder_ptr)(id holder) = nullptr; id Kotlin_Interop_createKotlinObjectHolder(KRef any) { - if (any == nullptr) { - return nullptr; - } - - return [[[KotlinObjectHolder alloc] initWithRef:any] autorelease]; + return Kotlin_Interop_createKotlinObjectHolder_ptr(any); } KRef Kotlin_Interop_unwrapKotlinObjectHolder(id holder) { - if (holder == nullptr) { - return nullptr; - } - - return [((KotlinObjectHolder*)holder) ref]; + return Kotlin_Interop_unwrapKotlinObjectHolder_ptr(holder); } KBoolean Kotlin_Interop_DoesObjectConformToProtocol(id obj, void* prot, KBoolean isMeta) { @@ -154,46 +121,15 @@ KBoolean Kotlin_Interop_IsObjectKindOfClass(id obj, void* cls) { return [((id)obj) isKindOfClass:(Class)cls]; } -// Used as an associated object for ObjCWeakReferenceImpl. -@interface KotlinObjCWeakReference : NSObject -@end; - -// libobjc: -id objc_loadWeakRetained(id *location); -id objc_storeWeak(id *location, id newObj); -void objc_destroyWeak(id *location); -void objc_release(id obj); - -@implementation KotlinObjCWeakReference { - @public id referred; -} - -// Called when removing Kotlin object. --(void)releaseAsAssociatedObject { - objc_destroyWeak(&referred); - objc_release(self); -} - -@end; - -OBJ_GETTER(Kotlin_Interop_refFromObjC, id obj); +OBJ_GETTER((*Konan_ObjCInterop_getWeakReference_ptr), KRef ref) = nullptr; +void (*Konan_ObjCInterop_initWeakReference_ptr)(KRef ref, id objcPtr) = nullptr; OBJ_GETTER(Konan_ObjCInterop_getWeakReference, KRef ref) { - MetaObjHeader* meta = ref->meta_object(); - KotlinObjCWeakReference* objcRef = (KotlinObjCWeakReference*)meta->associatedObject_; - - id objcReferred = objc_loadWeakRetained(&objcRef->referred); - KRef result = Kotlin_Interop_refFromObjC(objcReferred, OBJ_RESULT); - objc_release(objcReferred); - - return result; + RETURN_RESULT_OF(Konan_ObjCInterop_getWeakReference_ptr, ref); } void Konan_ObjCInterop_initWeakReference(KRef ref, id objcPtr) { - MetaObjHeader* meta = ref->meta_object(); - KotlinObjCWeakReference* objcRef = [KotlinObjCWeakReference new]; - objc_storeWeak(&objcRef->referred, objcPtr); - meta->associatedObject_ = objcRef; + Konan_ObjCInterop_initWeakReference_ptr(ref, objcPtr); } } // extern "C" diff --git a/runtime/src/main/cpp/ObjCInteropUtilsPrivate.h b/runtime/src/main/cpp/ObjCInteropUtilsPrivate.h new file mode 100644 index 00000000000..e5081fa4e15 --- /dev/null +++ b/runtime/src/main/cpp/ObjCInteropUtilsPrivate.h @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#ifndef RUNTIME_OBJCINTEROPUTILSPRIVATE_H +#define RUNTIME_OBJCINTEROPUTILSPRIVATE_H + +#if KONAN_OBJC_INTEROP + +#import + +#import "Types.h" +#import "Memory.h" + +extern "C" id (*Kotlin_Interop_createKotlinObjectHolder_ptr)(KRef any); +extern "C" KRef (*Kotlin_Interop_unwrapKotlinObjectHolder_ptr)(id holder); +extern "C" OBJ_GETTER((*Konan_ObjCInterop_getWeakReference_ptr), KRef ref); +extern "C" void (*Konan_ObjCInterop_initWeakReference_ptr)(KRef ref, id objcPtr); + +#endif // KONAN_OBJC_INTEROP + +#endif // RUNTIME_OBJCINTEROPUTILSPRIVATE_H \ No newline at end of file diff --git a/runtime/src/objc/cpp/ObjCExportClasses.mm b/runtime/src/objc/cpp/ObjCExportClasses.mm new file mode 100644 index 00000000000..b754d51ec69 --- /dev/null +++ b/runtime/src/objc/cpp/ObjCExportClasses.mm @@ -0,0 +1,234 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#import "Types.h" +#import "Memory.h" +#import "MemorySharedRefs.hpp" + +#if KONAN_OBJC_INTEROP + +#define OBJC_OLD_DISPATCH_PROTOTYPES 1 + +#import +#import +#import +#import +#import +#import +#import +#import + +#import "ObjCExport.h" +#import "ObjCExportPrivate.h" +#import "MemoryPrivate.hpp" +#import "Runtime.h" +#import "Utils.h" +#import "Exceptions.h" + +extern "C" id objc_retainAutoreleaseReturnValue(id self); +extern "C" id objc_autoreleaseReturnValue(id self); + +@interface NSObject (NSObjectPrivateMethods) +// Implemented for NSObject in libobjc/NSObject.mm +-(BOOL)_tryRetain; +@end; + +@implementation KotlinBase { + BackRefFromAssociatedObject refHolder; +} + +-(KRef)toKotlin:(KRef*)OBJ_RESULT { + RETURN_OBJ(refHolder.ref()); +} + ++(void)initialize { + if (self == [KotlinBase class]) { + Kotlin_ObjCExport_initialize(); + } + Kotlin_ObjCExport_initializeClass(self); +} + ++(instancetype)allocWithZone:(NSZone*)zone { + Kotlin_initRuntimeIfNeeded(); + + KotlinBase* result = [super allocWithZone:zone]; + + const TypeInfo* typeInfo = Kotlin_ObjCExport_getAssociatedTypeInfo(self); + if (typeInfo == nullptr) { + [NSException raise:NSGenericException + format:@"%s is not allocatable or +[KotlinBase initialize] method wasn't called on it", + class_getName(object_getClass(self))]; + } + + if (typeInfo->instanceSize_ < 0) { + [NSException raise:NSGenericException + format:@"%s must be allocated and initialized with a factory method", + class_getName(object_getClass(self))]; + } + ObjHolder holder; + AllocInstanceWithAssociatedObject(typeInfo, result, holder.slot()); + result->refHolder.initAndAddRef(holder.obj()); + return result; +} + ++(instancetype)createWrapper:(ObjHeader*)obj { + KotlinBase* candidate = [super allocWithZone:nil]; + // TODO: should we call NSObject.init ? + candidate->refHolder.initAndAddRef(obj); + + if (!obj->permanent()) { // TODO: permanent objects should probably be supported as custom types. + if (!obj->container()->shareable()) { + SetAssociatedObject(obj, candidate); + } else { + id old = AtomicCompareAndSwapAssociatedObject(obj, nullptr, candidate); + if (old != nullptr) { + candidate->refHolder.releaseRef(); + [candidate releaseAsAssociatedObject]; + return objc_retainAutoreleaseReturnValue(old); + } + } + } + + return objc_autoreleaseReturnValue(candidate); +} + +-(instancetype)retain { + if (refHolder.permanent()) { // TODO: consider storing `isPermanent` to self field. + [super retain]; + } else { + refHolder.addRef(); + } + return self; +} + +-(BOOL)_tryRetain { + if (refHolder.permanent()) { + return [super _tryRetain]; + } else { + return refHolder.tryAddRef(); + } +} + +-(oneway void)release { + if (refHolder.permanent()) { + [super release]; + } else { + refHolder.releaseRef(); + } +} + +-(void)releaseAsAssociatedObject { + [super release]; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + // TODO: write documentation. + return [self retain]; +} + +@end; + +@interface NSObject (NSObjectToKotlin) +@end; + +static void checkLoadedOnce(); + +@implementation NSObject (NSObjectToKotlin) +-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT { + RETURN_RESULT_OF(Kotlin_ObjCExport_convertUnmappedObjCObject, self); +} + +-(void)releaseAsAssociatedObject { + objc_release(self); +} + ++(void)load { + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + checkLoadedOnce(); + }); +} +@end; + +@interface NSString (NSStringToKotlin) +@end; + +@implementation NSString (NSStringToKotlin) +-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT { + RETURN_RESULT_OF(Kotlin_Interop_CreateKStringFromNSString, self); +} +@end; + +extern "C" { + +OBJ_GETTER(Kotlin_boxByte, KByte value); +OBJ_GETTER(Kotlin_boxShort, KShort value); +OBJ_GETTER(Kotlin_boxInt, KInt value); +OBJ_GETTER(Kotlin_boxLong, KLong value); +OBJ_GETTER(Kotlin_boxUByte, KUByte value); +OBJ_GETTER(Kotlin_boxUShort, KUShort value); +OBJ_GETTER(Kotlin_boxUInt, KUInt value); +OBJ_GETTER(Kotlin_boxULong, KULong value); +OBJ_GETTER(Kotlin_boxFloat, KFloat value); +OBJ_GETTER(Kotlin_boxDouble, KDouble value); + +} + +@interface NSNumber (NSNumberToKotlin) +@end; + +@implementation NSNumber (NSNumberToKotlin) +-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT { + const char* type = self.objCType; + + // TODO: the code below makes some assumption on char, short, int and long sizes. + + switch (type[0]) { + case 'c': RETURN_RESULT_OF(Kotlin_boxByte, self.charValue); + case 's': RETURN_RESULT_OF(Kotlin_boxShort, self.shortValue); + case 'i': RETURN_RESULT_OF(Kotlin_boxInt, self.intValue); + case 'q': RETURN_RESULT_OF(Kotlin_boxLong, self.longLongValue); + case 'C': RETURN_RESULT_OF(Kotlin_boxUByte, self.unsignedCharValue); + case 'S': RETURN_RESULT_OF(Kotlin_boxUShort, self.unsignedShortValue); + case 'I': RETURN_RESULT_OF(Kotlin_boxUInt, self.unsignedIntValue); + case 'Q': RETURN_RESULT_OF(Kotlin_boxULong, self.unsignedLongLongValue); + case 'f': RETURN_RESULT_OF(Kotlin_boxFloat, self.floatValue); + case 'd': RETURN_RESULT_OF(Kotlin_boxDouble, self.doubleValue); + + default: RETURN_RESULT_OF(Kotlin_ObjCExport_convertUnmappedObjCObject, self); + } +} +@end; + +@interface NSDecimalNumber (NSDecimalNumberToKotlin) +@end; + +@implementation NSDecimalNumber (NSDecimalNumberToKotlin) +// Overrides [NSNumber toKotlin:] implementation. +-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT { + RETURN_RESULT_OF(Kotlin_ObjCExport_convertUnmappedObjCObject, self); +} +@end; + +static void checkLoadedOnce() { + Class marker = objc_allocateClassPair([NSObject class], "KotlinFrameworkLoadedOnceMarker", 0); + if (marker == nullptr) { + [NSException raise:NSGenericException + format:@"Only one Kotlin framework can be loaded currently"]; + } else { + objc_registerClassPair(marker); + } +} + +__attribute__((constructor)) +static void injectToRuntime() { + RuntimeCheck(Kotlin_ObjCExport_toKotlinSelector == nullptr, "runtime injected twice"); + Kotlin_ObjCExport_toKotlinSelector = @selector(toKotlin:); + + RuntimeCheck(Kotlin_ObjCExport_releaseAsAssociatedObjectSelector == nullptr, "runtime injected twice"); + Kotlin_ObjCExport_releaseAsAssociatedObjectSelector = @selector(releaseAsAssociatedObject); +} + +#endif // KONAN_OBJC_INTEROP diff --git a/runtime/src/objc/cpp/ObjCInteropUtilsClasses.mm b/runtime/src/objc/cpp/ObjCInteropUtilsClasses.mm new file mode 100644 index 00000000000..796bc8c67ea --- /dev/null +++ b/runtime/src/objc/cpp/ObjCInteropUtilsClasses.mm @@ -0,0 +1,118 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#if KONAN_OBJC_INTEROP + +#import +#import +#import "Memory.h" +#import "MemorySharedRefs.hpp" +#import "ObjCInteropUtilsPrivate.h" + +// TODO: rework the interface to reduce the number of virtual calls +// in Kotlin_Interop_createKotlinObjectHolder and Kotlin_Interop_unwrapKotlinObjectHolder +@interface KotlinObjectHolder : NSObject +-(id)initWithRef:(KRef)ref; +-(KRef)ref; +@end; + +@implementation KotlinObjectHolder { + KRefSharedHolder refHolder; +}; + +-(id)initWithRef:(KRef)ref { + if (self = [super init]) { + refHolder.init(ref); + } + return self; +} + +-(KRef)ref { + return refHolder.ref(); +} + +-(void)dealloc { + refHolder.dispose(); + [super dealloc]; +} + +@end; + +static id Kotlin_Interop_createKotlinObjectHolder(KRef any) { + if (any == nullptr) { + return nullptr; + } + + return [[[KotlinObjectHolder alloc] initWithRef:any] autorelease]; +} + +static KRef Kotlin_Interop_unwrapKotlinObjectHolder(id holder) { + if (holder == nullptr) { + return nullptr; + } + + return [((KotlinObjectHolder*)holder) ref]; +} + +// Used as an associated object for ObjCWeakReferenceImpl. +@interface KotlinObjCWeakReference : NSObject +@end; + +// libobjc: +extern "C" { +id objc_loadWeakRetained(id *location); +id objc_storeWeak(id *location, id newObj); +void objc_destroyWeak(id *location); +void objc_release(id obj); +} + +@implementation KotlinObjCWeakReference { + @public id referred; +} + +// Called when removing Kotlin object. +-(void)releaseAsAssociatedObject { + objc_destroyWeak(&referred); + objc_release(self); +} + +@end; + +extern "C" OBJ_GETTER(Kotlin_Interop_refFromObjC, id obj); + +static OBJ_GETTER(Konan_ObjCInterop_getWeakReference, KRef ref) { + MetaObjHeader* meta = ref->meta_object(); + KotlinObjCWeakReference* objcRef = (KotlinObjCWeakReference*)meta->associatedObject_; + + id objcReferred = objc_loadWeakRetained(&objcRef->referred); + KRef result = Kotlin_Interop_refFromObjC(objcReferred, OBJ_RESULT); + objc_release(objcReferred); + + return result; +} + +static void Konan_ObjCInterop_initWeakReference(KRef ref, id objcPtr) { + MetaObjHeader* meta = ref->meta_object(); + KotlinObjCWeakReference* objcRef = [KotlinObjCWeakReference new]; + objc_storeWeak(&objcRef->referred, objcPtr); + meta->associatedObject_ = objcRef; +} + +__attribute__((constructor)) +static void injectToRuntime() { + RuntimeCheck(Kotlin_Interop_createKotlinObjectHolder_ptr == nullptr, "runtime injected twice"); + Kotlin_Interop_createKotlinObjectHolder_ptr = &Kotlin_Interop_createKotlinObjectHolder; + + RuntimeCheck(Kotlin_Interop_unwrapKotlinObjectHolder_ptr == nullptr, "runtime injected twice"); + Kotlin_Interop_unwrapKotlinObjectHolder_ptr = &Kotlin_Interop_unwrapKotlinObjectHolder; + + RuntimeCheck(Konan_ObjCInterop_getWeakReference_ptr == nullptr, "runtime injected twice"); + Konan_ObjCInterop_getWeakReference_ptr = &Konan_ObjCInterop_getWeakReference; + + RuntimeCheck(Konan_ObjCInterop_initWeakReference_ptr == nullptr, "runtime injected twice"); + Konan_ObjCInterop_initWeakReference_ptr = &Konan_ObjCInterop_initWeakReference; +} + +#endif // KONAN_OBJC_INTEROP