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:
committed by
SvyatoslavScherbina
parent
d228e77c0c
commit
b15c94855e
@@ -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
|
||||
+5
@@ -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
|
||||
|
||||
+5
@@ -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_\$_"
|
||||
|
||||
+30
-2
@@ -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)
|
||||
}
|
||||
|
||||
+166
-44
@@ -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) {
|
||||
|
||||
+11
-2
@@ -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) }
|
||||
|
||||
+5
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user