diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiBaseDeclarationsGenerator.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiBaseDeclarationsGenerator.kt new file mode 100644 index 00000000000..cbcc9b8c3ec --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiBaseDeclarationsGenerator.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.objcexport.testUtils + +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCTopLevel +import org.jetbrains.kotlin.backend.konan.tests.ObjCExportBaseDeclarationsTest +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.ParameterContext +import org.junit.jupiter.api.extension.ParameterResolver + +class AnalysisApiBaseDeclarationsGeneratorExtension : ParameterResolver { + override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Boolean { + return parameterContext.parameter.type == ObjCExportBaseDeclarationsTest.BaseDeclarationsGenerator::class.java + } + + override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Any { + return AnalysisApiBaseDeclarationsGenerator + } +} + +object AnalysisApiBaseDeclarationsGenerator : ObjCExportBaseDeclarationsTest.BaseDeclarationsGenerator { + override fun invoke(topLevelPrefix: String): List { + TODO("Analysis Api based 'base declaration generation' in not yet implemented") + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/testResources/META-INF/services/org.junit.jupiter.api.extension.Extension b/native/objcexport-header-generator/impl/analysis-api/testResources/META-INF/services/org.junit.jupiter.api.extension.Extension index 73f43b00e3b..9ebb1844e7c 100644 --- a/native/objcexport-header-generator/impl/analysis-api/testResources/META-INF/services/org.junit.jupiter.api.extension.Extension +++ b/native/objcexport-header-generator/impl/analysis-api/testResources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -3,4 +3,5 @@ # Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. # org.jetbrains.kotlin.objcexport.testUtils.AnalysisApiHeaderGeneratorExtension -org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysisExtension \ No newline at end of file +org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysisExtension +org.jetbrains.kotlin.objcexport.testUtils.AnalysisApiBaseDeclarationsGeneratorExtension \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt index 803ddfeb435..76f9a05c9db 100644 --- a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt +++ b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt @@ -436,44 +436,3 @@ internal fun ObjCExportMapper.bridgePropertyType(descriptor: PropertyDescriptor) return bridgeType(descriptor.type) } -@InternalKotlinNativeApi -enum class NSNumberKind(val mappedKotlinClassId: ClassId?, val objCType: ObjCType) { - CHAR(PrimitiveType.BYTE, ObjCPrimitiveType.char), - UNSIGNED_CHAR(UnsignedType.UBYTE, ObjCPrimitiveType.unsigned_char), - SHORT(PrimitiveType.SHORT, ObjCPrimitiveType.short), - UNSIGNED_SHORT(UnsignedType.USHORT, ObjCPrimitiveType.unsigned_short), - INT(PrimitiveType.INT, ObjCPrimitiveType.int), - UNSIGNED_INT(UnsignedType.UINT, ObjCPrimitiveType.unsigned_int), - LONG(ObjCPrimitiveType.long), - UNSIGNED_LONG(ObjCPrimitiveType.unsigned_long), - LONG_LONG(PrimitiveType.LONG, ObjCPrimitiveType.long_long), - UNSIGNED_LONG_LONG(UnsignedType.ULONG, ObjCPrimitiveType.unsigned_long_long), - FLOAT(PrimitiveType.FLOAT, ObjCPrimitiveType.float), - DOUBLE(PrimitiveType.DOUBLE, ObjCPrimitiveType.double), - BOOL(PrimitiveType.BOOLEAN, ObjCPrimitiveType.BOOL), - INTEGER(ObjCPrimitiveType.NSInteger), - UNSIGNED_INTEGER(ObjCPrimitiveType.NSUInteger) - - ; - - // UNSIGNED_SHORT -> unsignedShort - private val kindName = this.name.split('_') - .joinToString("") { it.lowercase().replaceFirstChar(Char::uppercaseChar) }.replaceFirstChar(Char::lowercaseChar) - - - val valueSelector = kindName // unsignedShort - val initSelector = "initWith${kindName.replaceFirstChar(Char::uppercaseChar)}:" // initWithUnsignedShort: - val factorySelector = "numberWith${kindName.replaceFirstChar(Char::uppercaseChar)}:" // numberWithUnsignedShort: - - constructor( - primitiveType: PrimitiveType, - objCPrimitiveType: ObjCPrimitiveType, - ) : this(ClassId.topLevel(primitiveType.typeFqName), objCPrimitiveType) - - constructor( - unsignedType: UnsignedType, - objCPrimitiveType: ObjCPrimitiveType, - ) : this(unsignedType.classId, objCPrimitiveType) - - constructor(objCPrimitiveType: ObjCPrimitiveType) : this(null, objCPrimitiveType) -} diff --git a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportTranslator.kt b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportTranslator.kt index e8ed4009723..99c73f85dc2 100644 --- a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportTranslator.kt +++ b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportTranslator.kt @@ -52,115 +52,14 @@ class ObjCExportTranslatorImpl( private val kotlinAnyName = namer.kotlinAnyName - override fun generateBaseDeclarations(): List = buildTopLevel { - add { - objCInterface(namer.kotlinAnyName, superClass = "NSObject", members = buildMembers { - add { ObjCMethod(null, true, ObjCInstanceType, listOf("init"), emptyList(), listOf("unavailable")) } - add { ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable")) } - add { ObjCMethod(null, false, ObjCVoidType, listOf("initialize"), emptyList(), listOf("objc_requires_super")) } - }) - } - - // TODO: add comment to the header. - add { - ObjCInterfaceImpl( - namer.kotlinAnyName.objCName, - superProtocols = listOf("NSCopying"), - categoryName = "${namer.kotlinAnyName.objCName}Copying" - ) - } - - // TODO: only if appears - add { - val generics = listOf("ObjectType") - objCInterface( - namer.mutableSetName, - generics = generics, - superClass = "NSMutableSet", - superClassGenerics = generics - ) - } - - // TODO: only if appears - add { - val generics = listOf("KeyType", "ObjectType") - objCInterface( - namer.mutableMapName, - generics = generics, - superClass = "NSMutableDictionary", - superClassGenerics = generics - ) - } - - val nsErrorCategoryName = "NSError${namer.topLevelNamePrefix}KotlinException" - add { - ObjCInterfaceImpl("NSError", categoryName = nsErrorCategoryName, members = buildMembers { - add { ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly")) } - }) - } - - genKotlinNumbers() - } - - private fun StubBuilder.genKotlinNumbers() { - val members = buildMembers { - NSNumberKind.entries.forEach { - add { nsNumberFactory(it, listOf("unavailable")) } - } - NSNumberKind.entries.forEach { - add { nsNumberInit(it, listOf("unavailable")) } - } - } - add { - objCInterface( - namer.kotlinNumberName, - superClass = "NSNumber", - members = members - ) - } - - NSNumberKind.entries.forEach { - if (it.mappedKotlinClassId != null) add { - genKotlinNumber(it.mappedKotlinClassId, it) - } - } - } - - private fun genKotlinNumber(kotlinClassId: ClassId, kind: NSNumberKind): ObjCInterface { - val name = namer.numberBoxName(kotlinClassId) - - val members = buildMembers { - add { nsNumberFactory(kind) } - add { nsNumberInit(kind) } - } - return objCInterface( - name, - superClass = namer.kotlinNumberName.objCName, - members = members - ) - } - - private fun nsNumberInit(kind: NSNumberKind, attributes: List = emptyList()): ObjCMethod { - return ObjCMethod( - null, - false, - ObjCInstanceType, - listOf(kind.factorySelector), - listOf(ObjCParameter("value", null, kind.objCType)), - attributes - ) - } - - private fun nsNumberFactory(kind: NSNumberKind, attributes: List = emptyList()): ObjCMethod { - return ObjCMethod( - null, - true, - ObjCInstanceType, - listOf(kind.initSelector), - listOf(ObjCParameter("value", null, kind.objCType)), - attributes - ) - } + override fun generateBaseDeclarations(): List = objCBaseDeclarations( + topLevelNamePrefix = namer.topLevelNamePrefix, + objCNameOfAny = namer.kotlinAnyName, + objCNameOfNumber = namer.kotlinNumberName, + objCNameOfMutableMap = namer.mutableMapName, + objCNameOfMutableSet = namer.mutableSetName, + objCNameForNumberBox = { classId -> namer.numberBoxName(classId) } + ) override fun getClassIfExtension(receiverType: KotlinType): ClassDescriptor? = mapper.getClassIfCategory(receiverType) diff --git a/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10BaseDeclarationsGenerator.kt b/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10BaseDeclarationsGenerator.kt new file mode 100644 index 00000000000..532d23b3eab --- /dev/null +++ b/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10BaseDeclarationsGenerator.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.testUtils + +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportProblemCollector +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCTopLevel +import org.jetbrains.kotlin.backend.konan.tests.ObjCExportBaseDeclarationsTest +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.ParameterContext +import org.junit.jupiter.api.extension.ParameterResolver + + +class Fe10BaseDeclarationsGeneratorExtension : ParameterResolver { + override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Boolean { + return parameterContext.parameter.type == ObjCExportBaseDeclarationsTest.BaseDeclarationsGenerator::class.java + } + + override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Any { + return Fe10BaseDeclarationsGenerator + } +} + +private object Fe10BaseDeclarationsGenerator : ObjCExportBaseDeclarationsTest.BaseDeclarationsGenerator { + override fun invoke(topLevelPrefix: String): List { + val translator = ObjCExportTranslatorImpl( + generator = null, + mapper = createObjCExportMapper(), + namer = createObjCExportNamer(configuration = createObjCExportNamerConfiguration(topLevelNamePrefix = topLevelPrefix)), + problemCollector = ObjCExportProblemCollector.SILENT, + objcGenerics = true + ) + + return translator.generateBaseDeclarations() + } +} diff --git a/native/objcexport-header-generator/impl/k1/testResources/META-INF/services/org.junit.jupiter.api.extension.Extension b/native/objcexport-header-generator/impl/k1/testResources/META-INF/services/org.junit.jupiter.api.extension.Extension index 4dadc1c1568..0b9bee299f9 100644 --- a/native/objcexport-header-generator/impl/k1/testResources/META-INF/services/org.junit.jupiter.api.extension.Extension +++ b/native/objcexport-header-generator/impl/k1/testResources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -2,4 +2,5 @@ # Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. # Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. # -org.jetbrains.kotlin.backend.konan.testUtils.Fe10HeaderGeneratorExtension \ No newline at end of file +org.jetbrains.kotlin.backend.konan.testUtils.Fe10HeaderGeneratorExtension +org.jetbrains.kotlin.backend.konan.testUtils.Fe10BaseDeclarationsGeneratorExtension \ No newline at end of file diff --git a/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/NsNumberType.kt b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/NsNumberType.kt new file mode 100644 index 00000000000..c74ec02f53a --- /dev/null +++ b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/NsNumberType.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.objcexport + +import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.name.ClassId + +/* +Defining ClassIds for Unsigned types: +Cannot reuse 'UnsignedType.kt' from core/descriptors + */ +private val uByteClassId = ClassId.fromString("kotlin/UByte") +private val uShortClassId = ClassId.fromString("kotlin/UShort") +private val uIntClassId = ClassId.fromString("kotlin/UInt") +private val uLongClassId = ClassId.fromString("kotlin/ULong") + +@InternalKotlinNativeApi +enum class NSNumberKind(val mappedKotlinClassId: ClassId?, val objCType: ObjCType) { + CHAR(PrimitiveType.BYTE, ObjCPrimitiveType.char), + UNSIGNED_CHAR(uByteClassId, ObjCPrimitiveType.unsigned_char), + SHORT(PrimitiveType.SHORT, ObjCPrimitiveType.short), + UNSIGNED_SHORT(uShortClassId, ObjCPrimitiveType.unsigned_short), + INT(PrimitiveType.INT, ObjCPrimitiveType.int), + UNSIGNED_INT(uIntClassId, ObjCPrimitiveType.unsigned_int), + LONG(ObjCPrimitiveType.long), + UNSIGNED_LONG(ObjCPrimitiveType.unsigned_long), + LONG_LONG(PrimitiveType.LONG, ObjCPrimitiveType.long_long), + UNSIGNED_LONG_LONG(uLongClassId, ObjCPrimitiveType.unsigned_long_long), + FLOAT(PrimitiveType.FLOAT, ObjCPrimitiveType.float), + DOUBLE(PrimitiveType.DOUBLE, ObjCPrimitiveType.double), + BOOL(PrimitiveType.BOOLEAN, ObjCPrimitiveType.BOOL), + INTEGER(ObjCPrimitiveType.NSInteger), + UNSIGNED_INTEGER(ObjCPrimitiveType.NSUInteger) + + ; + + // UNSIGNED_SHORT -> unsignedShort + private val kindName = this.name.split('_') + .joinToString("") { it.lowercase().replaceFirstChar(Char::uppercaseChar) }.replaceFirstChar(Char::lowercaseChar) + + val valueSelector = kindName // unsignedShort + val initSelector = "initWith${kindName.replaceFirstChar(Char::uppercaseChar)}:" // initWithUnsignedShort: + val factorySelector = "numberWith${kindName.replaceFirstChar(Char::uppercaseChar)}:" // numberWithUnsignedShort: + + constructor( + primitiveType: PrimitiveType, + objCPrimitiveType: ObjCPrimitiveType, + ) : this(ClassId.topLevel(primitiveType.typeFqName), objCPrimitiveType) + + constructor(objCPrimitiveType: ObjCPrimitiveType) : this(null, objCPrimitiveType) +} + + diff --git a/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportBaseDeclarations.kt b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportBaseDeclarations.kt new file mode 100644 index 00000000000..4d0540a9a2e --- /dev/null +++ b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportBaseDeclarations.kt @@ -0,0 +1,261 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.objcexport + +import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi +import org.jetbrains.kotlin.name.ClassId + +@InternalKotlinNativeApi +fun objCBaseDeclarations( + topLevelNamePrefix: String, + objCNameOfAny: ObjCExportClassOrProtocolName, + objCNameOfNumber: ObjCExportClassOrProtocolName, + objCNameOfMutableMap: ObjCExportClassOrProtocolName, + objCNameOfMutableSet: ObjCExportClassOrProtocolName, + objCNameForNumberBox: (numberClassId: ClassId) -> ObjCExportClassOrProtocolName +): List = buildList { + add { + objCInterface(objCNameOfAny, superClass = "NSObject", members = buildList { + add { + ObjCMethod( + origin = null, + comment = null, + isInstanceMethod = true, + returnType = ObjCInstanceType, + selectors = listOf("init"), + parameters = emptyList(), + attributes = listOf("unavailable") + ) + } + add { + ObjCMethod( + origin = null, + comment = null, + isInstanceMethod = false, + returnType = ObjCInstanceType, + selectors = listOf("new"), + parameters = emptyList(), attributes = listOf("unavailable") + ) + } + add { + ObjCMethod( + origin = null, + comment = null, + isInstanceMethod = false, + returnType = ObjCVoidType, + selectors = listOf("initialize"), + parameters = emptyList(), + attributes = listOf("objc_requires_super") + ) + } + }) + } + + // TODO: add comment to the header. + add { + ObjCInterfaceImpl( + objCNameOfAny.objCName, + superProtocols = listOf("NSCopying"), + categoryName = "${objCNameOfAny.objCName}Copying", + origin = null, + comment = null, + generics = emptyList(), + attributes = emptyList(), + members = emptyList(), + superClass = null, + superClassGenerics = emptyList() + ) + } + + // TODO: only if appears + add { + val generics = listOf("ObjectType") + objCInterface( + objCNameOfMutableSet, + generics = generics, + superClass = "NSMutableSet", + superClassGenerics = generics + ) + } + + // TODO: only if appears + add { + val generics = listOf("KeyType", "ObjectType") + objCInterface( + objCNameOfMutableMap, + generics = generics, + superClass = "NSMutableDictionary", + superClassGenerics = generics + ) + } + + val nsErrorCategoryName = "NSError${topLevelNamePrefix}KotlinException" + add { + ObjCInterfaceImpl( + name = "NSError", + categoryName = nsErrorCategoryName, + members = buildList { + add { + ObjCProperty( + name = "kotlinException", + origin = null, + type = ObjCNullableReferenceType(ObjCIdType), + propertyAttributes = listOf("readonly"), + setterName = null, + getterName = null, + comment = null, + declarationAttributes = emptyList() + ) + } + }, + attributes = emptyList(), + comment = null, + origin = null, + superProtocols = emptyList(), + generics = emptyList(), + superClass = null, + superClassGenerics = emptyList() + ) + } + + genKotlinNumbers(objCNameOfNumber = objCNameOfNumber, objCNameForNumberBox = objCNameForNumberBox) +} + +private fun MutableList.genKotlinNumbers( + objCNameOfNumber: ObjCExportClassOrProtocolName, + objCNameForNumberBox: (numberClassId: ClassId) -> ObjCExportClassOrProtocolName +) { + val members = buildList { + NSNumberKind.entries.forEach { + add(nsNumberFactory(it, listOf("unavailable"))) + } + NSNumberKind.entries.forEach { + add(nsNumberInit(it, listOf("unavailable"))) + } + } + add { + objCInterface( + objCNameOfNumber, + superClass = "NSNumber", + members = members + ) + } + + NSNumberKind.entries.forEach { + if (it.mappedKotlinClassId != null) add { + genKotlinNumber(it.mappedKotlinClassId, it, objCNameOfNumber, objCNameForNumberBox) + } + } +} + + +private fun genKotlinNumber( + kotlinClassId: ClassId, + kind: NSNumberKind, + objCNameOfNumber: ObjCExportClassOrProtocolName, + objCNameForNumberBox: (numberClassId: ClassId) -> ObjCExportClassOrProtocolName +): ObjCInterface { + val name = objCNameForNumberBox(kotlinClassId) + + val members = buildList { + add { nsNumberFactory(kind) } + add { nsNumberInit(kind) } + } + return objCInterface( + name, + superClass = objCNameOfNumber.objCName, + members = members + ) +} + +private fun nsNumberInit(kind: NSNumberKind, attributes: List = emptyList()): ObjCMethod { + return ObjCMethod( + origin = null, + comment = null, + isInstanceMethod = false, + returnType = ObjCInstanceType, + selectors = listOf(kind.factorySelector), + parameters = listOf( + ObjCParameter(name = "value", origin = null, type = kind.objCType, todo = null) + ), + attributes = attributes + ) +} + +private fun nsNumberFactory(kind: NSNumberKind, attributes: List = emptyList()): ObjCMethod { + return ObjCMethod( + origin = null, + comment = null, + isInstanceMethod = true, + returnType = ObjCInstanceType, + selectors = listOf(kind.initSelector), + parameters = listOf(ObjCParameter("value", null, kind.objCType, todo = null)), + attributes = attributes + ) +} + + +private inline fun MutableList.add(producer: () -> T) { + add(producer()) +} + +private fun objCInterface( + name: ObjCExportClassOrProtocolName, + generics: List, + superClass: String, + superClassGenerics: List, +): ObjCInterface = objCInterface( + name, + generics = generics.map { ObjCGenericTypeRawDeclaration(it) }, + superClass = superClass, + superClassGenerics = superClassGenerics.map { ObjCGenericTypeRawUsage(it) } +) + +private fun objCInterface( + name: ObjCExportClassOrProtocolName, + generics: List = emptyList(), + superClass: String? = null, + superClassGenerics: List = emptyList(), + superProtocols: List = emptyList(), + members: List = emptyList(), + attributes: List = emptyList(), + comment: ObjCComment? = null, +): ObjCInterface = ObjCInterfaceImpl( + name = name.objCName, + generics = generics, + origin = null, + superClass = superClass, + superClassGenerics = superClassGenerics, + superProtocols = superProtocols, + members = members, + attributes = attributes.plus(name.toNameAttributes()), + comment = comment, + categoryName = null +) + +private fun objCProtocol( + name: ObjCExportClassOrProtocolName, + superProtocols: List, + members: List, + attributes: List = emptyList(), + comment: ObjCComment? = null, +): ObjCProtocol = ObjCProtocolImpl( + name = name.objCName, + origin = null, + superProtocols = superProtocols, + members = members, + attributes = attributes + name.toNameAttributes(), + comment = comment +) + + +private fun ObjCExportClassOrProtocolName.toNameAttributes(): List = listOfNotNull( + binaryName.takeIf { it != objCName }?.let { objcRuntimeNameAttribute(it) }, + swiftName.takeIf { it != objCName }?.let { swiftNameAttribute(it) } +) + +private fun swiftNameAttribute(swiftName: String) = "swift_name(\"$swiftName\")" +private fun objcRuntimeNameAttribute(name: String) = "objc_runtime_name(\"$name\")" \ No newline at end of file diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDataDir.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDataDir.kt index 30dd5078bfd..782732c4a31 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDataDir.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDataDir.kt @@ -8,4 +8,5 @@ package org.jetbrains.kotlin.backend.konan.testUtils import java.io.File val testDataDir = File("native/objcexport-header-generator/testData") -val headersTestDataDir = testDataDir.resolve("headers") \ No newline at end of file +val headersTestDataDir = testDataDir.resolve("headers") +val baseDeclarationsDir = testDataDir.resolve("baseDeclarations") \ No newline at end of file diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportBaseDeclarationsTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportBaseDeclarationsTest.kt new file mode 100644 index 00000000000..5e3e9a7cf3d --- /dev/null +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportBaseDeclarationsTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.tests + +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCTopLevel +import org.jetbrains.kotlin.backend.konan.objcexport.StubRenderer +import org.jetbrains.kotlin.backend.konan.testUtils.baseDeclarationsDir +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.junit.jupiter.api.Test +import java.io.File + +/** + * ## Test Scope + * This test will just invoke the generation of 'base declarations' (basically ObjC stubs that shall always be present) + * The generated declarations will be compared to previously checked-in 'golden' headers. + */ +class ObjCExportBaseDeclarationsTest( + /** + * Injected implementation: Either K1 or based upon Analysis API. + */ + private val generator: BaseDeclarationsGenerator, +) { + + @Test + fun `test - noTopLevelPrefix`() { + doTest(baseDeclarationsDir.resolve("!noTopLevelPrefix.h"), "") + } + + @Test + fun `test - topLevelPrefix`() { + doTest(baseDeclarationsDir.resolve("!topLevelPrefix.h"), "MyTopLevelPrefix") + } + + private fun doTest(headerFile: File, topLevelPrefix: String) { + val declarations = generator(topLevelPrefix) + + val renderedDeclarations = declarations + .flatMap { declaration -> StubRenderer.render(declaration) } + .joinToString(System.lineSeparator()) + + KotlinTestUtils.assertEqualsToFile(headerFile, renderedDeclarations) + } + + fun interface BaseDeclarationsGenerator { + operator fun invoke(topLevelPrefix: String): List + } +} diff --git a/native/objcexport-header-generator/testData/baseDeclarations/!noTopLevelPrefix.h b/native/objcexport-header-generator/testData/baseDeclarations/!noTopLevelPrefix.h new file mode 100644 index 00000000000..46adc551ff0 --- /dev/null +++ b/native/objcexport-header-generator/testData/baseDeclarations/!noTopLevelPrefix.h @@ -0,0 +1,105 @@ +__attribute__((swift_name("KotlinBase"))) +@interface Base : NSObject +- (instancetype)init __attribute__((unavailable)); ++ (instancetype)new __attribute__((unavailable)); ++ (void)initialize __attribute__((objc_requires_super)); +@end +@interface Base (BaseCopying) +@end +__attribute__((swift_name("KotlinMutableSet"))) +@interface MutableSet : NSMutableSet +@end +__attribute__((swift_name("KotlinMutableDictionary"))) +@interface MutableDictionary : NSMutableDictionary +@end +@interface NSError (NSErrorKotlinException) +@property (readonly) id _Nullable kotlinException; +@end +__attribute__((swift_name("KotlinNumber"))) +@interface Number : NSNumber +- (instancetype)initWithChar:(char)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedChar:(unsigned char)value __attribute__((unavailable)); +- (instancetype)initWithShort:(short)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedShort:(unsigned short)value __attribute__((unavailable)); +- (instancetype)initWithInt:(int)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedInt:(unsigned int)value __attribute__((unavailable)); +- (instancetype)initWithLong:(long)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedLong:(unsigned long)value __attribute__((unavailable)); +- (instancetype)initWithLongLong:(long long)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedLongLong:(unsigned long long)value __attribute__((unavailable)); +- (instancetype)initWithFloat:(float)value __attribute__((unavailable)); +- (instancetype)initWithDouble:(double)value __attribute__((unavailable)); +- (instancetype)initWithBool:(BOOL)value __attribute__((unavailable)); +- (instancetype)initWithInteger:(NSInteger)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedInteger:(NSUInteger)value __attribute__((unavailable)); ++ (instancetype)numberWithChar:(char)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedChar:(unsigned char)value __attribute__((unavailable)); ++ (instancetype)numberWithShort:(short)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedShort:(unsigned short)value __attribute__((unavailable)); ++ (instancetype)numberWithInt:(int)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedInt:(unsigned int)value __attribute__((unavailable)); ++ (instancetype)numberWithLong:(long)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedLong:(unsigned long)value __attribute__((unavailable)); ++ (instancetype)numberWithLongLong:(long long)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedLongLong:(unsigned long long)value __attribute__((unavailable)); ++ (instancetype)numberWithFloat:(float)value __attribute__((unavailable)); ++ (instancetype)numberWithDouble:(double)value __attribute__((unavailable)); ++ (instancetype)numberWithBool:(BOOL)value __attribute__((unavailable)); ++ (instancetype)numberWithInteger:(NSInteger)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedInteger:(NSUInteger)value __attribute__((unavailable)); +@end +__attribute__((swift_name("KotlinByte"))) +@interface Byte : Number +- (instancetype)initWithChar:(char)value; ++ (instancetype)numberWithChar:(char)value; +@end +__attribute__((swift_name("KotlinUByte"))) +@interface UByte : Number +- (instancetype)initWithUnsignedChar:(unsigned char)value; ++ (instancetype)numberWithUnsignedChar:(unsigned char)value; +@end +__attribute__((swift_name("KotlinShort"))) +@interface Short : Number +- (instancetype)initWithShort:(short)value; ++ (instancetype)numberWithShort:(short)value; +@end +__attribute__((swift_name("KotlinUShort"))) +@interface UShort : Number +- (instancetype)initWithUnsignedShort:(unsigned short)value; ++ (instancetype)numberWithUnsignedShort:(unsigned short)value; +@end +__attribute__((swift_name("KotlinInt"))) +@interface Int : Number +- (instancetype)initWithInt:(int)value; ++ (instancetype)numberWithInt:(int)value; +@end +__attribute__((swift_name("KotlinUInt"))) +@interface UInt : Number +- (instancetype)initWithUnsignedInt:(unsigned int)value; ++ (instancetype)numberWithUnsignedInt:(unsigned int)value; +@end +__attribute__((swift_name("KotlinLong"))) +@interface Long : Number +- (instancetype)initWithLongLong:(long long)value; ++ (instancetype)numberWithLongLong:(long long)value; +@end +__attribute__((swift_name("KotlinULong"))) +@interface ULong : Number +- (instancetype)initWithUnsignedLongLong:(unsigned long long)value; ++ (instancetype)numberWithUnsignedLongLong:(unsigned long long)value; +@end +__attribute__((swift_name("KotlinFloat"))) +@interface Float : Number +- (instancetype)initWithFloat:(float)value; ++ (instancetype)numberWithFloat:(float)value; +@end +__attribute__((swift_name("KotlinDouble"))) +@interface Double : Number +- (instancetype)initWithDouble:(double)value; ++ (instancetype)numberWithDouble:(double)value; +@end +__attribute__((swift_name("KotlinBoolean"))) +@interface Boolean : Number +- (instancetype)initWithBool:(BOOL)value; ++ (instancetype)numberWithBool:(BOOL)value; +@end diff --git a/native/objcexport-header-generator/testData/baseDeclarations/!topLevelPrefix.h b/native/objcexport-header-generator/testData/baseDeclarations/!topLevelPrefix.h new file mode 100644 index 00000000000..87bdcb7c470 --- /dev/null +++ b/native/objcexport-header-generator/testData/baseDeclarations/!topLevelPrefix.h @@ -0,0 +1,105 @@ +__attribute__((swift_name("KotlinBase"))) +@interface MyTopLevelPrefixBase : NSObject +- (instancetype)init __attribute__((unavailable)); ++ (instancetype)new __attribute__((unavailable)); ++ (void)initialize __attribute__((objc_requires_super)); +@end +@interface MyTopLevelPrefixBase (MyTopLevelPrefixBaseCopying) +@end +__attribute__((swift_name("KotlinMutableSet"))) +@interface MyTopLevelPrefixMutableSet : NSMutableSet +@end +__attribute__((swift_name("KotlinMutableDictionary"))) +@interface MyTopLevelPrefixMutableDictionary : NSMutableDictionary +@end +@interface NSError (NSErrorMyTopLevelPrefixKotlinException) +@property (readonly) id _Nullable kotlinException; +@end +__attribute__((swift_name("KotlinNumber"))) +@interface MyTopLevelPrefixNumber : NSNumber +- (instancetype)initWithChar:(char)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedChar:(unsigned char)value __attribute__((unavailable)); +- (instancetype)initWithShort:(short)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedShort:(unsigned short)value __attribute__((unavailable)); +- (instancetype)initWithInt:(int)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedInt:(unsigned int)value __attribute__((unavailable)); +- (instancetype)initWithLong:(long)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedLong:(unsigned long)value __attribute__((unavailable)); +- (instancetype)initWithLongLong:(long long)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedLongLong:(unsigned long long)value __attribute__((unavailable)); +- (instancetype)initWithFloat:(float)value __attribute__((unavailable)); +- (instancetype)initWithDouble:(double)value __attribute__((unavailable)); +- (instancetype)initWithBool:(BOOL)value __attribute__((unavailable)); +- (instancetype)initWithInteger:(NSInteger)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedInteger:(NSUInteger)value __attribute__((unavailable)); ++ (instancetype)numberWithChar:(char)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedChar:(unsigned char)value __attribute__((unavailable)); ++ (instancetype)numberWithShort:(short)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedShort:(unsigned short)value __attribute__((unavailable)); ++ (instancetype)numberWithInt:(int)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedInt:(unsigned int)value __attribute__((unavailable)); ++ (instancetype)numberWithLong:(long)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedLong:(unsigned long)value __attribute__((unavailable)); ++ (instancetype)numberWithLongLong:(long long)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedLongLong:(unsigned long long)value __attribute__((unavailable)); ++ (instancetype)numberWithFloat:(float)value __attribute__((unavailable)); ++ (instancetype)numberWithDouble:(double)value __attribute__((unavailable)); ++ (instancetype)numberWithBool:(BOOL)value __attribute__((unavailable)); ++ (instancetype)numberWithInteger:(NSInteger)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedInteger:(NSUInteger)value __attribute__((unavailable)); +@end +__attribute__((swift_name("KotlinByte"))) +@interface MyTopLevelPrefixByte : MyTopLevelPrefixNumber +- (instancetype)initWithChar:(char)value; ++ (instancetype)numberWithChar:(char)value; +@end +__attribute__((swift_name("KotlinUByte"))) +@interface MyTopLevelPrefixUByte : MyTopLevelPrefixNumber +- (instancetype)initWithUnsignedChar:(unsigned char)value; ++ (instancetype)numberWithUnsignedChar:(unsigned char)value; +@end +__attribute__((swift_name("KotlinShort"))) +@interface MyTopLevelPrefixShort : MyTopLevelPrefixNumber +- (instancetype)initWithShort:(short)value; ++ (instancetype)numberWithShort:(short)value; +@end +__attribute__((swift_name("KotlinUShort"))) +@interface MyTopLevelPrefixUShort : MyTopLevelPrefixNumber +- (instancetype)initWithUnsignedShort:(unsigned short)value; ++ (instancetype)numberWithUnsignedShort:(unsigned short)value; +@end +__attribute__((swift_name("KotlinInt"))) +@interface MyTopLevelPrefixInt : MyTopLevelPrefixNumber +- (instancetype)initWithInt:(int)value; ++ (instancetype)numberWithInt:(int)value; +@end +__attribute__((swift_name("KotlinUInt"))) +@interface MyTopLevelPrefixUInt : MyTopLevelPrefixNumber +- (instancetype)initWithUnsignedInt:(unsigned int)value; ++ (instancetype)numberWithUnsignedInt:(unsigned int)value; +@end +__attribute__((swift_name("KotlinLong"))) +@interface MyTopLevelPrefixLong : MyTopLevelPrefixNumber +- (instancetype)initWithLongLong:(long long)value; ++ (instancetype)numberWithLongLong:(long long)value; +@end +__attribute__((swift_name("KotlinULong"))) +@interface MyTopLevelPrefixULong : MyTopLevelPrefixNumber +- (instancetype)initWithUnsignedLongLong:(unsigned long long)value; ++ (instancetype)numberWithUnsignedLongLong:(unsigned long long)value; +@end +__attribute__((swift_name("KotlinFloat"))) +@interface MyTopLevelPrefixFloat : MyTopLevelPrefixNumber +- (instancetype)initWithFloat:(float)value; ++ (instancetype)numberWithFloat:(float)value; +@end +__attribute__((swift_name("KotlinDouble"))) +@interface MyTopLevelPrefixDouble : MyTopLevelPrefixNumber +- (instancetype)initWithDouble:(double)value; ++ (instancetype)numberWithDouble:(double)value; +@end +__attribute__((swift_name("KotlinBoolean"))) +@interface MyTopLevelPrefixBoolean : MyTopLevelPrefixNumber +- (instancetype)initWithBool:(BOOL)value; ++ (instancetype)numberWithBool:(BOOL)value; +@end