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_ObjCExport_refFromObjC by lazyRtFunction
|
||||||
val Kotlin_Interop_CreateNSStringFromKString by lazyRtFunction
|
val Kotlin_Interop_CreateNSStringFromKString by lazyRtFunction
|
||||||
val Kotlin_Interop_CreateNSArrayFromKList 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_convertUnit by lazyRtFunction
|
||||||
val Kotlin_ObjCExport_GetAssociatedObject by lazyRtFunction
|
val Kotlin_ObjCExport_GetAssociatedObject by lazyRtFunction
|
||||||
val Kotlin_ObjCExport_AbstractMethodCalled 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")
|
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 {
|
private fun getClassGlobal(name: String, isMetaclass: Boolean): ConstPointer {
|
||||||
val prefix = if (isMetaclass) {
|
val prefix = if (isMetaclass) {
|
||||||
"OBJC_METACLASS_\$_"
|
"OBJC_METACLASS_\$_"
|
||||||
|
|||||||
+30
-2
@@ -178,6 +178,9 @@ internal class ObjCExportCodeGenerator(
|
|||||||
dataGenerator.emitEmptyClass(namer.getPackageName(fqName), namer.kotlinAnyName)
|
dataGenerator.emitEmptyClass(namer.getPackageName(fqName), namer.kotlinAnyName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dataGenerator.exportClass("KotlinMutableSet")
|
||||||
|
dataGenerator.exportClass("KotlinMutableDictionary")
|
||||||
|
|
||||||
emitSpecialClassesConvertions()
|
emitSpecialClassesConvertions()
|
||||||
|
|
||||||
objCTypeAdapters += createTypeAdapter(context.builtIns.any)
|
objCTypeAdapters += createTypeAdapter(context.builtIns.any)
|
||||||
@@ -375,7 +378,7 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(objCValueType: ObjCValueTyp
|
|||||||
private fun ObjCExportCodeGenerator.emitFunctionConverters() {
|
private fun ObjCExportCodeGenerator.emitFunctionConverters() {
|
||||||
val generator = BlockAdapterToFunctionGenerator(this)
|
val generator = BlockAdapterToFunctionGenerator(this)
|
||||||
|
|
||||||
(0 .. 22).forEach { numberOfParameters ->
|
(0 .. mapper.maxFunctionTypeParameterCount).forEach { numberOfParameters ->
|
||||||
val converter = generator.run { generateConvertFunctionToBlock(numberOfParameters) }
|
val converter = generator.run { generateConvertFunctionToBlock(numberOfParameters) }
|
||||||
setObjCExportTypeInfo(context.builtIns.getFunction(numberOfParameters), constPointer(converter))
|
setObjCExportTypeInfo(context.builtIns.getFunction(numberOfParameters), constPointer(converter))
|
||||||
}
|
}
|
||||||
@@ -399,7 +402,7 @@ private fun ObjCExportCodeGenerator.emitKotlinFunctionAdaptersToBlock() {
|
|||||||
val ptr = staticData.placeGlobalArray(
|
val ptr = staticData.placeGlobalArray(
|
||||||
"",
|
"",
|
||||||
pointerType(runtime.typeInfoType),
|
pointerType(runtime.typeInfoType),
|
||||||
(0 .. 22).map {
|
(0 .. mapper.maxFunctionTypeParameterCount).map {
|
||||||
generateKotlinFunctionAdapterToBlock(it)
|
generateKotlinFunctionAdapterToBlock(it)
|
||||||
}
|
}
|
||||||
).pointer.getElementPtr(0)
|
).pointer.getElementPtr(0)
|
||||||
@@ -419,6 +422,31 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
|
|||||||
constPointer(context.llvm.Kotlin_Interop_CreateNSArrayFromKList)
|
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 {
|
ObjCValueType.values().forEach {
|
||||||
emitBoxConverter(it)
|
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.isArray
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||||
import org.jetbrains.kotlin.backend.konan.reportCompilationError
|
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.getReceiverTypeFromFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||||
@@ -38,13 +38,10 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
|||||||
|
|
||||||
internal class ObjCExportHeaderGenerator(val context: Context) {
|
internal class ObjCExportHeaderGenerator(val context: Context) {
|
||||||
val mapper: ObjCExportMapper = object : ObjCExportMapper {
|
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) =
|
override fun getCategoryMembersFor(descriptor: ClassDescriptor) =
|
||||||
extensions[descriptor].orEmpty()
|
extensions[descriptor].orEmpty()
|
||||||
|
|
||||||
|
override val specialMappedTypes get() = customTypeMappers.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
val namer = ObjCExportNamer(context, mapper)
|
val namer = ObjCExportNamer(context, mapper)
|
||||||
@@ -52,6 +49,43 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
|
|||||||
val generatedClasses = mutableSetOf<ClassDescriptor>()
|
val generatedClasses = mutableSetOf<ClassDescriptor>()
|
||||||
val topLevel = mutableMapOf<FqName, MutableList<CallableMemberDescriptor>>()
|
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 kotlinAnyName = namer.kotlinAnyName
|
||||||
|
|
||||||
private val stubs = mutableListOf<Stub>()
|
private val stubs = mutableListOf<Stub>()
|
||||||
@@ -175,7 +209,7 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
|
|||||||
val name = namer.getPackageName(packageFqName)
|
val name = namer.getPackageName(packageFqName)
|
||||||
stubs.addBuiltBy {
|
stubs.addBuiltBy {
|
||||||
+"__attribute__((objc_subclassing_restricted))"
|
+"__attribute__((objc_subclassing_restricted))"
|
||||||
+"@interface $name : KotlinBase" // TODO: stop inheriting KotlinBase.
|
+"@interface $name : ${namer.kotlinAnyName}" // TODO: stop inheriting KotlinBase.
|
||||||
|
|
||||||
translateMembers(declarations)
|
translateMembers(declarations)
|
||||||
|
|
||||||
@@ -443,6 +477,21 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
|
|||||||
add("@end;")
|
add("@end;")
|
||||||
add("")
|
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 {
|
stubs.forEach {
|
||||||
addAll(it.lines)
|
addAll(it.lines)
|
||||||
add("")
|
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()
|
final override fun toString(): String = this.render()
|
||||||
|
|
||||||
open fun render(varName: String): String = "${this.render()} $varName"
|
open fun render(varName: String): String = "${this.render()} $varName"
|
||||||
abstract fun render(): String
|
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 ""
|
val attributes = if (TypeUtils.isNullableType(kotlinType)) " _Nullable" else ""
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ObjCClassType(kotlinType: KotlinType, val className: String) : ObjCReferenceType(kotlinType) {
|
private class ObjCClassType(
|
||||||
override fun render() = "$className*$attributes"
|
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) {
|
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")
|
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 {
|
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()
|
val classDescriptor = kotlinType.getErasedTypeClass()
|
||||||
|
|
||||||
if (classDescriptor.isSubclassOf(context.builtIns.list)) {
|
// TODO: translate `where T : BaseClass, T : SomeInterface` to `BaseClass* <SomeInterface>`
|
||||||
return ObjCClassType(kotlinType, "NSArray")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (classDescriptor == context.builtIns.any || classDescriptor in hiddenTypes) {
|
||||||
// TODO: Kotlin code doesn't have any checkcasts on unboxing,
|
return ObjCIdType(kotlinType)
|
||||||
// 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 !in generatedClasses) {
|
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.ValueType
|
||||||
import org.jetbrains.kotlin.backend.konan.correspondingValueType
|
import org.jetbrains.kotlin.backend.konan.correspondingValueType
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
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.backend.konan.isObjCObjectType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
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.resolve.descriptorUtil.isEffectivelyPublicApi
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
|
|
||||||
internal interface ObjCExportMapper {
|
internal interface ObjCExportMapper {
|
||||||
fun isRepresentedAsObjCInterface(descriptor: ClassDescriptor): Boolean
|
|
||||||
fun getCategoryMembersFor(descriptor: ClassDescriptor): List<CallableMemberDescriptor>
|
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? {
|
internal fun ObjCExportMapper.getClassIfCategory(descriptor: CallableMemberDescriptor): ClassDescriptor? {
|
||||||
if (descriptor.dispatchReceiverParameter != null) return null
|
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) {
|
descriptor.isEffectivelyPublicApi && !descriptor.defaultType.isObjCObjectType() && when (descriptor.kind) {
|
||||||
ClassKind.CLASS, ClassKind.INTERFACE, ClassKind.ENUM_CLASS, ClassKind.OBJECT -> true
|
ClassKind.CLASS, ClassKind.INTERFACE, ClassKind.ENUM_CLASS, ClassKind.OBJECT -> true
|
||||||
ClassKind.ENUM_ENTRY, ClassKind.ANNOTATION_CLASS -> false
|
ClassKind.ENUM_ENTRY, ClassKind.ANNOTATION_CLASS -> false
|
||||||
}
|
} && !isSpecialMapped(descriptor)
|
||||||
|
|
||||||
private fun ObjCExportMapper.isBase(descriptor: CallableMemberDescriptor): Boolean =
|
private fun ObjCExportMapper.isBase(descriptor: CallableMemberDescriptor): Boolean =
|
||||||
descriptor.overriddenDescriptors.all { !shouldBeExposed(it) }
|
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 commonPackageSegments = context.moduleDescriptor.guessMainPackage().pathSegments()
|
||||||
private val topLevelNamePrefix = context.moduleDescriptor.namePrefix
|
private val topLevelNamePrefix = context.moduleDescriptor.namePrefix
|
||||||
|
|
||||||
|
val mutableSetName = "${topLevelNamePrefix}MutableSet"
|
||||||
|
val mutableMapName = "${topLevelNamePrefix}MutableDictionary"
|
||||||
|
|
||||||
private val methodSelectors = object : Mapping<FunctionDescriptor, String>() {
|
private val methodSelectors = object : Mapping<FunctionDescriptor, String>() {
|
||||||
|
|
||||||
// Try to avoid clashing with critical NSObject instance methods:
|
// 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
|
val any = context.builtIns.any
|
||||||
|
|
||||||
classNames.forceAssign(any, kotlinAnyName)
|
classNames.forceAssign(any, kotlinAnyName)
|
||||||
|
classNames.forceAssign(context.builtIns.mutableSet, mutableSetName)
|
||||||
|
classNames.forceAssign(context.builtIns.mutableMap, mutableMapName)
|
||||||
|
|
||||||
fun ClassDescriptor.method(name: String) =
|
fun ClassDescriptor.method(name: String) =
|
||||||
this.unsubstitutedMemberScope.getContributedFunctions(
|
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
|
#if KONAN_OBJC_INTEROP
|
||||||
|
|
||||||
#import <Foundation/NSObject.h>
|
#import <Foundation/NSObject.h>
|
||||||
#import <Foundation/NSArray.h>
|
|
||||||
#import <Foundation/NSValue.h>
|
#import <Foundation/NSValue.h>
|
||||||
#import <Foundation/NSString.h>
|
#import <Foundation/NSString.h>
|
||||||
#import <Foundation/NSNull.h>
|
|
||||||
#import <Foundation/NSMethodSignature.h>
|
#import <Foundation/NSMethodSignature.h>
|
||||||
#import <Foundation/NSException.h>
|
#import <Foundation/NSException.h>
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
#import <dispatch/dispatch.h>
|
#import <dispatch/dispatch.h>
|
||||||
|
|
||||||
|
#import "ObjCExport.h"
|
||||||
#import "MemoryPrivate.hpp"
|
#import "MemoryPrivate.hpp"
|
||||||
#import "Runtime.h"
|
#import "Runtime.h"
|
||||||
#import "Utils.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);
|
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) {
|
extern "C" id Kotlin_ObjCExport_GetAssociatedObject(ObjHeader* obj) {
|
||||||
return GetAssociatedObject(obj);
|
return GetAssociatedObject(obj);
|
||||||
}
|
}
|
||||||
@@ -118,12 +106,7 @@ inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* type
|
|||||||
static Class getOrCreateClass(const TypeInfo* typeInfo);
|
static Class getOrCreateClass(const TypeInfo* typeInfo);
|
||||||
static void initializeClass(Class clazz);
|
static void initializeClass(Class clazz);
|
||||||
|
|
||||||
@protocol ConvertibleToKotlin
|
@interface KotlinBase : NSObject <ConvertibleToKotlin, NSCopying>
|
||||||
@required
|
|
||||||
-(KRef)toKotlin:(KRef*)OBJ_RESULT;
|
|
||||||
@end;
|
|
||||||
|
|
||||||
@interface KotlinBase : NSObject <ConvertibleToKotlin>
|
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
@implementation KotlinBase {
|
@implementation KotlinBase {
|
||||||
@@ -199,6 +182,11 @@ static void initializeClass(Class clazz);
|
|||||||
[super release];
|
[super release];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||||
|
// TODO: write documentation.
|
||||||
|
return [self retain];
|
||||||
|
}
|
||||||
|
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
extern "C" void Kotlin_ObjCExport_releaseReservedObjectTail(ObjHeader* obj) {
|
extern "C" void Kotlin_ObjCExport_releaseReservedObjectTail(ObjHeader* obj) {
|
||||||
@@ -324,9 +312,7 @@ static void initializeClass(Class clazz) {
|
|||||||
@interface NSObject (NSObjectToKotlin) <ConvertibleToKotlin>
|
@interface NSObject (NSObjectToKotlin) <ConvertibleToKotlin>
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
extern "C" id objc_retain(id self);
|
|
||||||
extern "C" id objc_retainBlock(id self);
|
extern "C" id objc_retainBlock(id self);
|
||||||
extern "C" void objc_release(id self);
|
|
||||||
extern "C" id objc_retainAutoreleaseReturnValue(id self);
|
extern "C" id objc_retainAutoreleaseReturnValue(id self);
|
||||||
|
|
||||||
@implementation NSObject (NSObjectToKotlin)
|
@implementation NSObject (NSObjectToKotlin)
|
||||||
@@ -351,27 +337,6 @@ extern "C" OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, NSString* str);
|
|||||||
}
|
}
|
||||||
@end;
|
@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" {
|
extern "C" {
|
||||||
|
|
||||||
OBJ_GETTER(Kotlin_boxBoolean, KBoolean value);
|
OBJ_GETTER(Kotlin_boxBoolean, KBoolean value);
|
||||||
@@ -425,47 +390,6 @@ static Class __NSCFBooleanClass = nullptr;
|
|||||||
}
|
}
|
||||||
@end;
|
@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>
|
@interface NSBlock <NSObject>
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
@@ -567,10 +491,6 @@ static const TypeInfo* getFunctionTypeInfoForBlock(id block) {
|
|||||||
|
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
extern "C" id Kotlin_Interop_CreateNSArrayFromKList(ObjHeader* obj) {
|
|
||||||
return [KListNSArray createWithKList:obj];
|
|
||||||
}
|
|
||||||
|
|
||||||
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj);
|
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj);
|
||||||
|
|
||||||
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj) {
|
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj) {
|
||||||
@@ -603,14 +523,35 @@ static id convertKotlinObject(ObjHeader* obj) {
|
|||||||
return [clazz createWrapper: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) {
|
static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj) {
|
||||||
const TypeInfo* typeInfo = obj->type_info();
|
const TypeInfo* typeInfo = obj->type_info();
|
||||||
convertReferenceToObjC converter = nullptr;
|
convertReferenceToObjC converter = nullptr;
|
||||||
|
|
||||||
for (int i = 0; i < typeInfo->implementedInterfacesCount_; ++i) {
|
converter = findConverterFromInterfaces(typeInfo);
|
||||||
converter = (convertReferenceToObjC)typeInfo->implementedInterfaces_[i]->writableInfo_->objCExport.convert;
|
|
||||||
if (converter != nullptr) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (converter == nullptr) {
|
if (converter == nullptr) {
|
||||||
getOrCreateClass(typeInfo);
|
getOrCreateClass(typeInfo);
|
||||||
@@ -622,18 +563,6 @@ static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj) {
|
|||||||
return converter(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(
|
static const TypeInfo* createTypeInfo(
|
||||||
const TypeInfo* superType,
|
const TypeInfo* superType,
|
||||||
const KStdVector<const TypeInfo*>& superInterfaces,
|
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)];
|
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
|
#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_);
|
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"
|
} // extern "C"
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ extern const TypeInfo* theObjCPointerHolderTypeInfo;
|
|||||||
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
|
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
|
||||||
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
|
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
|
||||||
KBoolean IsArray(KConstRef obj) RUNTIME_PURE;
|
KBoolean IsArray(KConstRef obj) RUNTIME_PURE;
|
||||||
|
bool IsSubInterface(const TypeInfo* thiz, const TypeInfo* other) RUNTIME_PURE;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#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
|
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)
|
private fun contentEquals(other: Map<*, *>): Boolean = size == other.size && containsAllEntries(other.entries)
|
||||||
|
|
||||||
internal fun containsAllEntries(m: Collection<Map.Entry<*, *>>): Boolean {
|
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(
|
internal class HashMapEntrySet<K, V> internal constructor(
|
||||||
val backing: HashMap<K, V>
|
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 val size: Int get() = backing.size
|
||||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||||
override fun contains(element: MutableMap.MutableEntry<K, V>): Boolean = backing.containsEntry(element)
|
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 clear() = backing.clear()
|
||||||
override fun add(element: MutableMap.MutableEntry<K, V>): Boolean = backing.putEntry(element)
|
override fun add(element: MutableMap.MutableEntry<K, V>): Boolean = backing.putEntry(element)
|
||||||
override fun remove(element: MutableMap.MutableEntry<K, V>): Boolean = backing.removeEntry(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(
|
class HashSet<K> internal constructor(
|
||||||
val backing: HashMap<K, *>
|
val backing: HashMap<K, *>
|
||||||
) : MutableSet<K>, AbstractMutableCollection<K>() {
|
) : MutableSet<K>, AbstractMutableCollection<K>(), konan.internal.KonanSet<K> {
|
||||||
|
|
||||||
constructor() : this(HashMap<K, Nothing>())
|
constructor() : this(HashMap<K, Nothing>())
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ class HashSet<K> internal constructor(
|
|||||||
override val size: Int get() = backing.size
|
override val size: Int get() = backing.size
|
||||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||||
override fun contains(element: K): Boolean = backing.containsKey(element)
|
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 clear() = backing.clear()
|
||||||
override fun add(element: K): Boolean = backing.addKey(element) >= 0
|
override fun add(element: K): Boolean = backing.addKey(element) >= 0
|
||||||
override fun remove(element: K): Boolean = backing.removeKey(element) >= 0
|
override fun remove(element: K): Boolean = backing.removeKey(element) >= 0
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
package kotlin.collections
|
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 equals(other: Any?): Boolean = other is Set<*> && other.isEmpty()
|
||||||
override fun hashCode(): Int = 0
|
override fun hashCode(): Int = 0
|
||||||
override fun toString(): String = "[]"
|
override fun toString(): String = "[]"
|
||||||
@@ -25,6 +25,7 @@ internal object EmptySet : Set<Nothing> {
|
|||||||
override val size: Int get() = 0
|
override val size: Int get() = 0
|
||||||
override fun isEmpty(): Boolean = true
|
override fun isEmpty(): Boolean = true
|
||||||
override fun contains(element: Nothing): Boolean = false
|
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 containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty()
|
||||||
|
|
||||||
override fun iterator(): Iterator<Nothing> = EmptyIterator
|
override fun iterator(): Iterator<Nothing> = EmptyIterator
|
||||||
|
|||||||
Reference in New Issue
Block a user