Implement -produce framework option

It compiles Kotlin code to Objective-C framework
(also importable to Swift)
This commit is contained in:
Svyatoslav Scherbina
2017-11-22 17:30:31 +03:00
committed by SvyatoslavScherbina
parent 6114a5b35c
commit d745e80135
40 changed files with 3999 additions and 136 deletions
+5
View File
@@ -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)...);
+5
View File
@@ -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
+38 -11
View File
@@ -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;
+13
View File
@@ -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.
//
+25
View File
@@ -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
+941
View File
@@ -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
+3 -1
View File
@@ -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 {
+3 -3
View File
@@ -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;
+9
View File
@@ -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
}
}
+17
View File
@@ -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)