Move all Objective-C global names to objc.bc

This commit is contained in:
Svyatoslav Scherbina
2019-10-10 16:01:01 +03:00
committed by SvyatoslavScherbina
parent a40604b640
commit c32fa6313b
7 changed files with 447 additions and 297 deletions
+11
View File
@@ -20,6 +20,17 @@ inline static void SetAssociatedObject(ObjHeader* obj, id value) {
obj->meta_object()->associatedObject_ = (void*)value; obj->meta_object()->associatedObject_ = (void*)value;
} }
inline static id AtomicCompareAndSwapAssociatedObject(ObjHeader* obj, id expectedValue, id newValue) {
id* location = reinterpret_cast<id*>(&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" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj); extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj);
+21 -223
View File
@@ -14,10 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
#import "Atomic.h"
#import "Types.h" #import "Types.h"
#import "Memory.h" #import "Memory.h"
#import "MemorySharedRefs.hpp"
#include "Natives.h" #include "Natives.h"
#if KONAN_OBJC_INTEROP #if KONAN_OBJC_INTEROP
@@ -32,11 +30,13 @@
#import <Foundation/NSException.h> #import <Foundation/NSException.h>
#import <Foundation/NSDecimalNumber.h> #import <Foundation/NSDecimalNumber.h>
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <objc/message.h>
#import <objc/runtime.h> #import <objc/runtime.h>
#import <objc/objc-exception.h> #import <objc/objc-exception.h>
#import <dispatch/dispatch.h> #import <dispatch/dispatch.h>
#import "ObjCExport.h" #import "ObjCExport.h"
#import "ObjCExportPrivate.h"
#import "MemoryPrivate.hpp" #import "MemoryPrivate.hpp"
#import "Runtime.h" #import "Runtime.h"
#import "Utils.h" #import "Utils.h"
@@ -99,7 +99,7 @@ struct WritableTypeInfo {
static char associatedTypeInfoKey; 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]; return (const TypeInfo*)[objc_getAssociatedObject(clazz, &associatedTypeInfoKey) pointerValue];
} }
@@ -111,12 +111,6 @@ extern "C" id Kotlin_ObjCExport_GetAssociatedObject(ObjHeader* obj) {
return GetAssociatedObject(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, extern "C" OBJ_GETTER(Kotlin_ObjCExport_AllocInstanceWithAssociatedObject,
const TypeInfo* typeInfo, id associatedObject) RUNTIME_NOTHROW; const TypeInfo* typeInfo, id associatedObject) RUNTIME_NOTHROW;
@@ -129,122 +123,12 @@ static Class getOrCreateClass(const TypeInfo* typeInfo);
static void initializeClass(Class clazz); static void initializeClass(Class clazz);
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject); extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
static inline id AtomicCompareAndSwapAssociatedObject(ObjHeader* obj, id expectedValue, id newValue) {
id* location = reinterpret_cast<id*>(&obj->meta_object()->associatedObject_);
return __sync_val_compare_and_swap(location, expectedValue, newValue);
}
extern "C" id objc_retainAutoreleaseReturnValue(id self); 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 <ConvertibleToKotlin, NSCopying>
@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) { extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject) {
if (associatedObject != nullptr) { if (associatedObject != nullptr) {
[((id)associatedObject) releaseAsAssociatedObject]; auto msgSend = reinterpret_cast<void (*)(void* self, SEL cmd)>(&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) { 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) { if (candidate != nullptr && (candidate->flags_ & TF_OBJC_DYNAMIC) == 0) {
return candidate; return candidate;
@@ -370,7 +254,7 @@ extern "C" const TypeInfo* Kotlin_ObjCInterop_getTypeInfoForProtocol(Protocol* p
static const TypeInfo* getOrCreateTypeInfo(Class clazz); static const TypeInfo* getOrCreateTypeInfo(Class clazz);
static void initializeClass(Class clazz) { extern "C" void Kotlin_ObjCExport_initializeClass(Class clazz) {
const ObjCTypeAdapter* typeAdapter = findClassAdapter(clazz); const ObjCTypeAdapter* typeAdapter = findClassAdapter(clazz);
if (typeAdapter == nullptr) { if (typeAdapter == nullptr) {
getOrCreateTypeInfo(clazz); 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)); const TypeInfo* typeInfo = getOrCreateTypeInfo(object_getClass(obj));
RETURN_RESULT_OF(AllocInstanceWithAssociatedObject, typeInfo, objc_retain(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(blockToKotlinImp, id self, SEL cmd);
static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd); static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd);
static OBJ_GETTER(SwiftObject_toKotlinImp, id self, SEL cmd); static OBJ_GETTER(SwiftObject_toKotlinImp, id self, SEL cmd);
static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd); static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd);
static void checkLoadedOnce();
static void initTypeAdaptersFrom(const ObjCTypeAdapter** adapters, int count) { static void initTypeAdaptersFrom(const ObjCTypeAdapter** adapters, int count) {
for (int index = 0; index < count; ++index) { for (int index = 0; index < count; ++index) {
const ObjCTypeAdapter* adapter = adapters[index]; const ObjCTypeAdapter* adapter = adapters[index];
@@ -435,15 +321,15 @@ static void initTypeAdapters() {
initTypeAdaptersFrom(Kotlin_ObjCExport_sortedProtocolAdapters, Kotlin_ObjCExport_sortedProtocolAdaptersNum); initTypeAdaptersFrom(Kotlin_ObjCExport_sortedProtocolAdapters, Kotlin_ObjCExport_sortedProtocolAdaptersNum);
} }
static void initializeObjCExport() { extern "C" void Kotlin_ObjCExport_initialize() {
initTypeAdapters(); initTypeAdapters();
SEL toKotlinSelector = @selector(toKotlin:); SEL toKotlinSelector = Kotlin_ObjCExport_toKotlinSelector;
Method toKotlinMethod = class_getClassMethod([NSObject class], toKotlinSelector); Method toKotlinMethod = class_getClassMethod([NSObject class], toKotlinSelector);
RuntimeAssert(toKotlinMethod != nullptr, ""); RuntimeAssert(toKotlinMethod != nullptr, "");
const char* toKotlinTypeEncoding = method_getTypeEncoding(toKotlinMethod); const char* toKotlinTypeEncoding = method_getTypeEncoding(toKotlinMethod);
SEL releaseAsAssociatedObjectSelector = @selector(releaseAsAssociatedObject); SEL releaseAsAssociatedObjectSelector = Kotlin_ObjCExport_releaseAsAssociatedObjectSelector;
Method releaseAsAssociatedObjectMethod = class_getClassMethod([NSObject class], releaseAsAssociatedObjectSelector); Method releaseAsAssociatedObjectMethod = class_getClassMethod([NSObject class], releaseAsAssociatedObjectSelector);
RuntimeAssert(releaseAsAssociatedObjectMethod != nullptr, ""); RuntimeAssert(releaseAsAssociatedObjectMethod != nullptr, "");
const char* releaseAsAssociatedObjectTypeEncoding = method_getTypeEncoding(releaseAsAssociatedObjectMethod); const char* releaseAsAssociatedObjectTypeEncoding = method_getTypeEncoding(releaseAsAssociatedObjectMethod);
@@ -477,99 +363,21 @@ static void initializeObjCExport() {
} }
} }
@interface NSObject (NSObjectToKotlin) <ConvertibleToKotlin>
@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) { 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) { static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd) {
objc_release(self); objc_release(self);
} }
@interface NSString (NSStringToKotlin) <ConvertibleToKotlin>
@end;
@implementation NSString (NSStringToKotlin) extern "C" OBJ_GETTER(Kotlin_boxBoolean, KBoolean value);
-(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);
}
static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd) { static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd) {
RETURN_RESULT_OF(Kotlin_boxBoolean, self.boolValue); RETURN_RESULT_OF(Kotlin_boxBoolean, self.boolValue);
} }
@interface NSNumber (NSNumberToKotlin) <ConvertibleToKotlin>
@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) <ConvertibleToKotlin>
@end;
@implementation NSDecimalNumber (NSDecimalNumberToKotlin)
// Overrides [NSNumber toKotlin:] implementation.
-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT {
RETURN_RESULT_OF(convertUnmappedObjCObject, self);
}
@end;
struct Block_descriptor_1; struct Block_descriptor_1;
// Based on https://clang.llvm.org/docs/Block-ABI-Apple.html and libclosure source. // 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) { extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj) {
if (obj == nullptr) RETURN_OBJ(nullptr); if (obj == nullptr) RETURN_OBJ(nullptr);
id convertible = (id<ConvertibleToKotlin>)obj; auto msgSend = reinterpret_cast<ObjHeader* (*)(id self, SEL cmd, ObjHeader** slot)>(&objc_msgSend);
return [convertible toKotlin:OBJ_RESULT]; RETURN_RESULT_OF(msgSend, obj, Kotlin_ObjCExport_toKotlinSelector);
} }
static id convertKotlinObject(ObjHeader* obj) { static id convertKotlinObject(ObjHeader* obj) {
@@ -1100,7 +908,7 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType) {
static SimpleMutex typeInfoCreationMutex; static SimpleMutex typeInfoCreationMutex;
static const TypeInfo* getOrCreateTypeInfo(Class clazz) { static const TypeInfo* getOrCreateTypeInfo(Class clazz) {
const TypeInfo* result = getAssociatedTypeInfo(clazz); const TypeInfo* result = Kotlin_ObjCExport_getAssociatedTypeInfo(clazz);
if (result != nullptr) { if (result != nullptr) {
return result; return result;
} }
@@ -1113,7 +921,7 @@ static const TypeInfo* getOrCreateTypeInfo(Class clazz) {
LockGuard<SimpleMutex> lockGuard(typeInfoCreationMutex); LockGuard<SimpleMutex> lockGuard(typeInfoCreationMutex);
result = getAssociatedTypeInfo(clazz); // double-checking. result = Kotlin_ObjCExport_getAssociatedTypeInfo(clazz); // double-checking.
if (result == nullptr) { if (result == nullptr) {
result = createTypeInfo(clazz, superType); result = createTypeInfo(clazz, superType);
setAssociatedTypeInfo(clazz, result); 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)]; 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 #else
extern "C" ALWAYS_INLINE void* Kotlin_Interop_refToObjC(ObjHeader* obj) { extern "C" ALWAYS_INLINE void* Kotlin_Interop_refToObjC(ObjHeader* obj) {
+30
View File
@@ -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 <objc/runtime.h>
#import "Types.h"
#import "Memory.h"
#import "ObjCExport.h"
@interface KotlinBase : NSObject <ConvertibleToKotlin, NSCopying>
+(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
+10 -74
View File
@@ -22,7 +22,7 @@
#import <Foundation/NSException.h> #import <Foundation/NSException.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import "Memory.h" #import "Memory.h"
#import "MemorySharedRefs.hpp" #import "ObjCInteropUtilsPrivate.h"
namespace { namespace {
Class nsStringClass = nullptr; Class nsStringClass = nullptr;
@@ -97,49 +97,16 @@ id MissingInitImp(id self, SEL _cmd) {
return nullptr; return nullptr;
} }
// TODO: rework the interface to reduce the number of virtual calls // Initialized in [ObjCInteropUtilsClasses.mm].
// in Kotlin_Interop_createKotlinObjectHolder and Kotlin_Interop_unwrapKotlinObjectHolder id (*Kotlin_Interop_createKotlinObjectHolder_ptr)(KRef any) = nullptr;
@interface KotlinObjectHolder : NSObject KRef (*Kotlin_Interop_unwrapKotlinObjectHolder_ptr)(id holder) = nullptr;
-(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;
id Kotlin_Interop_createKotlinObjectHolder(KRef any) { id Kotlin_Interop_createKotlinObjectHolder(KRef any) {
if (any == nullptr) { return Kotlin_Interop_createKotlinObjectHolder_ptr(any);
return nullptr;
}
return [[[KotlinObjectHolder alloc] initWithRef:any] autorelease];
} }
KRef Kotlin_Interop_unwrapKotlinObjectHolder(id holder) { KRef Kotlin_Interop_unwrapKotlinObjectHolder(id holder) {
if (holder == nullptr) { return Kotlin_Interop_unwrapKotlinObjectHolder_ptr(holder);
return nullptr;
}
return [((KotlinObjectHolder*)holder) ref];
} }
KBoolean Kotlin_Interop_DoesObjectConformToProtocol(id obj, void* prot, KBoolean isMeta) { KBoolean Kotlin_Interop_DoesObjectConformToProtocol(id obj, void* prot, KBoolean isMeta) {
@@ -154,46 +121,15 @@ KBoolean Kotlin_Interop_IsObjectKindOfClass(id obj, void* cls) {
return [((id<NSObject>)obj) isKindOfClass:(Class)cls]; return [((id<NSObject>)obj) isKindOfClass:(Class)cls];
} }
// Used as an associated object for ObjCWeakReferenceImpl. OBJ_GETTER((*Konan_ObjCInterop_getWeakReference_ptr), KRef ref) = nullptr;
@interface KotlinObjCWeakReference : NSObject void (*Konan_ObjCInterop_initWeakReference_ptr)(KRef ref, id objcPtr) = nullptr;
@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, KRef ref) { OBJ_GETTER(Konan_ObjCInterop_getWeakReference, KRef ref) {
MetaObjHeader* meta = ref->meta_object(); RETURN_RESULT_OF(Konan_ObjCInterop_getWeakReference_ptr, ref);
KotlinObjCWeakReference* objcRef = (KotlinObjCWeakReference*)meta->associatedObject_;
id objcReferred = objc_loadWeakRetained(&objcRef->referred);
KRef result = Kotlin_Interop_refFromObjC(objcReferred, OBJ_RESULT);
objc_release(objcReferred);
return result;
} }
void Konan_ObjCInterop_initWeakReference(KRef ref, id objcPtr) { void Konan_ObjCInterop_initWeakReference(KRef ref, id objcPtr) {
MetaObjHeader* meta = ref->meta_object(); Konan_ObjCInterop_initWeakReference_ptr(ref, objcPtr);
KotlinObjCWeakReference* objcRef = [KotlinObjCWeakReference new];
objc_storeWeak(&objcRef->referred, objcPtr);
meta->associatedObject_ = objcRef;
} }
} // extern "C" } // extern "C"
@@ -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 <objc/runtime.h>
#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
+234
View File
@@ -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 <Foundation/NSObject.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSString.h>
#import <Foundation/NSException.h>
#import <Foundation/NSDecimalNumber.h>
#import <objc/runtime.h>
#import <objc/objc-exception.h>
#import <dispatch/dispatch.h>
#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) <ConvertibleToKotlin>
@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) <ConvertibleToKotlin>
@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) <ConvertibleToKotlin>
@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) <ConvertibleToKotlin>
@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
@@ -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 <objc/runtime.h>
#import <Foundation/NSObject.h>
#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