Move all Objective-C global names to objc.bc
This commit is contained in:
committed by
SvyatoslavScherbina
parent
a40604b640
commit
c32fa6313b
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user