blob: 17ce7d202a186e6db3f85258426f2286c8fcbedb [file]
/*
* 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/Foundation.h>
#include "ExternalRCRef.hpp"
#import "Memory.h"
#import "ObjCExportPrivate.h"
#import "ObjCInteropUtilsPrivate.h"
using namespace kotlin;
// 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 {
mm::OwningExternalRCRef refHolder;
};
-(id)initWithRef:(KRef)ref {
if (self = [super init]) {
refHolder.reset(ref);
}
return self;
}
-(KRef)ref {
return refHolder.ref();
}
@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) {
KotlinObjCWeakReference* objcRef = (KotlinObjCWeakReference*)ref->GetAssociatedObject();
// objc_loadWeakRetained can call arbitrary user code, so it needs Native state:
id objcReferred = kotlin::CallWithThreadState<kotlin::ThreadState::kNative>(objc_loadWeakRetained, &objcRef->referred);
// Kotlin_Interop_refFromObjC creates Kotlin objects, so it needs Runnable state:
KRef result = Kotlin_Interop_refFromObjC(objcReferred, OBJ_RESULT);
// objc_release can call arbitrary user code, so it needs Native state:
kotlin::CallWithThreadState<kotlin::ThreadState::kNative>(objc_release, objcReferred);
// Possible optimizations for thread state switching above:
// 1. `Kotlin_Interop_refFromObjC` typically does `objc_retain` under the hood. We could coalesce it with `objc_release` above.
// 2. `objc_loadWeakRetained` and `objc_release` typically shouldn't call arbitrary user code here:
// the latter can't deallocate the object because it is still alive by this point, so the only possibility for user code here is
// when a user overrides `_tryRetain` or `release`.
// We do this for Kotlin subclasses of Obj-C classes, which also use this implementation.
// But we could use the other weak implementation for such classes, and assume no one else overrides these methods
// (in a dangerous way).
return result;
}
static void Konan_ObjCInterop_initWeakReference(KRef ref, id objcPtr) {
KotlinObjCWeakReference* objcRef = [KotlinObjCWeakReference new];
objc_storeWeak(&objcRef->referred, objcPtr);
ref->SetAssociatedObject(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