Files
kotlin-fork/kotlin-native/runtime/src/objc/cpp/ObjCExportCollections.mm
T
2024-03-04 20:53:05 +00:00

622 lines
17 KiB
Plaintext

/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Memory.h"
#import "MemorySharedRefs.hpp"
#import "Types.h"
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import <Foundation/Foundation.h>
#import "Exceptions.h"
#import "Runtime.h"
#import "ObjCExport.h"
#import "ObjCExportCollections.h"
#import "ObjCExportPrivate.h"
extern "C" {
// Imports from ObjCExportUtils.kt:
OBJ_GETTER0(Kotlin_NSArrayAsKList_create);
OBJ_GETTER0(Kotlin_NSMutableArrayAsKMutableList_create);
OBJ_GETTER0(Kotlin_NSSetAsKSet_create);
OBJ_GETTER0(Kotlin_NSDictionaryAsKMap_create);
KBoolean Kotlin_Iterator_hasNext(KRef iterator);
OBJ_GETTER(Kotlin_Iterator_next, KRef iterator);
KInt Kotlin_Collection_getSize(KRef collection);
KBoolean Kotlin_Set_contains(KRef set, KRef element);
OBJ_GETTER(Kotlin_Set_getElement, KRef set, KRef element);
OBJ_GETTER(Kotlin_Set_iterator, KRef set);
void Kotlin_MutableCollection_removeObject(KRef collection, KRef element);
void Kotlin_MutableCollection_addObject(KRef list, KRef obj);
OBJ_GETTER(Kotlin_MutableSet_createWithCapacity, KInt capacity);
KInt Kotlin_Map_getSize(KRef map);
KBoolean Kotlin_Map_containsKey(KRef map, KRef key);
OBJ_GETTER(Kotlin_Map_get, KRef map, KRef key);
OBJ_GETTER(Kotlin_Map_keyIterator, KRef map);
OBJ_GETTER(Kotlin_List_get, KRef list, KInt index);
OBJ_GETTER(Kotlin_MutableMap_createWithCapacity, KInt capacity);
void Kotlin_MutableMap_set(KRef map, KRef key, KRef value);
void Kotlin_MutableMap_remove(KRef map, KRef key);
void Kotlin_MutableList_addObjectAtIndex(KRef list, KInt index, KRef obj);
void Kotlin_MutableList_removeObjectAtIndex(KRef list, KInt index);
void Kotlin_MutableList_removeLastObject(KRef list);
void Kotlin_MutableList_setObject(KRef list, KInt index, KRef obj);
} // extern "C"
static inline KInt objCCapacityToKotlin(NSUInteger capacity) {
NSUInteger max = std::numeric_limits<KInt>::max();
return capacity > max ? max : capacity;
}
static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
if (index > std::numeric_limits<KInt>::max()) {
ThrowArrayIndexOutOfBoundsException();
}
return index;
}
// Note: collections can only be iterated on and converted to Kotlin representation
// when they are either frozen or if they are called on the worker that created them.
@interface NSArray (NSArrayToKotlin)
@end
@implementation NSArray (NSArrayToKotlin)
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSArrayAsKList_create, objc_retain(self));
}
-(void)releaseAsAssociatedObject {
objc_release(self);
}
@end
@interface NSMutableArray (NSMutableArrayToKotlin)
@end
@implementation NSMutableArray (NSArrayToKotlin)
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSMutableArrayAsKMutableList_create, objc_retain(self));
}
-(void)releaseAsAssociatedObject {
objc_release(self);
}
@end
@interface NSSet (NSSetToKotlin)
@end
@implementation NSSet (NSSetToKotlin)
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSSetAsKSet_create, objc_retain(self));
}
-(void)releaseAsAssociatedObject {
objc_release(self);
}
@end
@interface NSDictionary (NSDictionaryToKotlin)
@end
@implementation NSDictionary (NSDictionaryToKotlin)
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSDictionaryAsKMap_create, objc_retain(self));
}
-(void)releaseAsAssociatedObject {
objc_release(self);
}
@end
@interface KIteratorAsNSEnumerator : NSEnumerator
@end
@implementation KIteratorAsNSEnumerator {
KRefSharedHolder iteratorHolder;
}
-(void)dealloc {
iteratorHolder.dispose();
[super dealloc];
}
+(id)createWithKIterator:(KRef)iterator {
KIteratorAsNSEnumerator* result = [[[KIteratorAsNSEnumerator alloc] init] autorelease];
result->iteratorHolder.init(iterator);
return result;
}
- (id)nextObject {
kotlin::CalledFromNativeGuard guard;
KRef iterator = iteratorHolder.ref<ErrorPolicy::kTerminate>();
if (Kotlin_Iterator_hasNext(iterator)) {
ObjHolder holder;
return refToObjCOrNSNull(Kotlin_Iterator_next(iterator, holder.slot()));
} else {
return nullptr;
}
}
@end
@interface KListAsNSArray : NSArray
@end
@implementation KListAsNSArray {
KRefSharedHolder listHolder;
}
-(void)dealloc {
listHolder.dispose();
[super dealloc];
}
+(id)createRetainedWithKList:(KRef)list {
KListAsNSArray* result = [[KListAsNSArray alloc] init];
result->listHolder.init(list);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(listHolder.ref<ErrorPolicy::kTerminate>());
}
-(id)objectAtIndex:(NSUInteger)index {
kotlin::CalledFromNativeGuard guard;
ObjHolder kotlinValueHolder;
KRef kotlinValue = Kotlin_List_get(listHolder.ref<ErrorPolicy::kTerminate>(), index, kotlinValueHolder.slot());
return refToObjCOrNSNull(kotlinValue);
}
-(NSUInteger)count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Collection_getSize(listHolder.ref<ErrorPolicy::kTerminate>());
}
@end
@interface KMutableListAsNSMutableArray : NSMutableArray
@end
@implementation KMutableListAsNSMutableArray {
KRefSharedHolder listHolder;
}
-(void)dealloc {
listHolder.dispose();
[super dealloc];
}
+(id)createRetainedWithKList:(KRef)list {
KMutableListAsNSMutableArray* result = [[KMutableListAsNSMutableArray alloc] init];
result->listHolder.init(list);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(listHolder.ref<ErrorPolicy::kTerminate>());
}
-(id)objectAtIndex:(NSUInteger)index {
kotlin::CalledFromNativeGuard guard;
ObjHolder kotlinValueHolder;
KRef kotlinValue = Kotlin_List_get(listHolder.ref<ErrorPolicy::kTerminate>(), index, kotlinValueHolder.slot());
return refToObjCOrNSNull(kotlinValue);
}
-(NSUInteger)count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Collection_getSize(listHolder.ref<ErrorPolicy::kTerminate>());
}
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot());
Kotlin_MutableList_addObjectAtIndex(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index), kotlinObject);
}
- (void)removeObjectAtIndex:(NSUInteger)index {
kotlin::CalledFromNativeGuard guard;
Kotlin_MutableList_removeObjectAtIndex(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index));
}
- (void)addObject:(id)anObject {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
Kotlin_MutableCollection_addObject(listHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot()));
}
- (void)removeLastObject {
kotlin::CalledFromNativeGuard guard;
Kotlin_MutableList_removeLastObject(listHolder.ref<ErrorPolicy::kTerminate>());
}
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot());
Kotlin_MutableList_setObject(listHolder.ref<ErrorPolicy::kTerminate>(), objCIndexToKotlinOrThrow(index), kotlinObject);
}
@end
@interface KSetAsNSSet : NSSet
@end
static inline id KSet_getElement(KRef set, id object) {
if (object == NSNull.null) {
return Kotlin_Set_contains(set, nullptr) ? object : nullptr;
} else {
ObjHolder requestHolder, resultHolder;
KRef request = Kotlin_ObjCExport_refFromObjC(object, requestHolder.slot());
KRef result = Kotlin_Set_getElement(set, request, resultHolder.slot());
// Note: if result is nullptr, then it can't be a null element of the set, because request != nullptr;
// so map nullptr to nullptr:
return Kotlin_ObjCExport_refToObjC(result);
}
}
@implementation KSetAsNSSet {
KRefSharedHolder setHolder;
}
-(void)dealloc {
setHolder.dispose();
[super dealloc];
}
+(id)createRetainedWithKSet:(KRef)set {
KSetAsNSSet* result = [[KSetAsNSSet alloc] init];
result->setHolder.init(set);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(setHolder.ref<ErrorPolicy::kTerminate>());
}
-(NSUInteger) count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Collection_getSize(setHolder.ref<ErrorPolicy::kTerminate>());
}
- (id)member:(id)object {
kotlin::CalledFromNativeGuard guard;
return KSet_getElement(setHolder.ref<ErrorPolicy::kTerminate>(), object);
}
// Not mandatory, just an optimization:
- (BOOL)containsObject:(id)anObject {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
return Kotlin_Set_contains(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot()));
}
- (NSEnumerator*)objectEnumerator {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(setHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())];
}
@end
@interface KotlinMutableSet : NSMutableSet
@end
@implementation KotlinMutableSet {
KRefSharedHolder setHolder;
}
-(instancetype)init {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
ObjHolder holder;
KRef set = Kotlin_MutableSet_createWithCapacity(8, holder.slot());
self->setHolder.init(set);
}
return self;
}
- (instancetype)initWithCapacity:(NSUInteger)numItems {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
ObjHolder holder;
KRef set = Kotlin_MutableSet_createWithCapacity(objCCapacityToKotlin(numItems), holder.slot());
self->setHolder.init(set);
}
return self;
}
// TODO: super class implementation appears to be good enough.
- (instancetype)initWithObjects:(const id _Nonnull [_Nullable])objects count:(NSUInteger)cnt {
if (self = [self initWithCapacity:cnt]) {
for (NSUInteger i = 0; i < cnt; ++i) {
[self addObject:objects[i]];
}
}
return self;
}
// TODO: what about
// - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
// ?
-(void)dealloc {
// Note: since setHolder initialization is not performed directly with alloc,
// it is possible that it wasn't initialized properly.
// Fortunately setHolder.dispose() handles the zero-initialized case too.
setHolder.dispose();
[super dealloc];
}
-(instancetype)initWithKSet:(KRef)set {
if (self = [super init]) {
setHolder.init(set);
}
return self;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(setHolder.ref<ErrorPolicy::kTerminate>());
}
-(NSUInteger) count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Collection_getSize(setHolder.ref<ErrorPolicy::kTerminate>());
}
- (id)member:(id)object {
kotlin::CalledFromNativeGuard guard;
return KSet_getElement(setHolder.ref<ErrorPolicy::kTerminate>(), object);
}
// Not mandatory, just an optimization:
- (BOOL)containsObject:(id)anObject {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
return Kotlin_Set_contains(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(anObject, holder.slot()));
}
- (NSEnumerator*)objectEnumerator {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(setHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())];
}
- (void)addObject:(id)object {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
Kotlin_MutableCollection_addObject(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(object, holder.slot()));
}
- (void)removeObject:(id)object {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
Kotlin_MutableCollection_removeObject(setHolder.ref<ErrorPolicy::kTerminate>(), refFromObjCOrNSNull(object, holder.slot()));
}
@end
@interface KMapAsNSDictionary : NSDictionary
@end
static inline id KMap_get(KRef map, id aKey) {
ObjHolder keyHolder, valueHolder;
KRef kotlinKey = refFromObjCOrNSNull(aKey, keyHolder.slot());
KRef kotlinValue = Kotlin_Map_get(map, kotlinKey, valueHolder.slot());
if (kotlinValue == nullptr) {
// Either null or not found.
return Kotlin_Map_containsKey(map, kotlinKey) ? NSNull.null : nullptr;
} else {
return refToObjCOrNSNull(kotlinValue);
}
}
@implementation KMapAsNSDictionary {
KRefSharedHolder mapHolder;
}
-(void)dealloc {
mapHolder.dispose();
[super dealloc];
}
+(id)createRetainedWithKMap:(KRef)map {
KMapAsNSDictionary* result = [[KMapAsNSDictionary alloc] init];
result->mapHolder.init(map);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(mapHolder.ref<ErrorPolicy::kTerminate>());
}
// According to documentation, initWithObjects:forKeys:count: is required to be overridden when subclassing.
// But that doesn't make any sense, since this class can't be arbitrary initialized.
-(NSUInteger) count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Map_getSize(mapHolder.ref<ErrorPolicy::kTerminate>());
}
- (id)objectForKey:(id)aKey {
kotlin::CalledFromNativeGuard guard;
return KMap_get(mapHolder.ref<ErrorPolicy::kTerminate>(), aKey);
}
- (NSEnumerator *)keyEnumerator {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(mapHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())];
}
@end
@interface KotlinMutableDictionary : NSMutableDictionary
@end
@implementation KotlinMutableDictionary {
KRefSharedHolder mapHolder;
}
-(void)dealloc {
// Note: since mapHolder initialization is not performed directly with alloc,
// it is possible that it wasn't initialized properly.
// Fortunately mapHolder.dispose() handles the zero-initialized case too.
mapHolder.dispose();
[super dealloc];
}
-(instancetype)init {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
ObjHolder holder;
KRef map = Kotlin_MutableMap_createWithCapacity(8, holder.slot());
self->mapHolder.init(map);
}
return self;
}
// 'initWithObjects:forKeys:count:' seems to be implemented in base class.
// TODO: what about
// - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
// ?
- (instancetype)initWithCapacity:(NSUInteger)numItems {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable);
ObjHolder holder;
KRef map = Kotlin_MutableMap_createWithCapacity(objCCapacityToKotlin(numItems), holder.slot());
self->mapHolder.init(map);
}
return self;
}
-(instancetype)initWithKMap:(KRef)map {
if (self = [super init]) {
mapHolder.init(map);
}
return self;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(mapHolder.ref<ErrorPolicy::kTerminate>());
}
-(NSUInteger) count {
kotlin::CalledFromNativeGuard guard;
return Kotlin_Map_getSize(mapHolder.ref<ErrorPolicy::kTerminate>());
}
- (id)objectForKey:(id)aKey {
kotlin::CalledFromNativeGuard guard;
return KMap_get(mapHolder.ref<ErrorPolicy::kTerminate>(), aKey);
}
- (NSEnumerator *)keyEnumerator {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(mapHolder.ref<ErrorPolicy::kTerminate>(), holder.slot())];
}
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey {
id keyCopy = [aKey copyWithZone:nullptr]; // Correspond to the expected NSMutableDictionary behaviour.
{
kotlin::CalledFromNativeGuard guard;
ObjHolder keyHolder, valueHolder;
KRef kotlinKey = refFromObjCOrNSNull(keyCopy, keyHolder.slot());
KRef kotlinValue = refFromObjCOrNSNull(anObject, valueHolder.slot());
Kotlin_MutableMap_set(mapHolder.ref<ErrorPolicy::kTerminate>(), kotlinKey, kotlinValue);
}
objc_release(keyCopy);
}
- (void)removeObjectForKey:(id)aKey {
kotlin::CalledFromNativeGuard guard;
ObjHolder holder;
KRef kotlinKey = refFromObjCOrNSNull(aKey, holder.slot());
Kotlin_MutableMap_remove(mapHolder.ref<ErrorPolicy::kTerminate>(), kotlinKey);
}
@end
@interface NSEnumerator (NSEnumeratorAsAssociatedObject)
@end
@implementation NSEnumerator (NSEnumeratorAsAssociatedObject)
-(void)releaseAsAssociatedObject {
objc_release(self);
}
@end
// Referenced from the generated code:
extern "C" id Kotlin_Interop_CreateRetainedNSArrayFromKList(KRef obj) {
return [KListAsNSArray createRetainedWithKList:obj];
}
extern "C" id Kotlin_Interop_CreateRetainedNSMutableArrayFromKList(KRef obj) {
return [KMutableListAsNSMutableArray createRetainedWithKList:obj];
}
extern "C" id Kotlin_Interop_CreateRetainedNSSetFromKSet(KRef obj) {
return [KSetAsNSSet createRetainedWithKSet:obj];
}
extern "C" id Kotlin_Interop_CreateRetainedKotlinMutableSetFromKSet(KRef obj) {
return [[KotlinMutableSet alloc] initWithKSet:obj];
}
extern "C" id Kotlin_Interop_CreateRetainedNSDictionaryFromKMap(KRef obj) {
return [KMapAsNSDictionary createRetainedWithKMap:obj];
}
extern "C" id Kotlin_Interop_CreateRetainedKotlinMutableDictionaryFromKMap(KRef obj) {
return [[KotlinMutableDictionary alloc] initWithKMap:obj];
}
#endif // KONAN_OBJC_INTEROP