Implement -produce framework option
It compiles Kotlin code to Objective-C framework (also importable to Swift)
This commit is contained in:
committed by
SvyatoslavScherbina
parent
6114a5b35c
commit
d745e80135
@@ -33,6 +33,11 @@ inline void konanFreeMemory(void* memory) {
|
||||
konan::free(memory);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T* konanAllocArray(size_t length) {
|
||||
return reinterpret_cast<T*>(konanAllocMemory(length * sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename T, typename ...A>
|
||||
inline T* konanConstructInstance(A&& ...args) {
|
||||
return new (konanAllocMemory(sizeof(T))) T(::std::forward<A>(args)...);
|
||||
|
||||
@@ -30,4 +30,9 @@
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
#if KONAN_OBJC_INTEROP
|
||||
#define KONAN_OBJECTS_CAN_HAVE_RESERVED_TAIL 1
|
||||
#define KONAN_TYPE_INFO_HAS_WRITABLE_PART 1
|
||||
#endif
|
||||
|
||||
#endif // RUNTIME_COMMON_H
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "Assert.h"
|
||||
#include "Exceptions.h"
|
||||
#include "Memory.h"
|
||||
#include "MemoryPrivate.hpp"
|
||||
#include "Natives.h"
|
||||
|
||||
// If garbage collection algorithm for cyclic garbage to be used.
|
||||
@@ -349,10 +350,6 @@ inline bool isFreeable(const ContainerHeader* header) {
|
||||
return (header->refCount_ & CONTAINER_TAG_MASK) < CONTAINER_TAG_PERMANENT;
|
||||
}
|
||||
|
||||
inline bool isPermanent(const ContainerHeader* header) {
|
||||
return (header->refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_PERMANENT;
|
||||
}
|
||||
|
||||
inline bool isArena(const ContainerHeader* header) {
|
||||
return (header->refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_STACK;
|
||||
}
|
||||
@@ -361,14 +358,21 @@ inline container_size_t alignUp(container_size_t size, int alignment) {
|
||||
return (size + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
#if KONAN_OBJECTS_CAN_HAVE_RESERVED_TAIL
|
||||
// Note: defined by a compiler-generated bitcode.
|
||||
extern "C" const container_size_t kObjectReservedTailSize;
|
||||
#else
|
||||
constexpr container_size_t kObjectReservedTailSize = 0;
|
||||
#endif
|
||||
|
||||
// TODO: shall we do padding for alignment?
|
||||
inline container_size_t objectSize(const ObjHeader* obj) {
|
||||
const TypeInfo* type_info = obj->type_info();
|
||||
container_size_t size = type_info->instanceSize_ < 0 ?
|
||||
container_size_t size = kObjectReservedTailSize + (type_info->instanceSize_ < 0 ?
|
||||
// An array.
|
||||
ArrayDataSizeBytes(obj->array()) + sizeof(ArrayHeader)
|
||||
:
|
||||
type_info->instanceSize_ + sizeof(ObjHeader);
|
||||
type_info->instanceSize_ + sizeof(ObjHeader));
|
||||
return alignUp(size, kObjectAlignment);
|
||||
}
|
||||
|
||||
@@ -393,6 +397,7 @@ inline bool isRefCounted(KConstRef object) {
|
||||
|
||||
extern "C" {
|
||||
void objc_release(void* ptr);
|
||||
void Kotlin_ObjCExport_releaseReservedObjectTail(ObjHeader* obj);
|
||||
}
|
||||
|
||||
inline void runDeallocationHooks(ObjHeader* obj) {
|
||||
@@ -400,6 +405,10 @@ inline void runDeallocationHooks(ObjHeader* obj) {
|
||||
if (obj->type_info() == theObjCPointerHolderTypeInfo) {
|
||||
void* objcPtr = *reinterpret_cast<void**>(obj + 1); // TODO: use more reliable layout description
|
||||
objc_release(objcPtr);
|
||||
} else {
|
||||
if (HasReservedObjectTail(obj)) {
|
||||
Kotlin_ObjCExport_releaseReservedObjectTail(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -756,7 +765,7 @@ ContainerHeader* AllocContainer(size_t size) {
|
||||
// TODO: try to reuse elements of finalizer queue for new allocations, question
|
||||
// is how to get actual size of container.
|
||||
#endif
|
||||
ContainerHeader* result = konanConstructSizedInstance<ContainerHeader>(size);
|
||||
ContainerHeader* result = konanConstructSizedInstance<ContainerHeader>(alignUp(size, kObjectAlignment));
|
||||
CONTAINER_ALLOC_EVENT(state, size, result);
|
||||
#if TRACE_MEMORY
|
||||
state->containers->insert(result);
|
||||
@@ -794,7 +803,7 @@ void FreeContainer(ContainerHeader* header) {
|
||||
void ObjectContainer::Init(const TypeInfo* type_info) {
|
||||
RuntimeAssert(type_info->instanceSize_ >= 0, "Must be an object");
|
||||
uint32_t alloc_size =
|
||||
sizeof(ContainerHeader) + sizeof(ObjHeader) + type_info->instanceSize_;
|
||||
sizeof(ContainerHeader) + sizeof(ObjHeader) + type_info->instanceSize_ + kObjectReservedTailSize;
|
||||
header_ = AllocContainer(alloc_size);
|
||||
if (header_) {
|
||||
// One object in this container.
|
||||
@@ -810,7 +819,7 @@ void ArrayContainer::Init(const TypeInfo* type_info, uint32_t elements) {
|
||||
RuntimeAssert(type_info->instanceSize_ < 0, "Must be an array");
|
||||
uint32_t alloc_size =
|
||||
sizeof(ContainerHeader) + sizeof(ArrayHeader) -
|
||||
type_info->instanceSize_ * elements;
|
||||
type_info->instanceSize_ * elements + kObjectReservedTailSize;
|
||||
header_ = AllocContainer(alloc_size);
|
||||
RuntimeAssert(header_ != nullptr, "Cannot alloc memory");
|
||||
if (header_) {
|
||||
@@ -893,7 +902,7 @@ ObjHeader** ArenaContainer::getSlot() {
|
||||
|
||||
ObjHeader* ArenaContainer::PlaceObject(const TypeInfo* type_info) {
|
||||
RuntimeAssert(type_info->instanceSize_ >= 0, "must be an object");
|
||||
uint32_t size = type_info->instanceSize_ + sizeof(ObjHeader);
|
||||
uint32_t size = type_info->instanceSize_ + sizeof(ObjHeader) + kObjectReservedTailSize;
|
||||
ObjHeader* result = reinterpret_cast<ObjHeader*>(place(size));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
@@ -906,7 +915,7 @@ ObjHeader* ArenaContainer::PlaceObject(const TypeInfo* type_info) {
|
||||
|
||||
ArrayHeader* ArenaContainer::PlaceArray(const TypeInfo* type_info, uint32_t count) {
|
||||
RuntimeAssert(type_info->instanceSize_ < 0, "must be an array");
|
||||
container_size_t size = sizeof(ArrayHeader) - type_info->instanceSize_ * count;
|
||||
container_size_t size = sizeof(ArrayHeader) - type_info->instanceSize_ * count + kObjectReservedTailSize;
|
||||
ArrayHeader* result = reinterpret_cast<ArrayHeader*>(place(size));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
@@ -928,6 +937,14 @@ inline void ReleaseRef(const ObjHeader* object) {
|
||||
Release(object->container());
|
||||
}
|
||||
|
||||
void AddRefFromAssociatedObject(const ObjHeader* object) {
|
||||
AddRef(object);
|
||||
}
|
||||
|
||||
void ReleaseRefFromAssociatedObject(const ObjHeader* object) {
|
||||
ReleaseRef(object);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
MemoryState* InitMemory() {
|
||||
@@ -1036,6 +1053,16 @@ OBJ_GETTER(InitInstance,
|
||||
#endif
|
||||
}
|
||||
|
||||
bool HasReservedObjectTail(ObjHeader* obj) {
|
||||
return kObjectReservedTailSize != 0 && !isPermanent(obj);
|
||||
}
|
||||
|
||||
void* GetReservedObjectTail(ObjHeader* obj) {
|
||||
return reinterpret_cast<void*>(
|
||||
reinterpret_cast<uintptr_t>(obj) + objectSize(obj) - kObjectReservedTailSize
|
||||
);
|
||||
}
|
||||
|
||||
void SetRef(ObjHeader** location, const ObjHeader* object) {
|
||||
MEMORY_LOG("SetRef *%p: %p\n", location, object)
|
||||
*const_cast<const ObjHeader**>(location) = object;
|
||||
|
||||
@@ -115,6 +115,10 @@ struct ContainerHeader {
|
||||
}
|
||||
};
|
||||
|
||||
inline bool isPermanent(const ContainerHeader* header) {
|
||||
return (header->refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_PERMANENT;
|
||||
}
|
||||
|
||||
struct ArrayHeader;
|
||||
|
||||
// Header of every object.
|
||||
@@ -153,6 +157,10 @@ struct ObjHeader {
|
||||
const ArrayHeader* array() const { return reinterpret_cast<const ArrayHeader*>(this); }
|
||||
};
|
||||
|
||||
inline bool isPermanent(const ObjHeader* obj) {
|
||||
return isPermanent(obj->container());
|
||||
}
|
||||
|
||||
// Header of value type array objects. Keep layout in sync with that of object header.
|
||||
struct ArrayHeader {
|
||||
const TypeInfo* type_info_;
|
||||
@@ -331,6 +339,11 @@ void DeinitInstanceBody(const TypeInfo* typeInfo, void* body);
|
||||
OBJ_GETTER(InitInstance, ObjHeader** location, const TypeInfo* type_info,
|
||||
void (*ctor)(ObjHeader*));
|
||||
|
||||
// Returns true iff the object has space reserved in its tail for special purposes.
|
||||
bool HasReservedObjectTail(ObjHeader* obj) RUNTIME_NOTHROW;
|
||||
// Returns the pointer to the reserved space, `HasReservedObjectTail(obj)` must be true.
|
||||
void* GetReservedObjectTail(ObjHeader* obj) RUNTIME_NOTHROW;
|
||||
|
||||
//
|
||||
// Object reference management.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_MEMORYPRIVATE_HPP
|
||||
#define RUNTIME_MEMORYPRIVATE_HPP
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
void AddRefFromAssociatedObject(const ObjHeader* object) RUNTIME_NOTHROW;
|
||||
void ReleaseRefFromAssociatedObject(const ObjHeader* object) RUNTIME_NOTHROW;
|
||||
|
||||
#endif // RUNTIME_MEMORYPRIVATE_HPP
|
||||
@@ -0,0 +1,941 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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 "Types.h"
|
||||
#import "Memory.h"
|
||||
|
||||
#if KONAN_OBJC_INTEROP
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSNull.h>
|
||||
#import <Foundation/NSMethodSignature.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <objc/runtime.h>
|
||||
#import "MemoryPrivate.hpp"
|
||||
#import "Runtime.h"
|
||||
#import "Utils.h"
|
||||
|
||||
struct ObjCToKotlinMethodAdapter {
|
||||
const char* selector;
|
||||
const char* encoding;
|
||||
IMP imp;
|
||||
};
|
||||
|
||||
struct KotlinToObjCMethodAdapter {
|
||||
const char* selector;
|
||||
MethodNameHash nameSignature;
|
||||
int vtableIndex;
|
||||
const void* kotlinImpl;
|
||||
};
|
||||
|
||||
struct ObjCTypeAdapter {
|
||||
const TypeInfo* kotlinTypeInfo;
|
||||
|
||||
const void * const * kotlinVtable;
|
||||
int kotlinVtableSize;
|
||||
|
||||
const MethodTableRecord* kotlinMethodTable;
|
||||
int kotlinMethodTableSize;
|
||||
|
||||
const char* objCName;
|
||||
|
||||
const ObjCToKotlinMethodAdapter* directAdapters;
|
||||
int directAdapterNum;
|
||||
|
||||
const ObjCToKotlinMethodAdapter* virtualAdapters;
|
||||
int virtualAdapterNum;
|
||||
|
||||
const KotlinToObjCMethodAdapter* reverseAdapters;
|
||||
int reverseAdapterNum;
|
||||
};
|
||||
|
||||
typedef id (*convertReferenceToObjC)(ObjHeader* obj);
|
||||
|
||||
struct TypeInfoObjCExportAddition {
|
||||
convertReferenceToObjC convert;
|
||||
Class objCClass;
|
||||
const ObjCTypeAdapter* typeAdapter;
|
||||
};
|
||||
|
||||
struct WritableTypeInfo {
|
||||
TypeInfoObjCExportAddition objCExport;
|
||||
};
|
||||
|
||||
|
||||
static char associatedTypeInfoKey;
|
||||
|
||||
static const TypeInfo* getAssociatedTypeInfo(Class clazz) {
|
||||
return (const TypeInfo*)[objc_getAssociatedObject(clazz, &associatedTypeInfoKey) pointerValue];
|
||||
}
|
||||
|
||||
static void setAssociatedTypeInfo(Class clazz, const TypeInfo* typeInfo) {
|
||||
objc_setAssociatedObject(clazz, &associatedTypeInfoKey, [NSValue valueWithPointer:typeInfo], OBJC_ASSOCIATION_RETAIN);
|
||||
}
|
||||
|
||||
inline static bool HasAssociatedObjectField(ObjHeader* obj) {
|
||||
return HasReservedObjectTail(obj);
|
||||
}
|
||||
|
||||
inline static void SetAssociatedObject(ObjHeader* obj, id value) {
|
||||
*reinterpret_cast<id*>(GetReservedObjectTail(obj)) = value;
|
||||
}
|
||||
|
||||
extern "C" inline id Kotlin_ObjCExport_GetAssociatedObject(ObjHeader* obj) {
|
||||
return *reinterpret_cast<id*>(GetReservedObjectTail(obj));
|
||||
}
|
||||
|
||||
inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* typeInfo, id associatedObject) {
|
||||
ObjHeader* result = AllocInstance(typeInfo, OBJ_RESULT);
|
||||
RuntimeAssert(HasAssociatedObjectField(result), "");
|
||||
SetAssociatedObject(result, associatedObject);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void initializeClass(Class clazz);
|
||||
|
||||
@protocol ConvertibleToKotlin
|
||||
@required
|
||||
-(KRef)toKotlin:(KRef*)OBJ_RESULT;
|
||||
@end;
|
||||
|
||||
@interface KotlinBase : NSObject <ConvertibleToKotlin>
|
||||
@end;
|
||||
|
||||
@implementation KotlinBase {
|
||||
KRef kotlinObj;
|
||||
}
|
||||
|
||||
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
|
||||
RETURN_OBJ(kotlinObj);
|
||||
}
|
||||
|
||||
+(void)initialize {
|
||||
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:@"Allocating %s is not supported yet",
|
||||
class_getName(object_getClass(self))];
|
||||
}
|
||||
|
||||
AllocInstanceWithAssociatedObject(typeInfo, result, &result->kotlinObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+(instancetype)createWrapper:(ObjHeader*)obj {
|
||||
KotlinBase* result = [super allocWithZone:nil];
|
||||
// TODO: should we call NSObject.init ?
|
||||
UpdateRef(&result->kotlinObj, obj);
|
||||
|
||||
if (!isPermanent(obj)) {
|
||||
RuntimeAssert(HasAssociatedObjectField(obj), "");
|
||||
SetAssociatedObject(obj, result);
|
||||
}
|
||||
// TODO: permanent objects should probably be supported as custom types.
|
||||
|
||||
return [result autorelease];
|
||||
}
|
||||
|
||||
-(instancetype)retain {
|
||||
ObjHeader* obj = kotlinObj;
|
||||
if (isPermanent(obj)) { // TODO: consider storing `isPermanent` to self field.
|
||||
[super retain];
|
||||
} else {
|
||||
AddRefFromAssociatedObject(obj);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(oneway void)release {
|
||||
ObjHeader* obj = kotlinObj;
|
||||
if (isPermanent(obj)) {
|
||||
[super release];
|
||||
} else {
|
||||
ReleaseRefFromAssociatedObject(kotlinObj);
|
||||
}
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
RuntimeAssert(!isPermanent(kotlinObj), "");
|
||||
[super release];
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
extern "C" void Kotlin_ObjCExport_releaseReservedObjectTail(ObjHeader* obj) {
|
||||
RuntimeAssert(HasAssociatedObjectField(obj), "");
|
||||
id associatedObject = Kotlin_ObjCExport_GetAssociatedObject(obj);
|
||||
if (associatedObject != nullptr) {
|
||||
[associatedObject releaseAsAssociatedObject];
|
||||
}
|
||||
}
|
||||
|
||||
static const ObjCTypeAdapter* findAdapterByName(
|
||||
const char* name,
|
||||
const ObjCTypeAdapter** sortedAdapters,
|
||||
int adapterNum
|
||||
) {
|
||||
|
||||
int left = 0, right = adapterNum - 1;
|
||||
|
||||
while (right >= left) {
|
||||
int mid = (left + right) / 2;
|
||||
int cmp = strcmp(name, sortedAdapters[mid]->objCName);
|
||||
if (cmp < 0) {
|
||||
right = mid - 1;
|
||||
} else if (cmp > 0) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
return sortedAdapters[mid];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
__attribute__((weak)) const ObjCTypeAdapter** Kotlin_ObjCExport_sortedClassAdapters = nullptr;
|
||||
__attribute__((weak)) int Kotlin_ObjCExport_sortedClassAdaptersNum = 0;
|
||||
|
||||
__attribute__((weak)) const ObjCTypeAdapter** Kotlin_ObjCExport_sortedProtocolAdapters = nullptr;
|
||||
__attribute__((weak)) int Kotlin_ObjCExport_sortedProtocolAdaptersNum = 0;
|
||||
|
||||
static const ObjCTypeAdapter* findClassAdapter(Class clazz) {
|
||||
return findAdapterByName(class_getName(clazz),
|
||||
Kotlin_ObjCExport_sortedClassAdapters,
|
||||
Kotlin_ObjCExport_sortedClassAdaptersNum
|
||||
);
|
||||
}
|
||||
|
||||
static const ObjCTypeAdapter* findProtocolAdapter(Protocol* prot) {
|
||||
return findAdapterByName(protocol_getName(prot),
|
||||
Kotlin_ObjCExport_sortedProtocolAdapters,
|
||||
Kotlin_ObjCExport_sortedProtocolAdaptersNum
|
||||
);
|
||||
}
|
||||
|
||||
static const ObjCTypeAdapter* getTypeAdapter(const TypeInfo* typeInfo) {
|
||||
return typeInfo->writableInfo_->objCExport.typeAdapter;
|
||||
}
|
||||
|
||||
static Protocol* getProtocolForInterface(const TypeInfo* interfaceInfo) {
|
||||
const ObjCTypeAdapter* protocolAdapter = getTypeAdapter(interfaceInfo);
|
||||
if (protocolAdapter != nullptr) {
|
||||
Protocol* protocol = objc_getProtocol(protocolAdapter->objCName);
|
||||
if (protocol != nullptr) {
|
||||
return protocol;
|
||||
} else {
|
||||
// TODO: construct the protocol in compiler instead, because this case can't be handled easily.
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const TypeInfo* getOrCreateTypeInfo(Class clazz);
|
||||
|
||||
static void initializeClass(Class clazz) {
|
||||
const ObjCTypeAdapter* typeAdapter = findClassAdapter(clazz);
|
||||
if (typeAdapter == nullptr) {
|
||||
getOrCreateTypeInfo(clazz);
|
||||
return;
|
||||
}
|
||||
|
||||
const TypeInfo* typeInfo = typeAdapter->kotlinTypeInfo;
|
||||
bool isClassForPackage = typeInfo == nullptr;
|
||||
if (!isClassForPackage) {
|
||||
setAssociatedTypeInfo(clazz, typeInfo);
|
||||
}
|
||||
|
||||
for (int i = 0; i < typeAdapter->directAdapterNum; ++i) {
|
||||
const ObjCToKotlinMethodAdapter* adapter = typeAdapter->directAdapters + i;
|
||||
SEL selector = sel_registerName(adapter->selector);
|
||||
Class methodContainer = isClassForPackage ? object_getClass(clazz) : clazz;
|
||||
BOOL added = class_addMethod(methodContainer, selector, adapter->imp, adapter->encoding);
|
||||
RuntimeAssert(added, "Unexpected selector clash");
|
||||
}
|
||||
|
||||
if (isClassForPackage) return;
|
||||
|
||||
for (int i = 0; i < typeInfo->implementedInterfacesCount_; ++i) {
|
||||
Protocol* protocol = getProtocolForInterface(typeInfo->implementedInterfaces_[i]);
|
||||
if (protocol != nullptr) {
|
||||
class_addProtocol(clazz, protocol);
|
||||
class_addProtocol(object_getClass(clazz), protocol);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@interface NSObject (NSObjectToKotlin) <ConvertibleToKotlin>
|
||||
@end;
|
||||
|
||||
extern "C" id objc_retain(id self);
|
||||
extern "C" id objc_retainBlock(id self);
|
||||
extern "C" void objc_release(id self);
|
||||
extern "C" id objc_retainAutoreleaseReturnValue(id self);
|
||||
|
||||
@implementation NSObject (NSObjectToKotlin)
|
||||
-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT {
|
||||
const TypeInfo* typeInfo = getOrCreateTypeInfo(object_getClass(self));
|
||||
RETURN_RESULT_OF(AllocInstanceWithAssociatedObject, typeInfo, objc_retain(self));
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
objc_release(self);
|
||||
}
|
||||
@end;
|
||||
|
||||
@interface NSString (NSStringToKotlin) <ConvertibleToKotlin>
|
||||
@end;
|
||||
|
||||
extern "C" OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, NSString* str);
|
||||
|
||||
@implementation NSString (NSStringToKotlin)
|
||||
-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT {
|
||||
RETURN_RESULT_OF(Kotlin_Interop_CreateKStringFromNSString, self);
|
||||
}
|
||||
@end;
|
||||
|
||||
@interface NSArray (NSArrayToKotlin) <ConvertibleToKotlin>
|
||||
@end;
|
||||
|
||||
extern "C" void Kotlin_NSArrayList_constructor(ObjHeader* obj);
|
||||
|
||||
extern const TypeInfo *theNSArrayListTypeInfo;
|
||||
|
||||
@implementation NSArray (NSArrayToKotlin)
|
||||
-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT {
|
||||
ObjHeader* result = AllocInstanceWithAssociatedObject(theNSArrayListTypeInfo, objc_retain(self), OBJ_RESULT);
|
||||
|
||||
Kotlin_NSArrayList_constructor(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
objc_release(self);
|
||||
}
|
||||
@end;
|
||||
|
||||
extern "C" {
|
||||
|
||||
OBJ_GETTER(Kotlin_boxBoolean, KBoolean value);
|
||||
OBJ_GETTER(Kotlin_boxChar, KChar 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_boxFloat, KFloat value);
|
||||
OBJ_GETTER(Kotlin_boxDouble, KDouble value);
|
||||
|
||||
}
|
||||
|
||||
@interface NSNumber (NSNumberToKotlin) <ConvertibleToKotlin>
|
||||
@end;
|
||||
|
||||
static Class __NSCFBooleanClass = nullptr;
|
||||
|
||||
@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 'S': RETURN_RESULT_OF(Kotlin_boxChar, self.unsignedShortValue);
|
||||
case 'c': {
|
||||
Class booleanClass = __NSCFBooleanClass;
|
||||
if (booleanClass == nullptr) {
|
||||
// Note: __NSCFBoolean is not visible to linker, so this case can't be handled with a category.
|
||||
booleanClass = __NSCFBooleanClass = objc_getClass("__NSCFBoolean");
|
||||
if (booleanClass == nullptr) {
|
||||
[NSException raise:NSGenericException format:@"__NSCFBoolean class not found"];
|
||||
}
|
||||
}
|
||||
|
||||
if (object_getClass(self) == booleanClass) {
|
||||
RETURN_RESULT_OF(Kotlin_boxBoolean, self.boolValue);
|
||||
} else {
|
||||
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 'f': RETURN_RESULT_OF(Kotlin_boxFloat, self.floatValue);
|
||||
case 'd': RETURN_RESULT_OF(Kotlin_boxDouble, self.doubleValue);
|
||||
|
||||
default: return [super toKotlin:OBJ_RESULT];
|
||||
}
|
||||
}
|
||||
@end;
|
||||
|
||||
@interface KListNSArray : NSArray <ConvertibleToKotlin>
|
||||
@end;
|
||||
|
||||
extern "C" OBJ_GETTER(Kotlin_List_get, ObjHeader* list, KInt index);
|
||||
extern "C" KInt Kotlin_List_getSize(ObjHeader* list);
|
||||
|
||||
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj);
|
||||
|
||||
@implementation KListNSArray {
|
||||
ObjHeader* list;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
UpdateRef(&list, nullptr);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
+(id)createWithKList:(ObjHeader*)list {
|
||||
KListNSArray* result = [[[KListNSArray alloc] init] autorelease];
|
||||
UpdateRef(&result->list, list);
|
||||
return result;
|
||||
}
|
||||
|
||||
-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT {
|
||||
RETURN_OBJ(list);
|
||||
}
|
||||
|
||||
-(id)objectAtIndex:(NSUInteger)index {
|
||||
ObjHolder kotlinValueHolder;
|
||||
ObjHeader* kotlinValue = Kotlin_List_get(list, index, kotlinValueHolder.slot());
|
||||
if (kotlinValue == nullptr) return [NSNull null];
|
||||
|
||||
return Kotlin_ObjCExport_refToObjC(kotlinValue);
|
||||
}
|
||||
|
||||
-(NSUInteger)count {
|
||||
return Kotlin_List_getSize(list);
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
@interface NSBlock <NSObject>
|
||||
@end;
|
||||
|
||||
@interface NSBlock (NSBlockToKotlin) <ConvertibleToKotlin>
|
||||
@end;
|
||||
|
||||
struct Block_descriptor_1;
|
||||
|
||||
// Based on https://clang.llvm.org/docs/Block-ABI-Apple.html and libclosure source.
|
||||
struct Block_literal_1 {
|
||||
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(void *, ...);
|
||||
struct Block_descriptor_1 *descriptor; // IFF (1<<25)
|
||||
// Or:
|
||||
// struct Block_descriptor_1_without_helpers* descriptor // if hasn't (1<<25).
|
||||
|
||||
// imported variables
|
||||
};
|
||||
|
||||
struct Block_descriptor_1 {
|
||||
unsigned long int reserved; // NULL
|
||||
unsigned long int size; // sizeof(struct Block_literal_1)
|
||||
|
||||
// optional helper functions
|
||||
void (*copy_helper)(void *dst, void *src);
|
||||
void (*dispose_helper)(void *src);
|
||||
// required ABI.2010.3.16
|
||||
const char *signature; // IFF (1<<30)
|
||||
const void* layout; // IFF (1<<31)
|
||||
};
|
||||
|
||||
struct Block_descriptor_1_without_helpers {
|
||||
unsigned long int reserved; // NULL
|
||||
unsigned long int size; // sizeof(struct Block_literal_1)
|
||||
|
||||
// required ABI.2010.3.16
|
||||
const char *signature; // IFF (1<<30)
|
||||
const void* layout; // IFF (1<<31)
|
||||
};
|
||||
|
||||
static const char* getBlockEncoding(id block) {
|
||||
Block_literal_1* literal = reinterpret_cast<Block_literal_1*>(block);
|
||||
|
||||
int flags = literal->flags;
|
||||
RuntimeAssert((flags & (1 << 30)) != 0, "block has no signature stored");
|
||||
return (flags & (1 << 25)) != 0 ?
|
||||
literal->descriptor->signature :
|
||||
reinterpret_cast<struct Block_descriptor_1_without_helpers*>(literal->descriptor)->signature;
|
||||
}
|
||||
|
||||
// Note: replaced by compiler in appropriate compilation modes.
|
||||
__attribute__((weak)) const TypeInfo * const * Kotlin_ObjCExport_functionAdaptersToBlock = nullptr;
|
||||
|
||||
static const TypeInfo* getFunctionTypeInfoForBlock(id block) {
|
||||
const char* encoding = getBlockEncoding(block);
|
||||
|
||||
// TODO: optimize:
|
||||
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
|
||||
int parameterCount = signature.numberOfArguments - 1; // 1 for the block itself.
|
||||
|
||||
if (parameterCount > 22) {
|
||||
[NSException raise:NSGenericException format:@"Blocks with %d (>22) parameters aren't supported", parameterCount];
|
||||
}
|
||||
|
||||
for (int i = 1; i <= parameterCount; ++i) {
|
||||
const char* argEncoding = [signature getArgumentTypeAtIndex:i];
|
||||
if (argEncoding[0] != '@') {
|
||||
[NSException raise:NSGenericException
|
||||
format:@"Blocks with non-reference-typed arguments aren't supported (%s)", argEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
const char* returnTypeEncoding = signature.methodReturnType;
|
||||
if (returnTypeEncoding[0] != '@') {
|
||||
[NSException raise:NSGenericException
|
||||
format:@"Blocks with non-reference-typed return value aren't supported (%s)", returnTypeEncoding];
|
||||
}
|
||||
|
||||
// TODO: support Unit-as-void.
|
||||
|
||||
return Kotlin_ObjCExport_functionAdaptersToBlock[parameterCount];
|
||||
}
|
||||
|
||||
@implementation NSBlock (NSBlockToKotlin)
|
||||
-(ObjHeader*)toKotlin:(ObjHeader**)OBJ_RESULT {
|
||||
|
||||
const TypeInfo* typeInfo = getFunctionTypeInfoForBlock(self);
|
||||
RETURN_RESULT_OF(AllocInstanceWithAssociatedObject, typeInfo, objc_retainBlock(self));
|
||||
// TODO: call (Any) constructor?
|
||||
}
|
||||
|
||||
-(void)releaseAsAssociatedObject {
|
||||
objc_release(self);
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
extern "C" id Kotlin_Interop_CreateNSArrayFromKList(ObjHeader* obj) {
|
||||
return [KListNSArray createWithKList:obj];
|
||||
}
|
||||
|
||||
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj);
|
||||
|
||||
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj) {
|
||||
if (obj == nullptr) return nullptr;
|
||||
|
||||
if (HasAssociatedObjectField(obj)) {
|
||||
id associatedObject = Kotlin_ObjCExport_GetAssociatedObject(obj);
|
||||
if (associatedObject != nullptr) {
|
||||
return objc_retainAutoreleaseReturnValue(associatedObject);
|
||||
}
|
||||
}
|
||||
|
||||
convertReferenceToObjC converter = obj->type_info()->writableInfo_->objCExport.convert;
|
||||
if (converter != nullptr) {
|
||||
return converter(obj);
|
||||
}
|
||||
|
||||
return Kotlin_ObjCExport_refToObjC_slowpath(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];
|
||||
}
|
||||
|
||||
static Class getOrCreateClass(const TypeInfo* typeInfo);
|
||||
|
||||
static id convertKotlinObject(ObjHeader* obj) {
|
||||
Class clazz = obj->type_info()->writableInfo_->objCExport.objCClass;
|
||||
RuntimeAssert(clazz != nullptr, "");
|
||||
return [clazz createWrapper:obj];
|
||||
}
|
||||
|
||||
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj) {
|
||||
const TypeInfo* typeInfo = obj->type_info();
|
||||
convertReferenceToObjC converter = nullptr;
|
||||
|
||||
for (int i = 0; i < typeInfo->implementedInterfacesCount_; ++i) {
|
||||
converter = typeInfo->implementedInterfaces_[i]->writableInfo_->objCExport.convert;
|
||||
if (converter != nullptr) break;
|
||||
}
|
||||
|
||||
if (converter == nullptr) {
|
||||
getOrCreateClass(typeInfo);
|
||||
converter = &convertKotlinObject;
|
||||
}
|
||||
|
||||
typeInfo->writableInfo_->objCExport.convert = converter;
|
||||
|
||||
return converter(obj);
|
||||
}
|
||||
|
||||
extern "C" KInt Kotlin_NSArrayList_getSize(ObjHeader* obj) {
|
||||
NSArray* array = (NSArray*) Kotlin_ObjCExport_GetAssociatedObject(obj);
|
||||
return [array count];
|
||||
}
|
||||
|
||||
extern "C" OBJ_GETTER(Kotlin_NSArrayList_getElement, ObjHeader* obj, KInt index) {
|
||||
NSArray* array = (NSArray*) Kotlin_ObjCExport_GetAssociatedObject(obj);
|
||||
id element = [array objectAtIndex:index];
|
||||
if (element == NSNull.null) RETURN_OBJ(nullptr);
|
||||
RETURN_RESULT_OF(Kotlin_ObjCExport_refFromObjC, element);
|
||||
}
|
||||
|
||||
static const TypeInfo* createTypeInfo(
|
||||
const TypeInfo* superType,
|
||||
const KStdVector<const TypeInfo*>& superInterfaces,
|
||||
const KStdVector<const void*>& vtable,
|
||||
const KStdVector<MethodTableRecord>& methodTable
|
||||
) {
|
||||
TypeInfo* result = (TypeInfo*)konanAllocMemory(sizeof(TypeInfo) + vtable.size() * sizeof(void*));
|
||||
|
||||
MakeGlobalHash(nullptr, 0, &result->name_);
|
||||
result->instanceSize_ = superType->instanceSize_;
|
||||
result->superType_ = superType;
|
||||
result->objOffsets_ = superType->objOffsets_;
|
||||
result->objOffsetsCount_ = superType->objOffsetsCount_;
|
||||
|
||||
KStdVector<const TypeInfo*> implementedInterfaces(
|
||||
superType->implementedInterfaces_, superType->implementedInterfaces_ + superType->implementedInterfacesCount_
|
||||
);
|
||||
KStdUnorderedSet<const TypeInfo*> usedInterfaces(implementedInterfaces.begin(), implementedInterfaces.end());
|
||||
|
||||
for (const TypeInfo* interface : superInterfaces) {
|
||||
if (usedInterfaces.insert(interface).second) {
|
||||
implementedInterfaces.push_back(interface);
|
||||
}
|
||||
}
|
||||
|
||||
const TypeInfo** implementedInterfaces_ = konanAllocArray<const TypeInfo*>(implementedInterfaces.size());
|
||||
for (int i = 0; i < implementedInterfaces.size(); ++i) {
|
||||
implementedInterfaces_[i] = implementedInterfaces[i];
|
||||
}
|
||||
|
||||
result->implementedInterfaces_ = implementedInterfaces_;
|
||||
result->implementedInterfacesCount_ = implementedInterfaces.size();
|
||||
|
||||
MethodTableRecord* openMethods_ = konanAllocArray<MethodTableRecord>(methodTable.size());
|
||||
for (int i = 0; i < methodTable.size(); ++i) openMethods_[i] = methodTable[i];
|
||||
|
||||
result->openMethods_ = openMethods_;
|
||||
result->openMethodsCount_ = methodTable.size();
|
||||
|
||||
result->fields_ = nullptr;
|
||||
result->fieldsCount_ = 0;
|
||||
|
||||
result->packageName_ = nullptr;
|
||||
result->relativeName_ = nullptr; // TODO: add some info.
|
||||
result->writableInfo_ = (WritableTypeInfo*)konanAllocMemory(sizeof(WritableTypeInfo));
|
||||
|
||||
for (int i = 0; i < vtable.size(); ++i) result->vtable()[i] = vtable[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void addDefinedSelectors(Class clazz, KStdUnorderedSet<SEL>& result) {
|
||||
unsigned int objcMethodCount;
|
||||
Method* objcMethods = class_copyMethodList(clazz, &objcMethodCount);
|
||||
|
||||
for (int i = 0; i < objcMethodCount; ++i) {
|
||||
result.insert(method_getName(objcMethods[i]));
|
||||
}
|
||||
|
||||
if (objcMethods != nullptr) free(objcMethods);
|
||||
}
|
||||
|
||||
static KStdVector<const TypeInfo*> getProtocolsAsInterfaces(Class clazz) {
|
||||
KStdVector<const TypeInfo*> result;
|
||||
|
||||
unsigned int protocolCount;
|
||||
Protocol** protocols = class_copyProtocolList(clazz, &protocolCount);
|
||||
if (protocols == nullptr) return result;
|
||||
|
||||
for (int i = 0; i < protocolCount; ++i) {
|
||||
Protocol* protocol = protocols[i];
|
||||
const ObjCTypeAdapter* typeAdapter = findProtocolAdapter(protocol);
|
||||
if (typeAdapter != nullptr) result.push_back(typeAdapter->kotlinTypeInfo);
|
||||
}
|
||||
if (protocols != nullptr) free(protocols);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const TypeInfo* getMostSpecificKotlinClass(const TypeInfo* typeInfo) {
|
||||
const TypeInfo* result = typeInfo;
|
||||
while (getTypeAdapter(result) == nullptr) {
|
||||
result = result->superType_;
|
||||
RuntimeAssert(result != nullptr, "");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void insertOrReplace(KStdVector<MethodTableRecord>& methodTable, MethodNameHash nameSignature, void* entryPoint) {
|
||||
MethodTableRecord record = {nameSignature, entryPoint};
|
||||
|
||||
for (int i = methodTable.size() - 1; i >= 0; --i) {
|
||||
if (methodTable[i].nameSignature_ == nameSignature) {
|
||||
methodTable[i].methodEntryPoint_ = entryPoint;
|
||||
return;
|
||||
} else if (methodTable[i].nameSignature_ < nameSignature) {
|
||||
methodTable.insert(methodTable.begin() + (i + 1), record);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
methodTable.insert(methodTable.begin(), record);
|
||||
}
|
||||
|
||||
static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType) {
|
||||
Class superClass = class_getSuperclass(clazz);
|
||||
|
||||
KStdUnorderedSet<SEL> definedSelectors;
|
||||
addDefinedSelectors(clazz, definedSelectors);
|
||||
|
||||
const ObjCTypeAdapter* superTypeAdapter = getTypeAdapter(superType);
|
||||
|
||||
const void * const * superVtable = nullptr;
|
||||
int superVtableSize = getTypeAdapter(getMostSpecificKotlinClass(superType))->kotlinVtableSize;
|
||||
|
||||
const MethodTableRecord* superMethodTable = nullptr;
|
||||
int superMethodTableSize = 0;
|
||||
|
||||
if (superTypeAdapter != nullptr) {
|
||||
// Then super class is Kotlin class.
|
||||
|
||||
// And if it is abstract, then vtable and method table are not available from TypeInfo,
|
||||
// but present in type adapter instead:
|
||||
superVtable = superTypeAdapter->kotlinVtable;
|
||||
superMethodTable = superTypeAdapter->kotlinMethodTable;
|
||||
superMethodTableSize = superTypeAdapter->kotlinMethodTableSize;
|
||||
}
|
||||
|
||||
if (superVtable == nullptr) superVtable = superType->vtable();
|
||||
if (superMethodTable == nullptr) {
|
||||
superMethodTable = superType->openMethods_;
|
||||
superMethodTableSize = superType->openMethodsCount_;
|
||||
}
|
||||
|
||||
KStdVector<const void*> vtable(
|
||||
superVtable,
|
||||
superVtable + superVtableSize
|
||||
);
|
||||
|
||||
KStdVector<MethodTableRecord> methodTable(
|
||||
superMethodTable, superMethodTable + superMethodTableSize
|
||||
);
|
||||
|
||||
KStdVector<const TypeInfo*> addedInterfaces = getProtocolsAsInterfaces(clazz);
|
||||
|
||||
KStdVector<const TypeInfo*> supers(
|
||||
superType->implementedInterfaces_,
|
||||
superType->implementedInterfaces_ + superType->implementedInterfacesCount_
|
||||
);
|
||||
|
||||
for (const TypeInfo* t = superType; t != nullptr; t = t->superType_) {
|
||||
supers.push_back(t);
|
||||
}
|
||||
|
||||
for (const TypeInfo* t : supers) {
|
||||
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(t);
|
||||
if (typeAdapter == nullptr) continue;
|
||||
|
||||
for (int i = 0; i < typeAdapter->reverseAdapterNum; ++i) {
|
||||
const KotlinToObjCMethodAdapter* adapter = &typeAdapter->reverseAdapters[i];
|
||||
if (definedSelectors.find(sel_registerName(adapter->selector)) == definedSelectors.end()) continue;
|
||||
|
||||
if (adapter->kotlinImpl == nullptr) {
|
||||
[NSException raise:NSGenericException
|
||||
format:@"[%s %s] can't be implemented",
|
||||
class_getName(clazz), adapter->selector];
|
||||
// TODO: describe the reasons
|
||||
}
|
||||
|
||||
insertOrReplace(methodTable, adapter->nameSignature, const_cast<void*>(adapter->kotlinImpl));
|
||||
if (adapter->vtableIndex != -1) vtable[adapter->vtableIndex] = adapter->kotlinImpl;
|
||||
}
|
||||
}
|
||||
|
||||
for (const TypeInfo* typeInfo : addedInterfaces) {
|
||||
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(typeInfo);
|
||||
if (typeAdapter == nullptr) continue;
|
||||
|
||||
for (int i = 0; i < typeAdapter->reverseAdapterNum; ++i) {
|
||||
const KotlinToObjCMethodAdapter* adapter = &typeAdapter->reverseAdapters[i];
|
||||
if (adapter->kotlinImpl == nullptr) {
|
||||
[NSException raise:NSGenericException
|
||||
format:@"[%s %s] can't be implemented",
|
||||
class_getName(clazz), adapter->selector];
|
||||
}
|
||||
|
||||
insertOrReplace(methodTable, adapter->nameSignature, const_cast<void*>(adapter->kotlinImpl));
|
||||
RuntimeAssert(adapter->vtableIndex == -1, "");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consider forbidding the class being abstract.
|
||||
|
||||
const TypeInfo* result = createTypeInfo(superType, addedInterfaces, vtable, methodTable);
|
||||
|
||||
// TODO: it will probably never be requested, since such a class can't be instantiated in Kotlin.
|
||||
result->writableInfo_->objCExport.objCClass = clazz;
|
||||
return result;
|
||||
}
|
||||
|
||||
static SimpleMutex typeInfoCreationMutex;
|
||||
|
||||
static const TypeInfo* getOrCreateTypeInfo(Class clazz) {
|
||||
const TypeInfo* result = getAssociatedTypeInfo(clazz);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Class superClass = class_getSuperclass(clazz);
|
||||
|
||||
const TypeInfo* superType = superClass == nullptr ?
|
||||
theAnyTypeInfo :
|
||||
getOrCreateTypeInfo(superClass);
|
||||
|
||||
LockGuard<SimpleMutex> lockGuard(typeInfoCreationMutex);
|
||||
|
||||
result = getAssociatedTypeInfo(clazz); // double-checking.
|
||||
if (result == nullptr) {
|
||||
result = createTypeInfo(clazz, superType);
|
||||
setAssociatedTypeInfo(clazz, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static SimpleMutex classCreationMutex;
|
||||
static int anonymousClassNextId = 0;
|
||||
|
||||
static void addVirtualAdapters(Class clazz, const ObjCTypeAdapter* typeAdapter) {
|
||||
for (int i = 0; i < typeAdapter->virtualAdapterNum; ++i) {
|
||||
const ObjCToKotlinMethodAdapter* adapter = typeAdapter->virtualAdapters + i;
|
||||
SEL selector = sel_registerName(adapter->selector);
|
||||
|
||||
class_addMethod(clazz, selector, adapter->imp, adapter->encoding);
|
||||
}
|
||||
}
|
||||
|
||||
static Class createClass(const TypeInfo* typeInfo, Class superClass) {
|
||||
RuntimeAssert(typeInfo->superType_ != nullptr, "");
|
||||
|
||||
char classNameBuffer[64];
|
||||
snprintf(classNameBuffer, sizeof(classNameBuffer), "kobjcc%d", anonymousClassNextId++);
|
||||
const char* className = classNameBuffer;
|
||||
|
||||
Class result = objc_allocateClassPair(superClass, className, 0);
|
||||
RuntimeAssert(result != nullptr, "");
|
||||
|
||||
// TODO: optimize by adding virtual adapters only for overridden methods.
|
||||
|
||||
if (getTypeAdapter(typeInfo->superType_) == nullptr) {
|
||||
// class for super type is also synthesized, no need to add class adapters;
|
||||
} else {
|
||||
for (const TypeInfo* superType = typeInfo->superType_; superType != nullptr; superType = superType->superType_) {
|
||||
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(superType);
|
||||
if (typeAdapter != nullptr) {
|
||||
addVirtualAdapters(result, typeAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KStdUnorderedSet<const TypeInfo*> superImplementedInterfaces(
|
||||
typeInfo->superType_->implementedInterfaces_,
|
||||
typeInfo->superType_->implementedInterfaces_ + typeInfo->superType_->implementedInterfacesCount_
|
||||
);
|
||||
|
||||
for (int i = 0; i < typeInfo->implementedInterfacesCount_; ++i) {
|
||||
const TypeInfo* interface = typeInfo->implementedInterfaces_[i];
|
||||
if (superImplementedInterfaces.find(interface) == superImplementedInterfaces.end()) {
|
||||
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(interface);
|
||||
if (typeAdapter != nullptr) {
|
||||
addVirtualAdapters(result, typeAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
objc_registerClassPair(result);
|
||||
|
||||
// TODO: it will probably never be requested, since such a class can't be instantiated in Objective-C.
|
||||
setAssociatedTypeInfo(result, typeInfo);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static Class getOrCreateClass(const TypeInfo* typeInfo) {
|
||||
Class result = typeInfo->writableInfo_->objCExport.objCClass;
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(typeInfo);
|
||||
if (typeAdapter != nullptr) {
|
||||
result = objc_getClass(typeAdapter->objCName);
|
||||
RuntimeAssert(result != nullptr, "");
|
||||
typeInfo->writableInfo_->objCExport.objCClass = result;
|
||||
} else {
|
||||
Class superClass = getOrCreateClass(typeInfo->superType_);
|
||||
|
||||
LockGuard<SimpleMutex> lockGuard(classCreationMutex); // Note: non-recursive
|
||||
|
||||
result = typeInfo->writableInfo_->objCExport.objCClass; // double-checking.
|
||||
if (result == nullptr) {
|
||||
result = createClass(typeInfo, superClass);
|
||||
RuntimeAssert(result != nullptr, "");
|
||||
typeInfo->writableInfo_->objCExport.objCClass = result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C" void Kotlin_ObjCExport_AbstractMethodCalled(id self, SEL selector) {
|
||||
[NSException raise:NSGenericException
|
||||
format:@"[%s %s] is abstract",
|
||||
class_getName(object_getClass(self)), sel_getName(selector)];
|
||||
}
|
||||
|
||||
#else // KONAN_OBJC_INTEROP
|
||||
|
||||
extern "C" KInt Kotlin_NSArrayList_getSize(ObjHeader* obj) {
|
||||
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" OBJ_GETTER(Kotlin_NSArrayList_getElement, ObjHeader* obj, KInt index) {
|
||||
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||
RETURN_OBJ(nullptr);
|
||||
}
|
||||
|
||||
#endif // KONAN_OBJC_INTEROP
|
||||
@@ -43,6 +43,8 @@ static inline void SetKotlinTypeInfo(Class clazz, const TypeInfo* typeInfo) {
|
||||
GetKotlinClassData(clazz)->typeInfo = typeInfo;
|
||||
}
|
||||
|
||||
const TypeInfo* GetObjCKotlinTypeInfo(const ObjHeader* obj) RUNTIME_NOTHROW;
|
||||
|
||||
const TypeInfo* GetObjCKotlinTypeInfo(const ObjHeader* obj) {
|
||||
void* objcPtr = *reinterpret_cast<void * const *>(obj + 1); // TODO: use more reliable layout description
|
||||
Class clazz = object_getClass(reinterpret_cast<id>(objcPtr));
|
||||
@@ -85,9 +87,9 @@ static void AddDeallocMethod(Class clazz) {
|
||||
}
|
||||
|
||||
struct ObjCMethodDescription {
|
||||
void* (*imp)(void*, void*, ...);
|
||||
const char* selector;
|
||||
const char* encoding;
|
||||
const void* imp;
|
||||
};
|
||||
|
||||
struct KotlinObjCClassInfo {
|
||||
|
||||
@@ -44,15 +44,15 @@ namespace {
|
||||
|
||||
extern "C" {
|
||||
|
||||
NSString* Kotlin_Interop_CreateNSStringFromKString(const ArrayHeader* str) {
|
||||
id Kotlin_Interop_CreateNSStringFromKString(const ObjHeader* str) {
|
||||
if (str == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const KChar* utf16Chars = CharArrayAddressOfElementAt(str, 0);
|
||||
const KChar* utf16Chars = CharArrayAddressOfElementAt(str->array(), 0);
|
||||
|
||||
NSString* result = [[[getNSStringClass() alloc] initWithBytes:utf16Chars
|
||||
length:str->count_*sizeof(KChar)
|
||||
length:str->array()->count_*sizeof(KChar)
|
||||
encoding:NSUTF16LittleEndianStringEncoding] autorelease];
|
||||
|
||||
return result;
|
||||
|
||||
@@ -84,6 +84,15 @@ void Kotlin_initRuntimeIfNeeded() {
|
||||
runtimeState = InitRuntime();
|
||||
// Register runtime deinit function at thread cleanup.
|
||||
konan::onThreadExit(Kotlin_deinitRuntimeIfNeeded);
|
||||
#ifndef KONAN_WASM
|
||||
// `onThreadExit` doesn't work on main thread, use `atexit`:
|
||||
|
||||
static bool deinitScheduledAtexit = false;
|
||||
if (!deinitScheduledAtexit) {
|
||||
deinitScheduledAtexit = true; // Having data race is OK here.
|
||||
::atexit(Kotlin_deinitRuntimeIfNeeded);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
#include "Common.h"
|
||||
#include "Names.h"
|
||||
|
||||
#if KONAN_TYPE_INFO_HAS_WRITABLE_PART
|
||||
struct WritableTypeInfo;
|
||||
#endif
|
||||
|
||||
struct ObjHeader;
|
||||
|
||||
// An element of sorted by hash in-place array representing methods.
|
||||
@@ -67,8 +71,21 @@ struct TypeInfo {
|
||||
// or `null` if the class is anonymous.
|
||||
ObjHeader* relativeName_;
|
||||
|
||||
#if KONAN_TYPE_INFO_HAS_WRITABLE_PART
|
||||
WritableTypeInfo* writableInfo_;
|
||||
#endif
|
||||
|
||||
// vtable starts just after declared contents of the TypeInfo:
|
||||
// void* const vtable_[];
|
||||
#ifdef __cplusplus
|
||||
inline const void* const * vtable() const {
|
||||
return reinterpret_cast<void * const *>(this + 1);
|
||||
}
|
||||
|
||||
inline const void** vtable() {
|
||||
return reinterpret_cast<const void**>(this + 1);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -34,6 +34,7 @@ class BooleanBox(val value: Boolean) : Comparable<Boolean> {
|
||||
override fun compareTo(other: Boolean): Int = value.compareTo(other)
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_boxBoolean")
|
||||
fun boxBoolean(value: Boolean) = BooleanBox(value)
|
||||
|
||||
class CharBox(val value: Char) : Comparable<Char> {
|
||||
@@ -52,6 +53,7 @@ class CharBox(val value: Char) : Comparable<Char> {
|
||||
override fun compareTo(other: Char): Int = value.compareTo(other)
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_boxChar")
|
||||
fun boxChar(value: Char) = CharBox(value)
|
||||
|
||||
class ByteBox(val value: Byte) : Number(), Comparable<Byte> {
|
||||
@@ -78,6 +80,7 @@ class ByteBox(val value: Byte) : Number(), Comparable<Byte> {
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_boxByte")
|
||||
fun boxByte(value: Byte) = ByteBox(value)
|
||||
|
||||
class ShortBox(val value: Short) : Number(), Comparable<Short> {
|
||||
@@ -104,6 +107,7 @@ class ShortBox(val value: Short) : Number(), Comparable<Short> {
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_boxShort")
|
||||
fun boxShort(value: Short) = ShortBox(value)
|
||||
|
||||
class IntBox(val value: Int) : Number(), Comparable<Int> {
|
||||
@@ -130,6 +134,7 @@ class IntBox(val value: Int) : Number(), Comparable<Int> {
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_boxInt")
|
||||
fun boxInt(value: Int) = IntBox(value)
|
||||
|
||||
class LongBox(val value: Long) : Number(), Comparable<Long> {
|
||||
@@ -156,6 +161,7 @@ class LongBox(val value: Long) : Number(), Comparable<Long> {
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_boxLong")
|
||||
fun boxLong(value: Long) = LongBox(value)
|
||||
|
||||
class FloatBox(val value: Float) : Number(), Comparable<Float> {
|
||||
@@ -182,6 +188,7 @@ class FloatBox(val value: Float) : Number(), Comparable<Float> {
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_boxFloat")
|
||||
fun boxFloat(value: Float) = FloatBox(value)
|
||||
|
||||
class DoubleBox(val value: Double) : Number(), Comparable<Double> {
|
||||
@@ -208,4 +215,5 @@ class DoubleBox(val value: Double) : Number(), Comparable<Double> {
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
@ExportForCppRuntime("Kotlin_boxDouble")
|
||||
fun boxDouble(value: Double) = DoubleBox(value)
|
||||
|
||||
Reference in New Issue
Block a user