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;
}
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" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj);
+21 -223
View File
@@ -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 <Foundation/NSException.h>
#import <Foundation/NSDecimalNumber.h>
#import <Foundation/NSDictionary.h>
#import <objc/message.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"
@@ -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<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_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) {
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) {
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) <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) {
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) <ConvertibleToKotlin>
@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) <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;
// 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<ConvertibleToKotlin>)obj;
return [convertible toKotlin:OBJ_RESULT];
auto msgSend = reinterpret_cast<ObjHeader* (*)(id self, SEL cmd, ObjHeader** slot)>(&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<SimpleMutex> 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) {
+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/NSString.h>
#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<NSObject>)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"
@@ -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