Improve Kotlin collections support when producing framework

* Represent MutableList, Set, Map as standard Objective-C collections
* Represent MutableSet and MutableMap as Obj-C collection subclasses
* Use Objective-C generics to represent collection element type
* Make type mapping more correct
This commit is contained in:
Svyatoslav Scherbina
2018-01-26 12:50:34 +03:00
committed by SvyatoslavScherbina
parent d228e77c0c
commit b15c94855e
17 changed files with 1428 additions and 208 deletions
@@ -1,41 +0,0 @@
/*
* 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.
*/
package kotlinx.cinterop
import konan.internal.ExportForCppRuntime
// TODO: it it actually not related to cinterop.
@ExportTypeInfo("theNSArrayListTypeInfo")
internal class NSArrayList : AbstractList<Any?> {
// FIXME: override methods of Any.
@konan.internal.ExportForCppRuntime("Kotlin_NSArrayList_constructor")
constructor() : super()
override val size: Int get() = getSize()
@SymbolName("Kotlin_NSArrayList_getSize")
private external fun getSize(): Int
@SymbolName("Kotlin_NSArrayList_getElement")
external override fun get(index: Int): Any?
}
@ExportForCppRuntime private fun Kotlin_List_get(list: List<*>, index: Int): Any? = list.get(index)
@ExportForCppRuntime private fun Kotlin_List_getSize(list: List<*>): Int = list.size
@@ -398,6 +398,11 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val Kotlin_ObjCExport_refFromObjC by lazyRtFunction
val Kotlin_Interop_CreateNSStringFromKString by lazyRtFunction
val Kotlin_Interop_CreateNSArrayFromKList by lazyRtFunction
val Kotlin_Interop_CreateNSMutableArrayFromKList by lazyRtFunction
val Kotlin_Interop_CreateNSSetFromKSet by lazyRtFunction
val Kotlin_Interop_CreateKotlinMutableSetFromKSet by lazyRtFunction
val Kotlin_Interop_CreateNSDictionaryFromKMap by lazyRtFunction
val Kotlin_Interop_CreateKotlinMutableDictonaryFromKMap by lazyRtFunction
val Kotlin_ObjCExport_convertUnit by lazyRtFunction
val Kotlin_ObjCExport_GetAssociatedObject by lazyRtFunction
val Kotlin_ObjCExport_AbstractMethodCalled by lazyRtFunction
@@ -70,6 +70,11 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) {
val classObjectType = codegen.runtime.getStructType("_class_t")
fun exportClass(name: String) {
context.llvm.usedGlobals += getClassGlobal(name, isMetaclass = false).llvm
context.llvm.usedGlobals += getClassGlobal(name, isMetaclass = true).llvm
}
private fun getClassGlobal(name: String, isMetaclass: Boolean): ConstPointer {
val prefix = if (isMetaclass) {
"OBJC_METACLASS_\$_"
@@ -178,6 +178,9 @@ internal class ObjCExportCodeGenerator(
dataGenerator.emitEmptyClass(namer.getPackageName(fqName), namer.kotlinAnyName)
}
dataGenerator.exportClass("KotlinMutableSet")
dataGenerator.exportClass("KotlinMutableDictionary")
emitSpecialClassesConvertions()
objCTypeAdapters += createTypeAdapter(context.builtIns.any)
@@ -375,7 +378,7 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(objCValueType: ObjCValueTyp
private fun ObjCExportCodeGenerator.emitFunctionConverters() {
val generator = BlockAdapterToFunctionGenerator(this)
(0 .. 22).forEach { numberOfParameters ->
(0 .. mapper.maxFunctionTypeParameterCount).forEach { numberOfParameters ->
val converter = generator.run { generateConvertFunctionToBlock(numberOfParameters) }
setObjCExportTypeInfo(context.builtIns.getFunction(numberOfParameters), constPointer(converter))
}
@@ -399,7 +402,7 @@ private fun ObjCExportCodeGenerator.emitKotlinFunctionAdaptersToBlock() {
val ptr = staticData.placeGlobalArray(
"",
pointerType(runtime.typeInfoType),
(0 .. 22).map {
(0 .. mapper.maxFunctionTypeParameterCount).map {
generateKotlinFunctionAdapterToBlock(it)
}
).pointer.getElementPtr(0)
@@ -419,6 +422,31 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
constPointer(context.llvm.Kotlin_Interop_CreateNSArrayFromKList)
)
setObjCExportTypeInfo(
context.builtIns.mutableList,
constPointer(context.llvm.Kotlin_Interop_CreateNSMutableArrayFromKList)
)
setObjCExportTypeInfo(
context.builtIns.set,
constPointer(context.llvm.Kotlin_Interop_CreateNSSetFromKSet)
)
setObjCExportTypeInfo(
context.builtIns.mutableSet,
constPointer(context.llvm.Kotlin_Interop_CreateKotlinMutableSetFromKSet)
)
setObjCExportTypeInfo(
context.builtIns.map,
constPointer(context.llvm.Kotlin_Interop_CreateNSDictionaryFromKMap)
)
setObjCExportTypeInfo(
context.builtIns.mutableMap,
constPointer(context.llvm.Kotlin_Interop_CreateKotlinMutableDictonaryFromKMap)
)
ObjCValueType.values().forEach {
emitBoxConverter(it)
}
@@ -23,10 +23,10 @@ import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.descriptorUtil.*
@@ -38,13 +38,10 @@ import org.jetbrains.kotlin.utils.addIfNotNull
internal class ObjCExportHeaderGenerator(val context: Context) {
val mapper: ObjCExportMapper = object : ObjCExportMapper {
override fun isRepresentedAsObjCInterface(descriptor: ClassDescriptor): Boolean {
val objCType = mapReferenceType(descriptor.defaultType)
return objCType is ObjCClassType && objCType.className == translateClassName(descriptor)
}
override fun getCategoryMembersFor(descriptor: ClassDescriptor) =
extensions[descriptor].orEmpty()
override val specialMappedTypes get() = customTypeMappers.keys
}
val namer = ObjCExportNamer(context, mapper)
@@ -52,6 +49,43 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
val generatedClasses = mutableSetOf<ClassDescriptor>()
val topLevel = mutableMapOf<FqName, MutableList<CallableMemberDescriptor>>()
val customTypeMappers: Map<ClassDescriptor, CustomTypeMapper> = with (context.builtIns) {
val result = mutableListOf<CustomTypeMapper>()
val generator = this@ObjCExportHeaderGenerator
result += CustomTypeMapper.Collection(generator, list, "NSArray")
result += CustomTypeMapper.Collection(generator, mutableList, "NSMutableArray")
result += CustomTypeMapper.Collection(generator, set, "NSSet")
result += CustomTypeMapper.Collection(generator, mutableSet, namer.mutableSetName)
result += CustomTypeMapper.Collection(generator, map, "NSDictionary")
result += CustomTypeMapper.Collection(generator, mutableMap, namer.mutableMapName)
for (descriptor in listOf(boolean, char, byte, short, int, long, float, double)) {
// TODO: Kotlin code doesn't have any checkcasts on unboxing,
// so it is possible that it expects boxed number of other type and unboxes it incorrectly.
// TODO: NSNumber seem to have different equality semantics.
result += CustomTypeMapper.Simple(descriptor, "NSNumber")
}
result += CustomTypeMapper.Simple(string, "NSString")
(0 .. mapper.maxFunctionTypeParameterCount).forEach {
result += CustomTypeMapper.Function(generator, it)
}
result.associateBy { it.mappedClassDescriptor }
}
val hiddenTypes: Set<ClassDescriptor> = run {
val customMappedTypes = customTypeMappers.keys
customMappedTypes
.flatMap { it.getAllSuperClassifiers().toList() }
.map { it as ClassDescriptor }
.toSet() - customMappedTypes
}
private val kotlinAnyName = namer.kotlinAnyName
private val stubs = mutableListOf<Stub>()
@@ -175,7 +209,7 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
val name = namer.getPackageName(packageFqName)
stubs.addBuiltBy {
+"__attribute__((objc_subclassing_restricted))"
+"@interface $name : KotlinBase" // TODO: stop inheriting KotlinBase.
+"@interface $name : ${namer.kotlinAnyName}" // TODO: stop inheriting KotlinBase.
translateMembers(declarations)
@@ -443,6 +477,21 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
add("@end;")
add("")
// TODO: add comment to the header.
add("@interface $kotlinAnyName (${kotlinAnyName}Copying) <NSCopying>")
add("@end;")
add("")
add("__attribute__((objc_runtime_name(\"KotlinMutableSet\")))")
add("@interface ${namer.mutableSetName}<ObjectType> : NSMutableSet<ObjectType>") // TODO: only if appears
add("@end;")
add("")
add("__attribute__((objc_runtime_name(\"KotlinMutableDictionary\")))")
add("@interface ${namer.mutableMapName}<KeyType, ObjectType> : NSMutableDictionary<KeyType, ObjectType>") // TODO: only if appears
add("@end;")
add("")
stubs.forEach {
addAll(it.lines)
add("")
@@ -452,19 +501,34 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
}
}
private sealed class ObjCType {
internal sealed class ObjCType {
final override fun toString(): String = this.render()
open fun render(varName: String): String = "${this.render()} $varName"
abstract fun render(): String
}
private sealed class ObjCReferenceType(kotlinType: KotlinType) : ObjCType() {
internal sealed class ObjCReferenceType(kotlinType: KotlinType) : ObjCType() {
val attributes = if (TypeUtils.isNullableType(kotlinType)) " _Nullable" else ""
}
private class ObjCClassType(kotlinType: KotlinType, val className: String) : ObjCReferenceType(kotlinType) {
override fun render() = "$className*$attributes"
private class ObjCClassType(
kotlinType: KotlinType,
val className: String,
val typeArguments: List<ObjCReferenceType> = emptyList()
) : ObjCReferenceType(kotlinType) {
override fun render() = buildString {
append(className)
if (typeArguments.isNotEmpty()) {
append("<")
typeArguments.joinTo(this) { it.render() }
append(">")
}
append('*')
append(attributes)
}
}
private class ObjCProtocolType(kotlinType: KotlinType, val protocolName: String) : ObjCReferenceType(kotlinType) {
@@ -502,43 +566,101 @@ private object ObjCVoidType : ObjCType() {
override fun render(varName: String) = error("variables can't have `void` type")
}
internal interface CustomTypeMapper {
val mappedClassDescriptor: ClassDescriptor
fun mapType(type: KotlinType, mappedSuperType: KotlinType): ObjCReferenceType
class Simple(
override val mappedClassDescriptor: ClassDescriptor,
private val objCClassName: String
) : CustomTypeMapper {
override fun mapType(type: KotlinType, mappedSuperType: KotlinType): ObjCReferenceType =
ObjCClassType(type, objCClassName)
}
class Collection(
private val generator: ObjCExportHeaderGenerator,
override val mappedClassDescriptor: ClassDescriptor,
private val objCClassName: String
) : CustomTypeMapper {
override fun mapType(type: KotlinType, mappedSuperType: KotlinType): ObjCReferenceType {
val typeArguments = mappedSuperType.arguments.map {
val argument = it.type
if (TypeUtils.isNullableType(argument)) {
// Kotlin `null` keys and values are represented as `NSNull` singleton.
ObjCIdType(generator.context.builtIns.anyType)
} else {
generator.mapReferenceType(argument)
}
}
return ObjCClassType(type, objCClassName, typeArguments)
}
}
class Function(
private val generator: ObjCExportHeaderGenerator,
parameterCount: Int
) : CustomTypeMapper {
override val mappedClassDescriptor = generator.context.builtIns.getFunction(parameterCount)
override fun mapType(type: KotlinType, mappedSuperType: KotlinType): ObjCReferenceType {
val functionType = mappedSuperType
val returnType = functionType.getReturnTypeFromFunctionType()
val parameterTypes = listOfNotNull(functionType.getReceiverTypeFromFunctionType()) +
functionType.getValueParameterTypesFromFunctionType().map { it.type }
return ObjCBlockPointerType(
type,
generator.mapReferenceType(returnType),
parameterTypes.map { generator.mapReferenceType(it) }
)
}
}
}
private fun ObjCExportHeaderGenerator.mapReferenceType(kotlinType: KotlinType): ObjCReferenceType {
// TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* <SomeInterface>`
val typeToMapper = (listOf(kotlinType) + kotlinType.supertypes()).mapNotNull { type ->
val mapper = customTypeMappers[type.constructor.declarationDescriptor]
if (mapper != null) {
type to mapper
} else {
null
}
}.toMap()
val mostSpecificTypeToMapper = typeToMapper.filter { (_, mapper) ->
typeToMapper.values.all { it.mappedClassDescriptor == mapper.mappedClassDescriptor ||
!it.mappedClassDescriptor.isSubclassOf(mapper.mappedClassDescriptor) }
// E.g. if both List and MutableList are present, then retain only MutableList.
}
if (mostSpecificTypeToMapper.size > 1) {
val types = mostSpecificTypeToMapper.keys.toList()
val firstType = types[0]
val secondType = types[1]
context.reportCompilationWarning(
"Exposed type '$kotlinType' is '$firstType' and '$secondType' at the same time. " +
"This most likely wouldn't work as expected.")
// TODO: the same warning for such classes.
}
mostSpecificTypeToMapper.entries.firstOrNull()?.let { (type, mapper) ->
return mapper.mapType(kotlinType, type)
}
val classDescriptor = kotlinType.getErasedTypeClass()
if (classDescriptor.isSubclassOf(context.builtIns.list)) {
return ObjCClassType(kotlinType, "NSArray")
}
// TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* <SomeInterface>`
// TODO: Kotlin code doesn't have any checkcasts on unboxing,
// so it is possible that it expects boxed number of other type and unboxes it incorrectly.
if (classDescriptor.isSubclassOf(context.builtIns.number) ||
classDescriptor == context.builtIns.boolean ||
classDescriptor == context.builtIns.char) return ObjCClassType(kotlinType, "NSNumber")
when (classDescriptor) {
context.builtIns.any -> return ObjCIdType(kotlinType)
context.builtIns.string -> return ObjCClassType(kotlinType, "NSString")
}
val functionType = if (kotlinType.isFunctionType) {
kotlinType
} else {
kotlinType.supertypes().firstOrNull { it.isFunctionType }
// TODO: may be incorrect if type has more then one function supertype.
}
if (functionType != null) {
val returnType = functionType.getReturnTypeFromFunctionType()
val parameterTypes = listOfNotNull(functionType.getReceiverTypeFromFunctionType()) +
functionType.getValueParameterTypesFromFunctionType().map { it.type }
return ObjCBlockPointerType(
kotlinType,
mapReferenceType(returnType),
parameterTypes.map { mapReferenceType(it) }
)
if (classDescriptor == context.builtIns.any || classDescriptor in hiddenTypes) {
return ObjCIdType(kotlinType)
}
if (classDescriptor !in generatedClasses) {
@@ -21,19 +21,28 @@ import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.correspondingValueType
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.isObjCObjectType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isUnit
internal interface ObjCExportMapper {
fun isRepresentedAsObjCInterface(descriptor: ClassDescriptor): Boolean
fun getCategoryMembersFor(descriptor: ClassDescriptor): List<CallableMemberDescriptor>
val maxFunctionTypeParameterCount get() = 22
val specialMappedTypes: Set<ClassDescriptor>
}
private fun ObjCExportMapper.isRepresentedAsObjCInterface(descriptor: ClassDescriptor): Boolean =
!descriptor.isInterface && !isSpecialMapped(descriptor)
private fun ObjCExportMapper.isSpecialMapped(descriptor: ClassDescriptor): Boolean =
descriptor.getAllSuperClassifiers().any { it in specialMappedTypes }
internal fun ObjCExportMapper.getClassIfCategory(descriptor: CallableMemberDescriptor): ClassDescriptor? {
if (descriptor.dispatchReceiverParameter != null) return null
@@ -55,7 +64,7 @@ internal fun ObjCExportMapper.shouldBeExposed(descriptor: ClassDescriptor): Bool
descriptor.isEffectivelyPublicApi && !descriptor.defaultType.isObjCObjectType() && when (descriptor.kind) {
ClassKind.CLASS, ClassKind.INTERFACE, ClassKind.ENUM_CLASS, ClassKind.OBJECT -> true
ClassKind.ENUM_ENTRY, ClassKind.ANNOTATION_CLASS -> false
}
} && !isSpecialMapped(descriptor)
private fun ObjCExportMapper.isBase(descriptor: CallableMemberDescriptor): Boolean =
descriptor.overriddenDescriptors.all { !shouldBeExposed(it) }
@@ -34,6 +34,9 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
private val commonPackageSegments = context.moduleDescriptor.guessMainPackage().pathSegments()
private val topLevelNamePrefix = context.moduleDescriptor.namePrefix
val mutableSetName = "${topLevelNamePrefix}MutableSet"
val mutableMapName = "${topLevelNamePrefix}MutableDictionary"
private val methodSelectors = object : Mapping<FunctionDescriptor, String>() {
// Try to avoid clashing with critical NSObject instance methods:
@@ -231,6 +234,8 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
val any = context.builtIns.any
classNames.forceAssign(any, kotlinAnyName)
classNames.forceAssign(context.builtIns.mutableSet, mutableSetName)
classNames.forceAssign(context.builtIns.mutableMap, mutableMapName)
fun ClassDescriptor.method(name: String) =
this.unsubstitutedMemberScope.getContributedFunctions(
+36
View File
@@ -0,0 +1,36 @@
#ifndef RUNTIME_OBJCEXPORT_H
#define RUNTIME_OBJCEXPORT_H
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import "Types.h"
#import "Memory.h"
extern "C" id objc_retain(id self);
extern "C" void objc_release(id self);
inline static bool HasAssociatedObjectField(ObjHeader* obj) {
return HasReservedObjectTail(obj);
}
inline static id GetAssociatedObject(ObjHeader* obj) {
return *reinterpret_cast<id*>(GetReservedObjectTail(obj));
}
inline static void SetAssociatedObject(ObjHeader* obj, id value) {
*reinterpret_cast<id*>(GetReservedObjectTail(obj)) = value;
}
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj);
@protocol ConvertibleToKotlin
@required
-(KRef)toKotlin:(KRef*)OBJ_RESULT;
@end;
#endif // KONAN_OBJC_INTEROP
#endif // RUNTIME_OBJCEXPORT_H
+33 -116
View File
@@ -20,14 +20,14 @@
#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 <dispatch/dispatch.h>
#import "ObjCExport.h"
#import "MemoryPrivate.hpp"
#import "Runtime.h"
#import "Utils.h"
@@ -92,18 +92,6 @@ 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;
}
static inline id GetAssociatedObject(ObjHeader* obj) {
return *reinterpret_cast<id*>(GetReservedObjectTail(obj));
}
extern "C" id Kotlin_ObjCExport_GetAssociatedObject(ObjHeader* obj) {
return GetAssociatedObject(obj);
}
@@ -118,12 +106,7 @@ inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* type
static Class getOrCreateClass(const TypeInfo* typeInfo);
static void initializeClass(Class clazz);
@protocol ConvertibleToKotlin
@required
-(KRef)toKotlin:(KRef*)OBJ_RESULT;
@end;
@interface KotlinBase : NSObject <ConvertibleToKotlin>
@interface KotlinBase : NSObject <ConvertibleToKotlin, NSCopying>
@end;
@implementation KotlinBase {
@@ -199,6 +182,11 @@ static void initializeClass(Class clazz);
[super release];
}
- (instancetype)copyWithZone:(NSZone *)zone {
// TODO: write documentation.
return [self retain];
}
@end;
extern "C" void Kotlin_ObjCExport_releaseReservedObjectTail(ObjHeader* obj) {
@@ -324,9 +312,7 @@ static void initializeClass(Class clazz) {
@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)
@@ -351,27 +337,6 @@ extern "C" OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, NSString* str);
}
@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);
@@ -425,47 +390,6 @@ static Class __NSCFBooleanClass = nullptr;
}
@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;
@@ -567,10 +491,6 @@ static const TypeInfo* getFunctionTypeInfoForBlock(id block) {
@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) {
@@ -603,14 +523,35 @@ static id convertKotlinObject(ObjHeader* obj) {
return [clazz createWrapper:obj];
}
extern "C" id Kotlin_Interop_CreateNSStringFromKString(KRef str);
static convertReferenceToObjC findConverterFromInterfaces(const TypeInfo* typeInfo) {
const TypeInfo* foundTypeInfo = nullptr;
for (int i = 0; i < typeInfo->implementedInterfacesCount_; ++i) {
const TypeInfo* interfaceTypeInfo = typeInfo->implementedInterfaces_[i];
if (interfaceTypeInfo->writableInfo_->objCExport.convert != nullptr) {
if (foundTypeInfo == nullptr || IsSubInterface(interfaceTypeInfo, foundTypeInfo)) {
foundTypeInfo = interfaceTypeInfo;
} else if (!IsSubInterface(foundTypeInfo, interfaceTypeInfo)) {
[NSException raise:NSGenericException
format:@"Can't convert to Objective-C Kotlin object that is '%@' and '%@' and the same time",
Kotlin_Interop_CreateNSStringFromKString(foundTypeInfo->relativeName_),
Kotlin_Interop_CreateNSStringFromKString(interfaceTypeInfo->relativeName_)];
}
}
}
return foundTypeInfo == nullptr ?
nullptr :
(convertReferenceToObjC)foundTypeInfo->writableInfo_->objCExport.convert;
}
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 = (convertReferenceToObjC)typeInfo->implementedInterfaces_[i]->writableInfo_->objCExport.convert;
if (converter != nullptr) break;
}
converter = findConverterFromInterfaces(typeInfo);
if (converter == nullptr) {
getOrCreateClass(typeInfo);
@@ -622,18 +563,6 @@ static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj) {
return converter(obj);
}
extern "C" KInt Kotlin_NSArrayList_getSize(ObjHeader* obj) {
NSArray* array = (NSArray*) GetAssociatedObject(obj);
return [array count];
}
extern "C" OBJ_GETTER(Kotlin_NSArrayList_getElement, ObjHeader* obj, KInt index) {
NSArray* array = (NSArray*) 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,
@@ -968,16 +897,4 @@ extern "C" void Kotlin_ObjCExport_AbstractMethodCalled(id self, SEL selector) {
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
@@ -0,0 +1,806 @@
#import "Memory.h"
#import "Types.h"
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSNull.h>
#import "Exceptions.h"
#import "Runtime.h"
#import "ObjCExport.h"
extern "C" {
// Imports from ObjCExportUtils.kt:
OBJ_GETTER0(Kotlin_NSArrayAsKList_create);
OBJ_GETTER0(Kotlin_NSMutableArrayAsKMutableList_create);
OBJ_GETTER0(Kotlin_NSEnumeratorAsKIterator_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);
void Kotlin_NSEnumeratorAsKIterator_done(KRef thiz);
void Kotlin_NSEnumeratorAsKIterator_setNext(KRef thiz, KRef value);
void Kotlin_ObjCExport_ThrowCollectionTooLarge();
void Kotlin_ObjCExport_ThrowCollectionConcurrentModification();
} // extern "C"
// Objective-C collections can't store `nil`, and the common convention is to use `NSNull.null` instead.
// Follow the convention when converting Kotlin `null`:
static inline id refToObjCOrNSNull(KRef obj) {
if (obj == nullptr) {
return NSNull.null;
} else {
return Kotlin_ObjCExport_refToObjC(obj);
}
}
static inline OBJ_GETTER(refFromObjCOrNSNull, id obj) {
if (obj == NSNull.null) {
RETURN_OBJ(nullptr);
} else {
RETURN_RESULT_OF(Kotlin_ObjCExport_refFromObjC, obj);
}
}
static inline KInt objCCapacityToKotlin(NSUInteger capacity) {
KInt max = std::numeric_limits<KInt>::max();
return capacity > max ? max : capacity;
}
static inline KInt objCSizeToKotlinOrThrow(NSUInteger size) {
if (size > std::numeric_limits<KInt>::max()) {
Kotlin_ObjCExport_ThrowCollectionTooLarge();
}
return size;
}
static inline KInt objCIndexToKotlinOrThrow(NSUInteger index) {
if (index > std::numeric_limits<KInt>::max()) {
ThrowArrayIndexOutOfBoundsException();
}
return index;
}
static inline OBJ_GETTER(invokeAndAssociate, KRef (*func)(KRef* result), id obj) {
Kotlin_initRuntimeIfNeeded();
KRef kotlinObj = func(OBJ_RESULT);
RuntimeAssert(HasAssociatedObjectField(kotlinObj), "");
SetAssociatedObject(kotlinObj, obj);
return kotlinObj;
}
@interface NSArray (NSArrayToKotlin) <ConvertibleToKotlin>
@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) <ConvertibleToKotlin>
@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) <ConvertibleToKotlin>
@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) <ConvertibleToKotlin>
@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 {
KRef iterator;
}
-(void)dealloc {
UpdateRef(&iterator, nullptr);
[super dealloc];
}
+(id)createWithKIterator:(KRef)iterator {
KIteratorAsNSEnumerator* result = [[[KIteratorAsNSEnumerator alloc] init] autorelease];
UpdateRef(&result->iterator, iterator);
return result;
}
- (id)nextObject {
if (Kotlin_Iterator_hasNext(iterator)) {
ObjHolder holder;
return refToObjCOrNSNull(Kotlin_Iterator_next(iterator, holder.slot()));
} else {
return nullptr;
}
}
@end;
@interface KListAsNSArray : NSArray <ConvertibleToKotlin>
@end;
@implementation KListAsNSArray {
KRef list;
}
-(void)dealloc {
UpdateRef(&list, nullptr);
[super dealloc];
}
+(id)createWithKList:(KRef)list {
KListAsNSArray* result = [[[KListAsNSArray alloc] init] autorelease];
UpdateRef(&result->list, list);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(list);
}
-(id)objectAtIndex:(NSUInteger)index {
ObjHolder kotlinValueHolder;
KRef kotlinValue = Kotlin_List_get(list, index, kotlinValueHolder.slot());
return refToObjCOrNSNull(kotlinValue);
}
-(NSUInteger)count {
return Kotlin_Collection_getSize(list);
}
@end;
@interface KMutableListAsNSMutableArray : NSMutableArray <ConvertibleToKotlin>
@end;
@implementation KMutableListAsNSMutableArray {
KRef list;
}
-(void)dealloc {
UpdateRef(&list, nullptr);
[super dealloc];
}
+(id)createWithKList:(KRef)list {
KMutableListAsNSMutableArray* result = [[[KMutableListAsNSMutableArray alloc] init] autorelease];
UpdateRef(&result->list, list);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(list);
}
-(id)objectAtIndex:(NSUInteger)index {
ObjHolder kotlinValueHolder;
KRef kotlinValue = Kotlin_List_get(list, index, kotlinValueHolder.slot());
return refToObjCOrNSNull(kotlinValue);
}
-(NSUInteger)count {
return Kotlin_Collection_getSize(list);
}
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
ObjHolder holder;
KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot());
Kotlin_MutableList_addObjectAtIndex(list, objCIndexToKotlinOrThrow(index), kotlinObject);
}
- (void)removeObjectAtIndex:(NSUInteger)index {
Kotlin_MutableList_removeObjectAtIndex(list, objCIndexToKotlinOrThrow(index));
}
- (void)addObject:(id)anObject {
ObjHolder holder;
Kotlin_MutableCollection_addObject(list, refFromObjCOrNSNull(anObject, holder.slot()));
}
- (void)removeLastObject {
Kotlin_MutableList_removeLastObject(list);
}
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
ObjHolder holder;
KRef kotlinObject = refFromObjCOrNSNull(anObject, holder.slot());
Kotlin_MutableList_setObject(list, objCIndexToKotlinOrThrow(index), kotlinObject);
}
@end;
@interface KSetAsNSSet : NSSet <ConvertibleToKotlin>
@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());
return refToObjCOrNSNull(result);
}
}
@implementation KSetAsNSSet {
KRef set;
}
-(void)dealloc {
UpdateRef(&set, nullptr);
[super dealloc];
}
+(id)createWithKSet:(KRef)set {
KSetAsNSSet* result = [[[KSetAsNSSet alloc] init] autorelease];
UpdateRef(&result->set, set);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(set);
}
-(NSUInteger) count {
return Kotlin_Collection_getSize(set);
}
- (id)member:(id)object {
return KSet_getElement(set, object);
}
// Not mandatory, just an optimization:
- (BOOL)containsObject:(id)anObject {
ObjHolder holder;
return Kotlin_Set_contains(set, refFromObjCOrNSNull(anObject, holder.slot()));
}
- (NSEnumerator*)objectEnumerator {
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(set, holder.slot())];
}
@end;
@interface KotlinMutableSet : NSMutableSet <ConvertibleToKotlin>
@end;
@implementation KotlinMutableSet {
KRef set;
}
-(instancetype)init {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
Kotlin_MutableSet_createWithCapacity(8, &self->set);
}
return self;
}
- (instancetype)initWithCapacity:(NSUInteger)numItems {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
Kotlin_MutableSet_createWithCapacity(objCCapacityToKotlin(numItems), &self->set);
}
return self;
}
- (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 {
UpdateRef(&set, nullptr);
[super dealloc];
}
+(id)createWithKSet:(KRef)set {
KotlinMutableSet* result = [[[super allocWithZone:nullptr] init] autorelease];
UpdateRef(&result->set, set);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(set);
}
-(NSUInteger) count {
return Kotlin_Collection_getSize(set);
}
- (id)member:(id)object {
return KSet_getElement(set, object);
}
// Not mandatory, just an optimization:
- (BOOL)containsObject:(id)anObject {
ObjHolder holder;
return Kotlin_Set_contains(set, refFromObjCOrNSNull(anObject, holder.slot()));
}
- (NSEnumerator*)objectEnumerator {
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Set_iterator(set, holder.slot())];
}
- (void)addObject:(id)object {
ObjHolder holder;
Kotlin_MutableCollection_addObject(set, refFromObjCOrNSNull(object, holder.slot()));
}
- (void)removeObject:(id)object {
ObjHolder holder;
Kotlin_MutableCollection_removeObject(set, refFromObjCOrNSNull(object, holder.slot()));
}
@end;
@interface KMapAsNSDictionary : NSDictionary <ConvertibleToKotlin>
@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 {
KRef map;
}
-(void)dealloc {
UpdateRef(&map, nullptr);
[super dealloc];
}
+(id)createWithKMap:(KRef)map {
KMapAsNSDictionary* result = [[[KMapAsNSDictionary alloc] init] autorelease];
UpdateRef(&result->map, map);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(map);
}
// 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 {
return Kotlin_Map_getSize(map);
}
- (id)objectForKey:(id)aKey {
return KMap_get(map, aKey);
}
- (NSEnumerator *)keyEnumerator {
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(map, holder.slot())];
}
@end;
@interface KotlinMutableDictionary : NSMutableDictionary <ConvertibleToKotlin>
@end;
@implementation KotlinMutableDictionary {
KRef map;
}
-(void)dealloc {
UpdateRef(&map, nullptr);
[super dealloc];
}
-(instancetype)init {
if (self = [super init]) {
Kotlin_initRuntimeIfNeeded();
Kotlin_MutableMap_createWithCapacity(8, &self->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_MutableMap_createWithCapacity(objCCapacityToKotlin(numItems), &self->map);
}
return self;
}
+(id)createWithKMap:(KRef)map {
KotlinMutableDictionary* result = [[[KotlinMutableDictionary alloc] init] autorelease];
UpdateRef(&result->map, map);
return result;
}
-(KRef)toKotlin:(KRef*)OBJ_RESULT {
RETURN_OBJ(map);
}
-(NSUInteger) count {
return Kotlin_Map_getSize(map);
}
- (id)objectForKey:(id)aKey {
return KMap_get(map, aKey);
}
- (NSEnumerator *)keyEnumerator {
ObjHolder holder;
return [KIteratorAsNSEnumerator createWithKIterator:Kotlin_Map_keyIterator(map, holder.slot())];
}
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey {
ObjHolder keyHolder, valueHolder;
id keyCopy = [aKey copyWithZone:nullptr]; // Correspond to the expected NSMutableDictionary behaviour.
KRef kotlinKey = refFromObjCOrNSNull(keyCopy, keyHolder.slot());
objc_release(keyCopy);
KRef kotlinValue = refFromObjCOrNSNull(anObject, valueHolder.slot());
Kotlin_MutableMap_set(map, kotlinKey, kotlinValue);
}
- (void)removeObjectForKey:(id)aKey {
ObjHolder holder;
KRef kotlinKey = refFromObjCOrNSNull(aKey, holder.slot());
Kotlin_MutableMap_remove(map, 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_CreateNSArrayFromKList(KRef obj) {
return [KListAsNSArray createWithKList:obj];
}
extern "C" id Kotlin_Interop_CreateNSMutableArrayFromKList(KRef obj) {
return [KMutableListAsNSMutableArray createWithKList:obj];
}
extern "C" id Kotlin_Interop_CreateNSSetFromKSet(KRef obj) {
return [KSetAsNSSet createWithKSet:obj];
}
extern "C" id Kotlin_Interop_CreateKotlinMutableSetFromKSet(KRef obj) {
return [KotlinMutableSet createWithKSet:obj];
}
extern "C" id Kotlin_Interop_CreateNSDictionaryFromKMap(KRef obj) {
return [KMapAsNSDictionary createWithKMap:obj];
}
extern "C" id Kotlin_Interop_CreateKotlinMutableDictonaryFromKMap(KRef obj) {
return [KotlinMutableDictionary createWithKMap:obj];
}
// Imported to ObjCExportUtils.kt:
extern "C" KInt Kotlin_NSArrayAsKList_getSize(KRef obj) {
NSArray* array = (NSArray*) GetAssociatedObject(obj);
return objCSizeToKotlinOrThrow([array count]);
}
extern "C" OBJ_GETTER(Kotlin_NSArrayAsKList_get, KRef obj, KInt index) {
NSArray* array = (NSArray*) GetAssociatedObject(obj);
id element = [array objectAtIndex:index];
RETURN_RESULT_OF(refFromObjCOrNSNull, element);
}
extern "C" void Kotlin_NSMutableArrayAsKMutableList_add(KRef thiz, KInt index, KRef element) {
NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz);
[mutableArray insertObject:refToObjCOrNSNull(element) atIndex:index];
}
extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_removeAt, KRef thiz, KInt index) {
NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz);
KRef res = refFromObjCOrNSNull([mutableArray objectAtIndex:index], OBJ_RESULT);
[mutableArray removeObjectAtIndex:index];
return res;
}
extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_set, KRef thiz, KInt index, KRef element) {
NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz);
KRef res = refFromObjCOrNSNull([mutableArray objectAtIndex:index], OBJ_RESULT);
[mutableArray replaceObjectAtIndex:index withObject:refToObjCOrNSNull(element)];
return res;
}
extern "C" void Kotlin_NSEnumeratorAsKIterator_computeNext(KRef thiz) {
NSEnumerator* enumerator = (NSEnumerator*) GetAssociatedObject(thiz);
id next = [enumerator nextObject];
if (next == nullptr) {
Kotlin_NSEnumeratorAsKIterator_done(thiz);
} else {
ObjHolder holder;
Kotlin_NSEnumeratorAsKIterator_setNext(thiz, refFromObjCOrNSNull(next, holder.slot()));
}
}
extern "C" KInt Kotlin_NSSetAsKSet_getSize(KRef thiz) {
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
return objCSizeToKotlinOrThrow(set.count);
}
extern "C" KBoolean Kotlin_NSSetAsKSet_contains(KRef thiz, KRef element) {
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
return [set containsObject:refToObjCOrNSNull(element)];
}
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_getElement, KRef thiz, KRef element) {
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
id res = [set member:refToObjCOrNSNull(element)];
RETURN_RESULT_OF(refFromObjCOrNSNull, res);
}
static inline OBJ_GETTER(CreateKIteratorFromNSEnumerator, NSEnumerator* enumerator) {
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSEnumeratorAsKIterator_create, objc_retain(enumerator));
}
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_iterator, KRef thiz) {
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [set objectEnumerator]);
}
extern "C" KInt Kotlin_NSDictionaryAsKMap_getSize(KRef thiz) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
return objCSizeToKotlinOrThrow(dict.count);
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsKey(KRef thiz, KRef key) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
return [dict objectForKey:refToObjCOrNSNull(key)] != nullptr;
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsValue(KRef thiz, KRef value) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
id objCValue = refToObjCOrNSNull(value);
for (id key in dict) {
if ([[dict objectForKey:key] isEqual:objCValue]) {
return true;
}
}
return false;
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_get, KRef thiz, KRef key) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
id value = [dict objectForKey:refToObjCOrNSNull(key)];
RETURN_RESULT_OF(refFromObjCOrNSNull, value);
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_getOrThrowConcurrentModification, KRef thiz, KRef key) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
id value = [dict objectForKey:refToObjCOrNSNull(key)];
if (value == nullptr) {
Kotlin_ObjCExport_ThrowCollectionConcurrentModification();
}
RETURN_RESULT_OF(refFromObjCOrNSNull, value);
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsEntry(KRef thiz, KRef key, KRef value) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
return [refToObjCOrNSNull(value) isEqual:[dict objectForKey:refToObjCOrNSNull(key)]];
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_keyIterator, KRef thiz) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [dict keyEnumerator]);
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_valueIterator, KRef thiz) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [dict objectEnumerator]);
}
#else // KONAN_OBJC_INTEROP
extern "C" KInt Kotlin_NSArrayAsKList_getSize(KRef obj) {
RuntimeAssert(false, "Objective-C interop is disabled");
return -1;
}
extern "C" OBJ_GETTER(Kotlin_NSArrayAsKList_get, KRef obj, KInt index) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" void Kotlin_NSMutableArrayAsKMutableList_add(KRef thiz, KInt index, KRef element) {
RuntimeAssert(false, "Objective-C interop is disabled");
}
extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_removeAt, KRef thiz, KInt index) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_set, KRef thiz, KInt index, KRef element) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" void Kotlin_NSEnumeratorAsKIterator_computeNext(KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
}
extern "C" KInt Kotlin_NSSetAsKSet_getSize(KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
return -1;
}
extern "C" KBoolean Kotlin_NSSetAsKSet_contains(KRef thiz, KRef element) {
RuntimeAssert(false, "Objective-C interop is disabled");
return false;
}
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_getElement, KRef thiz, KRef element) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_iterator, KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" KInt Kotlin_NSDictionaryAsKMap_getSize(KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
return -1;
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsKey(KRef thiz, KRef key) {
RuntimeAssert(false, "Objective-C interop is disabled");
return false;
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsValue(KRef thiz, KRef value) {
RuntimeAssert(false, "Objective-C interop is disabled");
return false;
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_get, KRef thiz, KRef key) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_getOrThrowConcurrentModification, KRef thiz, KRef key) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsEntry(KRef thiz, KRef key, KRef value) {
RuntimeAssert(false, "Objective-C interop is disabled");
return false;
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_keyIterator, KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_valueIterator, KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
#endif // KONAN_OBJC_INTEROP
+10
View File
@@ -62,4 +62,14 @@ OBJ_GETTER(Kotlin_TypeInfo_getRelativeName, KNativePtr typeInfo) {
RETURN_OBJ(reinterpret_cast<const TypeInfo*>(typeInfo)->relativeName_);
}
bool IsSubInterface(const TypeInfo* thiz, const TypeInfo* other) {
for (int i = 0; i < thiz->implementedInterfacesCount_; ++i) {
if (thiz->implementedInterfaces_[i] == other) {
return true;
}
}
return false;
}
} // extern "C"
+1
View File
@@ -88,6 +88,7 @@ extern const TypeInfo* theObjCPointerHolderTypeInfo;
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
KBoolean IsArray(KConstRef obj) RUNTIME_PURE;
bool IsSubInterface(const TypeInfo* thiz, const TypeInfo* other) RUNTIME_PURE;
#ifdef __cplusplus
}
@@ -0,0 +1,26 @@
/*
* 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.
*/
package konan.internal
internal interface KonanSet<out E> : Set<E> {
/**
* Searches for the specified element in this set.
*
* @return the element from the set equal to [element], or `null` if no such element found.
*/
fun getElement(element: @UnsafeVariance E): E?
}
@@ -0,0 +1,270 @@
/*
* 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.
*/
package konan.internal
import konan.internal.ExportForCppRuntime
internal class NSArrayAsKList : AbstractList<Any?>() {
override val size: Int get() = getSize()
@SymbolName("Kotlin_NSArrayAsKList_getSize")
private external fun getSize(): Int
@SymbolName("Kotlin_NSArrayAsKList_get")
external override fun get(index: Int): Any?
}
internal class NSMutableArrayAsKMutableList : AbstractMutableList<Any?>() {
override val size: Int get() = getSize()
@SymbolName("Kotlin_NSArrayAsKList_getSize")
private external fun getSize(): Int
@SymbolName("Kotlin_NSArrayAsKList_get")
external override fun get(index: Int): Any?
@SymbolName("Kotlin_NSMutableArrayAsKMutableList_add")
external override fun add(index: Int, element: Any?): Unit
@SymbolName("Kotlin_NSMutableArrayAsKMutableList_removeAt")
external override fun removeAt(index: Int): Any?
@SymbolName("Kotlin_NSMutableArrayAsKMutableList_set")
external override fun set(index: Int, element: Any?): Any?
}
internal class NSSetAsKSet : AbstractSet<Any?>(), konan.internal.KonanSet<Any?> {
override val size: Int get() = getSize()
@SymbolName("Kotlin_NSSetAsKSet_getSize")
private external fun getSize(): Int
@SymbolName("Kotlin_NSSetAsKSet_contains")
external override fun contains(element: Any?): Boolean
@SymbolName("Kotlin_NSSetAsKSet_getElement")
external override fun getElement(element: Any?): Any?
@SymbolName("Kotlin_NSSetAsKSet_iterator")
external override fun iterator(): Iterator<Any?>
}
internal class NSDictionaryAsKMap : Map<Any?, Any?> {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Map<*, *>) return false
if (this.size != other.size) return false
return other.entries.all { this.containsEntry(it.key, it.value) }
}
override fun hashCode(): Int {
var result = 0
keyIterator().forEach { key ->
result += key.hashCode() xor this.getOrThrowConcurrentModification(key).hashCode()
}
return result
}
override fun toString(): String = entries.joinToString(", ", "{", "}") { toString(it.key) + "=" + toString(it.value) }
private fun toString(o: Any?): String = if (o === this) "(this Map)" else o.toString()
override val size: Int get() = getSize()
@SymbolName("Kotlin_NSDictionaryAsKMap_getSize")
private external fun getSize(): Int
override fun isEmpty(): Boolean = (size == 0)
@SymbolName("Kotlin_NSDictionaryAsKMap_containsKey")
override external fun containsKey(key: Any?): Boolean
@SymbolName("Kotlin_NSDictionaryAsKMap_containsValue")
override external fun containsValue(value: Any?): Boolean
@SymbolName("Kotlin_NSDictionaryAsKMap_get")
external override operator fun get(key: Any?): Any?
@SymbolName("Kotlin_NSDictionaryAsKMap_getOrThrowConcurrentModification")
private external fun getOrThrowConcurrentModification(key: Any?): Any?
@SymbolName("Kotlin_NSDictionaryAsKMap_containsEntry")
private external fun containsEntry(key: Any?, value: Any?): Boolean
// Views
override val keys: Set<Any?> get() = this.Keys()
override val values: Collection<Any?> get() = this.Values()
override val entries: Set<Map.Entry<Any?, Any?>> get() = this.Entries()
@SymbolName("Kotlin_NSDictionaryAsKMap_keyIterator")
private external fun keyIterator(): Iterator<Any?>
private inner class Keys : AbstractSet<Any?>() {
override val size: Int get() = this@NSDictionaryAsKMap.size
override fun iterator(): Iterator<Any?> = this@NSDictionaryAsKMap.keyIterator()
override fun contains(element: Any?): Boolean = this@NSDictionaryAsKMap.containsKey(element)
}
@SymbolName("Kotlin_NSDictionaryAsKMap_valueIterator")
private external fun valueIterator(): Iterator<Any?>
private inner class Values : AbstractCollection<Any?>() {
// TODO: what about equals and hashCode?
override val size: Int get() = this@NSDictionaryAsKMap.size
override fun iterator(): Iterator<Any?> = this@NSDictionaryAsKMap.valueIterator()
override fun contains(element: Any?): Boolean = this@NSDictionaryAsKMap.containsValue(element)
}
private inner class Entries : AbstractSet<Map.Entry<Any?, Any?>>() {
override val size: Int get() = this@NSDictionaryAsKMap.size
@SymbolName("Kotlin_NSSetAsKSet_iterator")
override fun iterator(): Iterator<Map.Entry<Any?, Any?>> = this@NSDictionaryAsKMap.EntryIterator()
override fun contains(element: Map.Entry<Any?, Any?>): Boolean {
return this@NSDictionaryAsKMap.containsEntry(element.key, element.value)
}
}
private class Entry(override val key: Any?, override val value: Any?) : Map.Entry<Any?, Any?> {
override fun equals(other: Any?): Boolean =
other is Map.Entry<*, *> &&
other.key == key &&
other.value == value
override fun hashCode(): Int = key.hashCode() xor value.hashCode()
override fun toString(): String = "$key=$value"
}
private inner class EntryIterator : Iterator<Map.Entry<Any?, Any?>> {
val keyIterator = this@NSDictionaryAsKMap.keyIterator()
override fun hasNext(): Boolean = keyIterator.hasNext()
override fun next(): Map.Entry<Any?, Any?> {
val nextKey = keyIterator.next()
val nextValue = this@NSDictionaryAsKMap.getOrThrowConcurrentModification(nextKey)
return Entry(nextKey, nextValue)
}
}
}
internal class NSEnumeratorAsKIterator : AbstractIterator<Any?>() {
@SymbolName("Kotlin_NSEnumeratorAsKIterator_computeNext")
override external fun computeNext()
@ExportForCppRuntime
private fun Kotlin_NSEnumeratorAsKIterator_done() = this.done()
@ExportForCppRuntime
private fun Kotlin_NSEnumeratorAsKIterator_setNext(value: Any?) = this.setNext(value)
}
@ExportForCppRuntime private fun Kotlin_Collection_getSize(collection: Collection<*>): Int = collection.size
@ExportForCppRuntime private fun Kotlin_List_get(list: List<*>, index: Int): Any? = list.get(index)
@ExportForCppRuntime private fun Kotlin_MutableList_addObjectAtIndex(list: MutableList<Any?>, index: Int, obj: Any?) {
list.add(index, obj)
}
@ExportForCppRuntime private fun Kotlin_MutableList_removeObjectAtIndex(list: MutableList<Any?>, index: Int) {
list.removeAt(index)
}
@ExportForCppRuntime private fun Kotlin_MutableCollection_addObject(list: MutableCollection<Any?>, obj: Any?) {
list.add(obj)
}
@ExportForCppRuntime private fun Kotlin_MutableList_removeLastObject(list: MutableList<Any?>) {
list.removeAt(list.lastIndex)
}
@ExportForCppRuntime private fun Kotlin_MutableList_setObject(list: MutableList<Any?>, index: Int, obj: Any?) {
list.set(index, obj)
}
@ExportForCppRuntime private fun Kotlin_MutableCollection_removeObject(
collection: MutableCollection<Any?>, element: Any?
) {
collection.remove(element)
}
@ExportForCppRuntime private fun Kotlin_Iterator_hasNext(iterator: Iterator<Any?>): Boolean = iterator.hasNext()
@ExportForCppRuntime private fun Kotlin_Iterator_next(iterator: Iterator<Any?>): Any? = iterator.next()
@ExportForCppRuntime private fun Kotlin_Set_contains(set: Set<Any?>, element: Any?): Boolean = set.contains(element)
@ExportForCppRuntime private fun Kotlin_Set_getElement(set: Set<Any?>, element: Any?): Any? =
if (set is konan.internal.KonanSet<Any?>) {
set.getElement(element)
} else if (set.contains(element)) {
set.first { it == element }
} else {
null
}
@ExportForCppRuntime private fun Kotlin_Set_iterator(set: Set<Any?>): Iterator<Any?> = set.iterator()
@ExportForCppRuntime private fun Kotlin_MutableSet_createWithCapacity(capacity: Int): MutableSet<Any?> =
HashSet<Any?>(capacity)
@ExportForCppRuntime private fun Kotlin_Map_getSize(map: Map<Any?, Any?>): Int = map.size
@ExportForCppRuntime private fun Kotlin_Map_containsKey(map: Map<Any?, Any?>, key: Any?): Boolean = map.containsKey(key)
@ExportForCppRuntime private fun Kotlin_Map_get(map: Map<Any?, Any?>, key: Any?): Any? = map.get(key)
@ExportForCppRuntime private fun Kotlin_Map_keyIterator(map: Map<Any?, Any?>): Iterator<Any?> = map.keys.iterator()
@ExportForCppRuntime private fun Kotlin_MutableMap_createWithCapacity(capacity: Int): MutableMap<Any?, Any?> =
HashMap<Any?, Any?>(capacity)
@ExportForCppRuntime private fun Kotlin_MutableMap_set(map: MutableMap<Any?, Any?>, key: Any?, value: Any?) {
map.set(key, value)
}
@ExportForCppRuntime private fun Kotlin_MutableMap_remove(map: MutableMap<Any?, Any?>, key: Any?) {
map.remove(key)
}
@ExportForCppRuntime private fun Kotlin_ObjCExport_ThrowCollectionTooLarge() {
throw Error("an Objective-C collection is too large")
}
@ExportForCppRuntime private fun Kotlin_ObjCExport_ThrowCollectionConcurrentModification() {
throw Error("an Objective-C collection was modified while iterating")
}
@ExportForCppRuntime private fun Kotlin_NSArrayAsKList_create() = NSArrayAsKList()
@ExportForCppRuntime private fun Kotlin_NSMutableArrayAsKMutableList_create() = NSMutableArrayAsKMutableList()
@ExportForCppRuntime private fun Kotlin_NSEnumeratorAsKIterator_create() = NSEnumeratorAsKIterator()
@ExportForCppRuntime private fun Kotlin_NSSetAsKSet_create() = NSSetAsKSet()
@ExportForCppRuntime private fun Kotlin_NSDictionaryAsKMap_create() = NSDictionaryAsKMap()
@@ -368,6 +368,24 @@ class HashMap<K, V> private constructor(
return valuesArray!![index] == entry.value
}
internal fun getEntry(entry: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>? {
val index = findKey(entry.key)
return if (index < 0 || valuesArray!![index] != entry.value) {
null
} else {
EntryRef(this, index)
}
}
internal fun getKey(key: K): K? {
val index = findKey(key)
return if (index >= 0) {
keysArray[index]!!
} else {
null
}
}
private fun contentEquals(other: Map<*, *>): Boolean = size == other.size && containsAllEntries(other.entries)
internal fun containsAllEntries(m: Collection<Map.Entry<*, *>>): Boolean {
@@ -644,11 +662,12 @@ internal class HashMapValues<V> internal constructor(
internal class HashMapEntrySet<K, V> internal constructor(
val backing: HashMap<K, V>
) : MutableSet<MutableMap.MutableEntry<K, V>> {
) : MutableSet<MutableMap.MutableEntry<K, V>>, konan.internal.KonanSet<MutableMap.MutableEntry<K, V>> {
override val size: Int get() = backing.size
override fun isEmpty(): Boolean = backing.isEmpty()
override fun contains(element: MutableMap.MutableEntry<K, V>): Boolean = backing.containsEntry(element)
override fun getElement(element: MutableMap.MutableEntry<K, V>): MutableMap.MutableEntry<K, V>? = backing.getEntry(element)
override fun clear() = backing.clear()
override fun add(element: MutableMap.MutableEntry<K, V>): Boolean = backing.putEntry(element)
override fun remove(element: MutableMap.MutableEntry<K, V>): Boolean = backing.removeEntry(element)
@@ -18,7 +18,7 @@ package kotlin.collections
class HashSet<K> internal constructor(
val backing: HashMap<K, *>
) : MutableSet<K>, AbstractMutableCollection<K>() {
) : MutableSet<K>, AbstractMutableCollection<K>(), konan.internal.KonanSet<K> {
constructor() : this(HashMap<K, Nothing>())
@@ -31,6 +31,7 @@ class HashSet<K> internal constructor(
override val size: Int get() = backing.size
override fun isEmpty(): Boolean = backing.isEmpty()
override fun contains(element: K): Boolean = backing.containsKey(element)
override fun getElement(element: K): K? = backing.getKey(element)
override fun clear() = backing.clear()
override fun add(element: K): Boolean = backing.addKey(element) >= 0
override fun remove(element: K): Boolean = backing.removeKey(element) >= 0
@@ -17,7 +17,7 @@
package kotlin.collections
internal object EmptySet : Set<Nothing> {
internal object EmptySet : Set<Nothing>, konan.internal.KonanSet<Nothing> {
override fun equals(other: Any?): Boolean = other is Set<*> && other.isEmpty()
override fun hashCode(): Int = 0
override fun toString(): String = "[]"
@@ -25,6 +25,7 @@ internal object EmptySet : Set<Nothing> {
override val size: Int get() = 0
override fun isEmpty(): Boolean = true
override fun contains(element: Nothing): Boolean = false
override fun getElement(element: Nothing): Nothing? = null
override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty()
override fun iterator(): Iterator<Nothing> = EmptyIterator