Skip to content

Commit

Permalink
Move all Objective-C global names to objc.bc
Browse files Browse the repository at this point in the history
  • Loading branch information
SvyatoslavScherbina committed Oct 17, 2019
1 parent a40604b commit c32fa63
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 297 deletions.
11 changes: 11 additions & 0 deletions runtime/src/main/cpp/ObjCExport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
244 changes: 21 additions & 223 deletions runtime/src/main/cpp/ObjCExport.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down Expand Up @@ -99,7 +99,7 @@

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];
}

Expand All @@ -111,12 +111,6 @@ static void setAssociatedTypeInfo(Class clazz, const TypeInfo* typeInfo) {
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;

Expand All @@ -129,122 +123,12 @@ inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* type
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);
}
}

Expand Down Expand Up @@ -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;
Expand All @@ -370,7 +254,7 @@ static void addProtocolForInterface(Class clazz, const TypeInfo* interfaceInfo)

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);
Expand Down Expand Up @@ -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];
Expand All @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -694,8 +502,8 @@ static ALWAYS_INLINE id Kotlin_ObjCExport_refToObjCImpl(ObjHeader* 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) {
Expand Down Expand Up @@ -1100,7 +908,7 @@ static void throwIfCantBeOverridden(Class clazz, const KotlinToObjCMethodAdapter
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;
}
Expand All @@ -1113,7 +921,7 @@ static void throwIfCantBeOverridden(Class clazz, const KotlinToObjCMethodAdapter

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);
Expand Down Expand Up @@ -1214,16 +1022,6 @@ static Class getOrCreateClass(const TypeInfo* typeInfo) {
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) {
Expand Down
Loading

0 comments on commit c32fa63

Please sign in to comment.