From 797284dada66bdfd9c190c93f9e3ed8e5ead2108 Mon Sep 17 00:00:00 2001 From: "eugene.levenetc" Date: Fri, 26 Jan 2024 12:46:39 +0100 Subject: [PATCH] [ObjCExport] Implement initial processing queue (stable order, dependencies, forward declarations) This commit will introduce the first processing queue which will take care of properly ordering the 'to translate' symbols as well as taking care of 'dependency' symbols (aka symbols mentioned in signatures or as supertypes). - There are several changes like properly translating types as ObjCProtocolType instead of ObjCClassType (if origin was an interface) - Type translation of generics defined on interfaces will also emit id type. - Add initial nested classes collection to the queue - Add extension function test, add tickets references ^KT-65237 Verification Pending ^KT-65329 Verification Pending --- .../TranslationOrder.md | 20 ++ .../objcexport/analysisApiUtils/errors.kt | 2 +- .../getAllClassOrObjectSymbols.kt | 40 ++++ .../getDeclaredSuperInterfaceSymbols.kt | 28 +++ .../analysisApiUtils/isCloneable.kt | 15 ++ .../objcexport/translateSuperInterfaces.kt | 18 ++ .../kotlin/objcexport/translateToObjCClass.kt | 17 +- .../objcexport/translateToObjCExportStubs.kt | 18 ++ .../objcexport/translateToObjCHeader.kt | 186 ++++++++++------- .../objcexport/translateToObjCObject.kt | 11 +- .../objcexport/translateToObjCProtocol.kt | 20 +- .../kotlin/objcexport/translateToObjCType.kt | 27 ++- ...rdedClassesAndProtocolsDependenciesTest.kt | 89 ++++++++ .../tests/GetAllClassOrObjectSymbolsTest.kt | 65 ++++++ .../tests/GetSuperInterfacesTest.kt | 79 +++++++ .../objcexport/tests/IsCloneableTest.kt | 46 +++++ .../objcexport/ObjCExportHeaderGenerator.kt | 3 +- .../backend/konan/objcexport/objcTypes.kt | 17 +- .../backend/konan/testUtils/testDataDir.kt | 3 +- .../konan/tests/ObjCDependenciesTypesTest.kt | 59 ++++++ .../ObjCExportForwardDeclarationsTest.kt | 5 - .../tests/ObjCExportHeaderGeneratorTest.kt | 39 +++- .../testData/dependencies/array/!array.h | 49 +++++ .../testData/dependencies/array/Foo.kt | 1 + .../dependencies/iterator/!iterator.h | 36 ++++ .../testData/dependencies/iterator/Foo.kt | 1 + .../stringBuilder/!stringBuilder.h | 194 ++++++++++++++++++ .../dependencies/stringBuilder/Foo.kt | 1 + ...ssReferencingDependencyClassAsReturnType.h | 47 +++++ .../Foo.kt | 3 + .../!classReferencingOtherClassAsReturnType.h | 38 ++++ .../Foo.kt | 5 + .../extensionFunctions/!extensionFunctions.h | 36 ++++ .../headers/extensionFunctions/Foo.kt | 5 + .../!interfaceImplementingInterfaceOrder.h | 33 +++ .../Foo.kt | 3 + ...aceReferencingOtherInterfaceAsReturnType.h | 34 +++ .../Foo.kt | 5 + .../headers/nestedClass/!nestedClass.h | 25 ++- .../testData/headers/nestedClass/Foo.kt | 7 +- 40 files changed, 1213 insertions(+), 117 deletions(-) create mode 100644 native/objcexport-header-generator/TranslationOrder.md create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getAllClassOrObjectSymbols.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getDeclaredSuperInterfaceSymbols.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/isCloneable.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateSuperInterfaces.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExportStubs.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/ForwardedClassesAndProtocolsDependenciesTest.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetAllClassOrObjectSymbolsTest.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetSuperInterfacesTest.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/IsCloneableTest.kt create mode 100644 native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt create mode 100644 native/objcexport-header-generator/testData/dependencies/array/!array.h create mode 100644 native/objcexport-header-generator/testData/dependencies/array/Foo.kt create mode 100644 native/objcexport-header-generator/testData/dependencies/iterator/!iterator.h create mode 100644 native/objcexport-header-generator/testData/dependencies/iterator/Foo.kt create mode 100644 native/objcexport-header-generator/testData/dependencies/stringBuilder/!stringBuilder.h create mode 100644 native/objcexport-header-generator/testData/dependencies/stringBuilder/Foo.kt create mode 100644 native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/!classReferencingDependencyClassAsReturnType.h create mode 100644 native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/Foo.kt create mode 100644 native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/!classReferencingOtherClassAsReturnType.h create mode 100644 native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/Foo.kt create mode 100644 native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h create mode 100644 native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt create mode 100644 native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/!interfaceImplementingInterfaceOrder.h create mode 100644 native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/Foo.kt create mode 100644 native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/!interfaceReferencingOtherInterfaceAsReturnType.h create mode 100644 native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/Foo.kt diff --git a/native/objcexport-header-generator/TranslationOrder.md b/native/objcexport-header-generator/TranslationOrder.md new file mode 100644 index 00000000000..372dcf0228d --- /dev/null +++ b/native/objcexport-header-generator/TranslationOrder.md @@ -0,0 +1,20 @@ +# Translation Order +This document will compile all knowledge about the order of translation of symbols and in which order those symbols shall +be present in the resulting header. + + +## Rule 1 +We first order symbols by package (alphabetically), then by containing file name, then by class name. + +Note: +Classifiers order will not be further documented here as it is not contextual (at least as we understand it right now) + +## Rule 2 +If a type is mentioned as supertype, we see that the supertype is declared in the resulting header *before* the using class; +however, the supertype naming will be ordered *after* the current class + +## Rule 3 +If a type is mentioned as return or paramter type, we see a forward declaration being emitted, but the resulting order +of the header is unaffected. + + diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt index c1f94957c19..69788cfb896 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt @@ -44,5 +44,5 @@ internal val errorInterface superClassGenerics = emptyList() ) -internal val objCErrorType = ObjCClassType(errorClassName) +internal val objCErrorType = ObjCClassType(errorClassName, classId = null) internal val errorForwardClass = ObjCClassForwardDeclaration(errorClassName) \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getAllClassOrObjectSymbols.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getAllClassOrObjectSymbols.kt new file mode 100644 index 00000000000..be379e6a3be --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getAllClassOrObjectSymbols.kt @@ -0,0 +1,40 @@ +package org.jetbrains.kotlin.objcexport.analysisApiUtils + +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtClassifierSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol + + +/** + * Returns all classes or objects (including transitively nested ones) contained within a file + * + * Example: + *```kotlin + * class A { + * class B { + * class C + * } + * } + *``` + * returns `sequenceOf(A, B, C)` + */ +context(KtAnalysisSession) +internal fun KtFileSymbol.getAllClassOrObjectSymbols(): List { + return getFileScope().getClassifierSymbols() + .filterIsInstance() + .flatMap { classSymbol -> listOf(classSymbol) + classSymbol.getAllClassOrObjectSymbols() } + .toList() +} + +context(KtAnalysisSession) +private fun KtClassOrObjectSymbol.getAllClassOrObjectSymbols(): Sequence { + return sequence { + val nestedClasses = getMemberScope().getClassifierSymbols().filterIsInstance() + yieldAll(nestedClasses) + + nestedClasses.forEach { nestedClass -> + yieldAll(nestedClass.getAllClassOrObjectSymbols()) + } + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getDeclaredSuperInterfaceSymbols.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getDeclaredSuperInterfaceSymbols.kt new file mode 100644 index 00000000000..6cdc207cccb --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getDeclaredSuperInterfaceSymbols.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2024 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.analysisApiUtils + +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol +import org.jetbrains.kotlin.analysis.api.types.KtClassType +import org.jetbrains.kotlin.analysis.api.types.KtClassTypeQualifier + +/** + * @return The **declared** super interfaces (**not the transitive closure**) + */ +context(KtAnalysisSession) +internal fun KtClassOrObjectSymbol.getDeclaredSuperInterfaceSymbols(): List { + return superTypes + .asSequence() + .mapNotNull { type -> type as? KtClassType } + .flatMap { type -> type.qualifiers } + .mapNotNull { qualifier -> qualifier as? KtClassTypeQualifier.KtResolvedClassTypeQualifier } + .mapNotNull { it.symbol as? KtClassOrObjectSymbol } + .filter { !it.isCloneable } // TODO: Write unit test for this + .filter { superInterface -> superInterface.classKind == KtClassKind.INTERFACE } + .toList() +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/isCloneable.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/isCloneable.kt new file mode 100644 index 00000000000..a3182bd3311 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/isCloneable.kt @@ -0,0 +1,15 @@ +package org.jetbrains.kotlin.objcexport.analysisApiUtils + +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.name.ClassId + +internal val KtClassOrObjectSymbol.isCloneable: Boolean + get() { + return classIdIfNonLocal?.isCloneable ?: false + } + +internal val ClassId.isCloneable: Boolean + get() { + return asSingleFqName() == StandardNames.FqNames.cloneable.toSafe() + } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateSuperInterfaces.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateSuperInterfaces.kt new file mode 100644 index 00000000000..0dcd3c930a2 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateSuperInterfaces.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2024 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 + +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol +import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDeclaredSuperInterfaceSymbols + +context(KtAnalysisSession, KtObjCExportSession) +internal fun KtClassOrObjectSymbol.translateSuperInterfaces(): List { + return getDeclaredSuperInterfaceSymbols().mapNotNull { superInterfaceSymbol -> + superInterfaceSymbol.translateToObjCProtocol() + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCClass.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCClass.kt index 84b6c94239f..232e042d3f0 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCClass.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCClass.kt @@ -6,7 +6,6 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllMembers import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC @@ -18,7 +17,7 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? { val superClass = getSuperClassSymbolNotAny() val kotlinAnyName = getDefaultSuperClassOrProtocolName() - val superName = if (superClass == null) kotlinAnyName else throw RuntimeException("Super class translation isn't implemented yet") + val superName = if (superClass == null) kotlinAnyName else getSuperClassName() val enumKind = this.classKind == KtClassKind.ENUM_CLASS val final = if (this is KtSymbolWithModality) this.modality == Modality.FINAL else false val attributes = if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList() @@ -28,9 +27,10 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? { val origin: ObjCExportStubOrigin = getObjCExportStubOrigin() val superProtocols: List = superProtocols() - val members: List = getAllMembers() - .sortedWith(StableSymbolOrder) + val members: List = getMemberScope().getCallableSymbols().plus(getMemberScope().getConstructors()) + .sortedWith(StableCallableOrder) .flatMap { it.translateToObjCExportStubs() } + .toList() val categoryName: String? = null val generics: List = emptyList() @@ -57,6 +57,13 @@ private fun abbreviate(name: String): String { val uppers = normalizedName.filterIndexed { index, character -> index == 0 || character.isUpperCase() } if (uppers.length >= 3) return uppers - return normalizedName } + +/** + * See issue KT-65384 + * And K1 implementation [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.translateClass] + */ +private fun KtClassOrObjectSymbol.getSuperClassName(): ObjCExportClassOrProtocolName { + return ObjCExportClassOrProtocolName("UnimplementedSwiftName", "UnimplementedObjCName") +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExportStubs.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExportStubs.kt new file mode 100644 index 00000000000..d881f577b95 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExportStubs.kt @@ -0,0 +1,18 @@ +package org.jetbrains.kotlin.objcexport + +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtConstructorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub + +context(KtAnalysisSession, KtObjCExportSession) +internal fun KtCallableSymbol.translateToObjCExportStubs(): List { + return when (this) { + is KtConstructorSymbol -> translateToObjCConstructors() + is KtPropertySymbol -> listOfNotNull(translateToObjCProperty()) + is KtFunctionSymbol -> listOfNotNull(translateToObjCMethod()) + else -> emptyList() + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt index d3fb320e616..af32e45084d 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt @@ -1,94 +1,136 @@ /* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2024 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 import org.jetbrains.kotlin.analysis.api.KtAnalysisSession -import org.jetbrains.kotlin.analysis.api.symbols.* -import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind.* +import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol import org.jetbrains.kotlin.backend.konan.objcexport.* -import org.jetbrains.kotlin.objcexport.analysisApiUtils.errorForwardClass -import org.jetbrains.kotlin.objcexport.analysisApiUtils.errorInterface -import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasErrorTypes +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.objcexport.analysisApiUtils.* import org.jetbrains.kotlin.psi.KtFile - +import org.jetbrains.kotlin.utils.addIfNotNull context(KtAnalysisSession, KtObjCExportSession) fun translateToObjCHeader(files: List): ObjCHeader { - val declarations = files - .sortedWith(StableFileOrder) - .flatMap { file -> file.getFileSymbol().translateToObjCExportStubs() } - .toMutableList() + val stubs = mutableListOf() + val protocolForwardDeclarations = mutableSetOf() + val classForwardDeclarations = mutableSetOf() - val classForwardDeclarations = getClassForwardDeclarations(declarations).toMutableSet() - val protocolForwardDeclarations = getProtocolForwardDeclarations(declarations) + val symbolTranslationQueue = mutableListOf() + val translatedClassifiers = mutableMapOf() - if (declarations.hasErrorTypes()) { - declarations.add(errorInterface) + fun process( + symbol: KtSymbol, + forwardProtocolsAndClasses: Boolean = false, + ): List { + val result = mutableListOf() + when (symbol) { + /* In this case: Go and translate all classes/objects/interfaces inside the file as well */ + is KtFileSymbol -> { + val translatedTopLevelFileFacade = symbol.translateToObjCTopLevelInterfaceFileFacade() + result.addIfNotNull(translatedTopLevelFileFacade) + + symbolTranslationQueue.addAll( + symbol.getAllClassOrObjectSymbols().sortedWith(StableClassifierOrder) + ) + } + + /* Translate the Class/Interface, but ensure that all supertypes will be translated also */ + is KtClassOrObjectSymbol -> { + val classId = symbol.classIdIfNonLocal ?: return result + + /* Add the classId to already processed classIds and do not redo if already processed */ + val stub = translatedClassifiers.getOrPut(classId) { + + val translatedObjCClassOrProtocol = when (symbol.classKind) { + KtClassKind.INTERFACE -> symbol.translateToObjCProtocol() + KtClassKind.CLASS -> symbol.translateToObjCClass() + KtClassKind.OBJECT -> symbol.translateToObjCObject() + else -> return result + } ?: return result + + symbol.getDeclaredSuperInterfaceSymbols().forEach { superInterfaceSymbol -> + result.addAll(process(superInterfaceSymbol)) + } + + result.add(translatedObjCClassOrProtocol) + translatedObjCClassOrProtocol + } + + if (forwardProtocolsAndClasses) { + when (stub) { + is ObjCInterface -> classForwardDeclarations.add(ObjCClassForwardDeclaration(stub.name, stub.generics)) + is ObjCProtocol -> protocolForwardDeclarations.add(stub.name) + } + } + } + } + return result + } + + fun processDeclaredSymbols(symbols: List): List { + val result = mutableListOf() + symbolTranslationQueue.addAll(symbols) + while (true) { + val next = symbolTranslationQueue.removeFirstOrNull() ?: break + result.addAll(process(next)) + } + return result + } + + fun processDependencySymbols(stubs: List): List { + val dependencyClassSymbols = stubs.closureSequence() + .mapNotNull { stub -> + when (stub) { + is ObjCMethod -> stub.returnType + is ObjCParameter -> stub.type + is ObjCProperty -> stub.type + is ObjCTopLevel -> null + } + } + .flatMap { type -> + if (type is ObjCClassType) type.typeArguments + type + else listOf(type) + } + .mapNotNull { if (it is ObjCReferenceType) it.classId else null } + .mapNotNull { classId -> getClassOrObjectSymbolByClassId(classId) } + .toList() + + symbolTranslationQueue.addAll(dependencyClassSymbols) + + val result = dependencyClassSymbols.flatMap { symbol -> + process(symbol, forwardProtocolsAndClasses = true) + } + + return if (result.isNotEmpty()) result + processDependencySymbols(result) + else result + } + + val fileSymbols = files.sortedWith(StableFileOrder).map { it.getFileSymbol() } + val declaredStubs = processDeclaredSymbols(fileSymbols) + val dependencyStubs = processDependencySymbols(declaredStubs) + + stubs.addAll(declaredStubs + dependencyStubs) + + if (stubs.hasErrorTypes()) { + stubs.add(errorInterface) classForwardDeclarations.add(errorForwardClass) } + protocolForwardDeclarations += stubs + .filterIsInstance() + .flatMap { it.superProtocols } + return ObjCHeader( - stubs = declarations, + stubs = stubs, classForwardDeclarations = classForwardDeclarations, protocolForwardDeclarations = protocolForwardDeclarations, - additionalImports = emptyList(), + additionalImports = emptyList() ) -} - -/** - * Class which have static property must have forward declaration - * - * ``` - * @class Foo; - * - * @interface Foo - * @property (class) Foo - * @end - * ``` - */ -private fun getClassForwardDeclarations(declarations: List): Set { - return declarations - .filterIsInstance() - .filter { clazz -> - clazz.members - .filterIsInstance() - .any { property -> - val className = (property.type as? ObjCClassType)?.className == clazz.name - val static = property.propertyAttributes.contains("class") - className && static - } - }.map { clazz -> - ObjCClassForwardDeclaration(clazz.name) - }.toSet() -} - -private fun getProtocolForwardDeclarations(declarations: List) = declarations - .filterIsInstance() - .flatMap { it.superProtocols } - .toSet() - - -context(KtAnalysisSession, KtObjCExportSession) -fun KtFileSymbol.translateToObjCExportStubs(): List { - return listOfNotNull(translateToObjCTopLevelInterfaceFileFacade()) + getFileScope().getClassifierSymbols() - .sortedWith(StableClassifierOrder) - .flatMap { classifierSymbol -> classifierSymbol.translateToObjCExportStubs() } -} - - -context(KtAnalysisSession, KtObjCExportSession) -internal fun KtSymbol.translateToObjCExportStubs(): List { - return when { - this is KtFileSymbol -> translateToObjCExportStubs() - this is KtClassOrObjectSymbol && classKind == INTERFACE -> listOfNotNull(translateToObjCProtocol()) - this is KtClassOrObjectSymbol && classKind == CLASS -> listOfNotNull(translateToObjCClass()) - this is KtClassOrObjectSymbol && classKind == OBJECT -> listOfNotNull(translateToObjCObject()) - this is KtConstructorSymbol -> translateToObjCConstructors() - this is KtPropertySymbol -> listOfNotNull(translateToObjCProperty()) - this is KtFunctionSymbol -> listOfNotNull(translateToObjCMethod()) - else -> emptyList() - } } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt index 72beaf28e02..da773b00599 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt @@ -6,7 +6,6 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllMembers import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC @@ -32,9 +31,10 @@ fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? { val superClassGenerics: List = emptyList() val objectMembers = getDefaultMembers() - getAllMembers().flatMap { it.translateToObjCExportStubs() }.forEach { - objectMembers.add(it) - } + getMemberScope().getCallableSymbols() + .sortedWith(StableCallableOrder) + .flatMap { it.translateToObjCExportStubs() } + .forEach { objectMembers.add(it) } return ObjCInterfaceImpl( name.objCName, @@ -97,7 +97,8 @@ private fun KtClassOrObjectSymbol.getDefaultMembers(): MutableList { - return superTypes - .asSequence() - .filter { type -> !type.isAny } - .mapNotNull { type -> type as? KtClassType } - .flatMap { type -> type.qualifiers } - .mapNotNull { qualifier -> qualifier as? KtClassTypeQualifier.KtResolvedClassTypeQualifier } - .mapNotNull { it.symbol as? KtClassOrObjectSymbol } - .filter { superInterface -> superInterface.classKind == KtClassKind.INTERFACE } + return getDeclaredSuperInterfaceSymbols() + .filter { superInterface -> !superInterface.isCloneable } .map { superInterface -> superInterface.getObjCClassOrProtocolName().objCName } .toList() } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt index b596b50bf80..ddc8a32793b 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt @@ -4,6 +4,8 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol import org.jetbrains.kotlin.analysis.api.types.KtErrorType import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType @@ -79,9 +81,9 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference } /* Check if inline type represents 'regular' inline class */ + val classSymbol: KtClassOrObjectSymbol? = if (classId != null) getClassOrObjectSymbolByClassId(classId) else null run check@{ if (classId == null) return@check - val classSymbol = getClassOrObjectSymbolByClassId(classId) ?: return@check if (classSymbol !is KtNamedClassOrObjectSymbol) return@check if (classSymbol.isInline) return ObjCIdType } @@ -112,15 +114,30 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference } if (fullyExpandedType is KtNonErrorClassType) { - val typeName = typesMap[classId] - ?: fullyExpandedType.classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName + typesMap[classId]?.let { typeName -> + val typeArguments = translateToObjCTypeArguments() + return ObjCClassType(typeName, typeArguments, classId = null) // todo: not clear for reader why null is important here! + } + val typeName = fullyExpandedType.classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName val typeArguments = translateToObjCTypeArguments() - return ObjCClassType(typeName, typeArguments) + + // TODO NOW: create type translation test + if (classSymbol?.classKind == KtClassKind.INTERFACE) { + return ObjCProtocolType(typeName, classId) + } + + return ObjCClassType(typeName, typeArguments, classId) } if (fullyExpandedType is KtTypeParameterType) { - if (fullyExpandedType.symbol.getContainingSymbol() is KtCallableSymbol) { + val definingSymbol = fullyExpandedType.symbol.getContainingSymbol() + + if (definingSymbol is KtCallableSymbol) { + return ObjCIdType + } + + if (definingSymbol is KtClassOrObjectSymbol && definingSymbol.classKind == KtClassKind.INTERFACE) { return ObjCIdType } /* diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/ForwardedClassesAndProtocolsDependenciesTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/ForwardedClassesAndProtocolsDependenciesTest.kt new file mode 100644 index 00000000000..941cae767e2 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/ForwardedClassesAndProtocolsDependenciesTest.kt @@ -0,0 +1,89 @@ +package org.jetbrains.kotlin.objcexport.tests + +import org.intellij.lang.annotations.Language +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader +import org.jetbrains.kotlin.objcexport.KtObjCExportConfiguration +import org.jetbrains.kotlin.objcexport.KtObjCExportSession +import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis +import org.jetbrains.kotlin.objcexport.translateToObjCHeader +import org.jetbrains.kotlin.psi.KtFile +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +/** + * Tests logic of filtering protocols and classes which must be forwarded or excluded when dependency is used + * For full tests of dependencies see [ObjCDependenciesTypesTest] + */ +class ForwardedClassesAndProtocolsDependenciesTest( + private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis, +) { + @Test + fun `test - iterator`() { + doTest( + code = """ + val i: Iterator + """, + protocols = setOf("Iterator"), + classes = emptySet() + ) + } + + @Test + fun `test - array`() { + doTest( + code = """ + val i: Array + """, + protocols = setOf("Iterator"), + classes = setOf("Array") + ) + } + + @Test + fun `test - string builder`() { + doTest( + code = """ + val i: StringBuilder + """, + protocols = setOf("CharSequence", "Appendable", "Iterator"), + classes = setOf("StringBuilder", "CharArray", "CharIterator") + ) + } + + @Test + fun `test - declared symbols`() { + doTest( + code = """ + open class ClassA + class ClassB: ClassA() + interface InterfaceA + interface InterfaceB : InterfaceA() + fun getClass(): ClassB = error("error") + fun getInterface(): InterfaceB = error("error") + """, + protocols = setOf("InterfaceB", "InterfaceA"), + classes = setOf("ClassB") + ) + } + + private fun doTest( + @Language("kotlin") code: String, + protocols: Set, + classes: Set, + ) { + val file = inlineSourceCodeAnalysis.createKtFile(code.trimIndent()) + val classesAndProtocols = translateClassesAndProtocols(file) + + assertEquals(protocols, classesAndProtocols.protocolForwardDeclarations.toSet(), "Invalid protocols set") + assertEquals(classes, classesAndProtocols.classForwardDeclarations.map { it.className }.toSet(), "Invalid classes set") + } + + private fun translateClassesAndProtocols(file: KtFile): ObjCHeader { + return analyze(file) { + KtObjCExportSession(KtObjCExportConfiguration()) { + translateToObjCHeader(listOf(file)) + } + } + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetAllClassOrObjectSymbolsTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetAllClassOrObjectSymbolsTest.kt new file mode 100644 index 00000000000..1ab86cf616b --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetAllClassOrObjectSymbolsTest.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2024 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.tests + +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllClassOrObjectSymbols +import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis +import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class GetAllClassOrObjectSymbolsTest( + private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis, +) { + + @Test + fun `test - no classifiers in file`() { + val file = inlineSourceCodeAnalysis.createKtFile("val foo = 42") + analyze(file) { + assertEquals(emptyList(), file.getFileSymbol().getAllClassOrObjectSymbols()) + } + } + + @Test + fun `test - single class in file`() { + val file = inlineSourceCodeAnalysis.createKtFile("class Foo") + analyze(file) { + assertEquals(listOf(file.getClassOrFail("Foo")), file.getFileSymbol().getAllClassOrObjectSymbols()) + } + } + + @Test + fun `test - multiple nested classes in file`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + class A { + class B { + class C + } + } + + class D { + class E + } + """.trimIndent() + ) + + analyze(file) { + assertEquals( + listOf( + file.getClassOrFail("A"), + file.getClassOrFail("A").getMemberScope().getClassOrFail("B"), + file.getClassOrFail("A").getMemberScope().getClassOrFail("B").getMemberScope().getClassOrFail("C"), + + file.getClassOrFail("D"), + file.getClassOrFail("D").getMemberScope().getClassOrFail("E") + ), + file.getFileSymbol().getAllClassOrObjectSymbols() + ) + } + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetSuperInterfacesTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetSuperInterfacesTest.kt new file mode 100644 index 00000000000..2aebee6934b --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/GetSuperInterfacesTest.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2024 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.tests + +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDeclaredSuperInterfaceSymbols +import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis +import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class GetSuperInterfacesTest( + private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis, +) { + @Test + fun `test - transitive super interfaces`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + interface A + interface B + interface C + + interface X: A, B + interface Y : C + + class Foo: X, Y + """.trimIndent() + ) + + analyze(file) { + val foo = file.getClassOrFail("Foo") + + assertEquals( + listOf(file.getClassOrFail("X"), file.getClassOrFail("Y")), + foo.getDeclaredSuperInterfaceSymbols() + ) + } + } + + @Test + fun `test - super interface and super class`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + interface A + interface B + abstract class X + class Foo: X(), A, B + """.trimIndent() + ) + + analyze(file) { + assertEquals( + listOf(file.getClassOrFail("A"), file.getClassOrFail("B")), + file.getClassOrFail("Foo").getDeclaredSuperInterfaceSymbols() + ) + } + } + + @Test + fun `test - subclassing Any explicitly`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + interface A + interface B + class Foo: Any(), A, B + """.trimIndent() + ) + + analyze(file) { + assertEquals( + listOf(file.getClassOrFail("A"), file.getClassOrFail("B")), + file.getClassOrFail("Foo").getDeclaredSuperInterfaceSymbols() + ) + } + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/IsCloneableTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/IsCloneableTest.kt new file mode 100644 index 00000000000..3fb5c5de9b6 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/IsCloneableTest.kt @@ -0,0 +1,46 @@ +package org.jetbrains.kotlin.objcexport.tests + +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertTrue +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol +import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCloneable +import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis +import org.jetbrains.kotlin.objcexport.testUtils.getPropertyOrFail +import org.junit.jupiter.api.Test + +class IsCloneableTest( + private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis, +) { + + @Test + fun `test - is cloneable`() { + val file = inlineSourceCodeAnalysis.createKtFile("val foo: Cloneable") + analyze(file) { + val foo = file.getPropertyOrFail("foo") + assertTrue(foo.type.isCloneable) + } + } + + @Test + fun `test - is iterator cloneable`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + class A + val foo: A + """.trimIndent() + ) + analyze(file) { + val foo = file.getPropertyOrFail("foo") + assertFalse(foo.type.isCloneable) + } + } +} + +context(KtAnalysisSession) +private val KtPropertySymbol.type: KtClassOrObjectSymbol + get() { + return getter?.returnType?.expandedClassSymbol!! + } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt index 3a97bff1aa2..2ce139f2708 100644 --- a/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt +++ b/native/objcexport-header-generator/impl/k1/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt @@ -197,7 +197,8 @@ abstract class ObjCExportHeaderGenerator @InternalKotlinNativeApi constructor( private fun generateInterface(descriptor: ClassDescriptor) { if (!generatedClasses.add(descriptor)) return - stubs.add(translator.translateInterface(descriptor)) + val stub = translator.translateInterface(descriptor) + stubs.add(stub) } internal fun requireClassOrInterface(descriptor: ClassDescriptor) { diff --git a/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt index e4f9e3b0e7b..49574216cd6 100644 --- a/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt +++ b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.types.Variance sealed class ObjCType { @@ -25,7 +26,10 @@ data class ObjCRawType( override fun render(attrsAndName: String): String = rawText.withAttrsAndName(attrsAndName) } -sealed class ObjCReferenceType : ObjCType() +sealed class ObjCReferenceType : ObjCType() { + @InternalKotlinNativeApi + open val classId: ClassId? = null +} sealed class ObjCNonNullReferenceType : ObjCReferenceType() @@ -33,6 +37,9 @@ data class ObjCNullableReferenceType( val nonNullType: ObjCNonNullReferenceType, val isNullableResult: Boolean = false, ) : ObjCReferenceType() { + + override val classId: ClassId? get() = nonNullType.classId + override fun render(attrsAndName: String): String { val attribute = if (isNullableResult) objcNullableResultAttribute else objcNullableAttribute return nonNullType.render(" $attribute".withAttrsAndName(attrsAndName)) @@ -42,8 +49,10 @@ data class ObjCNullableReferenceType( data class ObjCClassType( val className: String, val typeArguments: List = emptyList(), + override val classId: ClassId? = null, ) : ObjCNonNullReferenceType() { + override fun render(attrsAndName: String) = buildString { append(className) if (typeArguments.isNotEmpty()) { @@ -66,11 +75,12 @@ sealed class ObjCGenericTypeUsage : ObjCNonNullReferenceType() { data class ObjCGenericTypeRawUsage(override val typeName: String) : ObjCGenericTypeUsage() data class ObjCGenericTypeParameterUsage( - override val typeName: String + override val typeName: String, ) : ObjCGenericTypeUsage() data class ObjCProtocolType( val protocolName: String, + override val classId: ClassId? = null, ) : ObjCNonNullReferenceType() { override fun render(attrsAndName: String) = "id<$protocolName>".withAttrsAndName(attrsAndName) } @@ -87,7 +97,6 @@ data class ObjCBlockPointerType( val returnType: ObjCType, val parameterTypes: List, ) : ObjCNonNullReferenceType() { - override fun render(attrsAndName: String) = returnType.render(buildString { append("(^") append(attrsAndName) @@ -199,7 +208,7 @@ data class ObjCGenericTypeRawDeclaration( data class ObjCGenericTypeParameterDeclaration( override val typeName: String, - override val variance: ObjCVariance + override val variance: ObjCVariance, ) : ObjCGenericTypeDeclaration() @InternalKotlinNativeApi 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 5bed39f7169..65b3e39a3d0 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 @@ -10,4 +10,5 @@ import java.io.File val testDataDir = File("native/objcexport-header-generator/testData") val headersTestDataDir = testDataDir.resolve("headers") val baseDeclarationsDir = testDataDir.resolve("baseDeclarations") -val forwardDeclarationsDir = testDataDir.resolve("forwardDeclarations") \ No newline at end of file +val forwardDeclarationsDir = testDataDir.resolve("forwardDeclarations") +val dependenciesDir = testDataDir.resolve("dependencies") \ No newline at end of file diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt new file mode 100644 index 00000000000..bcc12530b55 --- /dev/null +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2024 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.testUtils.HeaderGenerator +import org.jetbrains.kotlin.backend.konan.testUtils.TodoAnalysisApi +import org.jetbrains.kotlin.backend.konan.testUtils.dependenciesDir +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.junit.jupiter.api.Test +import java.io.File +import kotlin.test.fail + +/** + * Currently analysis api doesn't fails until full support of stable order and other parts + * + * Test intended to verify how dependencies are translated, which included + * - stable orders + * - stubs orders + * - depth of traversing (some types must be skipped + */ +class ObjCDependenciesTypesTest( + private val generator: HeaderGenerator, +) { + + /** + * - Wrong translation of constructors KT-65365 + * - Bad parsing of versions (SinceKotlin Annotation) + * - Missing implementation of mangling + */ + @Test + @TodoAnalysisApi + fun `test - stringBuilder`() { + doTest(dependenciesDir.resolve("stringBuilder")) + } + + @Test + fun `test - iterator`() { + doTest(dependenciesDir.resolve("iterator")) + } + + /** + * - Wrong translation of constructors KT-65365 + * - Exposing unwanted 'clone' method KT-65629 + */ + @Test + @TodoAnalysisApi + fun `test - array`() { + doTest(dependenciesDir.resolve("array")) + } + + private fun doTest(root: File) { + if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory") + val generatedHeaders = generator.generateHeaders(root, HeaderGenerator.Configuration()).toString() + KotlinTestUtils.assertEqualsToFile(root.resolve("!${root.nameWithoutExtension}.h"), generatedHeaders) + } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportForwardDeclarationsTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportForwardDeclarationsTest.kt index 680528fb396..e8d4943d677 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportForwardDeclarationsTest.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportForwardDeclarationsTest.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.backend.konan.tests import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator -import org.jetbrains.kotlin.backend.konan.testUtils.TodoAnalysisApi import org.jetbrains.kotlin.backend.konan.testUtils.forwardDeclarationsDir import org.jetbrains.kotlin.test.KotlinTestUtils import org.junit.jupiter.api.Test @@ -18,25 +17,21 @@ class ObjCExportForwardDeclarationsTest( ) { @Test - @TodoAnalysisApi fun `test - function returning interface`() { doTest(forwardDeclarationsDir.resolve("functionReturningInterface")) } @Test - @TodoAnalysisApi fun `test - function returning class`() { doTest(forwardDeclarationsDir.resolve("functionReturningClass")) } @Test - @TodoAnalysisApi fun `test - property returning interface`() { doTest(forwardDeclarationsDir.resolve("propertyReturningInterface")) } @Test - @TodoAnalysisApi fun `test - property returning class`() { doTest(forwardDeclarationsDir.resolve("propertyReturningClass")) } diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt index 403722dfc8d..b0550065fb4 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt @@ -70,6 +70,9 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { doTest(headersTestDataDir.resolve("sameClassNameInDifferentPackage")) } + /** + * Naming of nested classes: KT-65633 + */ @Test @TodoAnalysisApi fun `test - nestedClass`() { @@ -83,7 +86,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { } @Test - @TodoAnalysisApi // Early translate of forward declarations fun `test - classImplementingInterface`() { doTest(headersTestDataDir.resolve("classImplementingInterface")) } @@ -178,7 +180,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { } @Test - @TodoAnalysisApi // Early translate of forward declarations fun `test - classWithUnresolvedSuperTypeGenerics`() { doTest(headersTestDataDir.resolve("classWithUnresolvedSuperTypeGenerics")) } @@ -198,6 +199,40 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { doTest(headersTestDataDir.resolve("manyClassesAndInterfaces")) } + @Test + fun `test - classReferencingOtherClassAsReturnType`() { + doTest(headersTestDataDir.resolve("classReferencingOtherClassAsReturnType")) + } + + /** + * - IntIterator has unwanted 'hasNext' exposed + * - IntIterator's next method returns int32_t instead of expected Int * + */ + @Test + @TodoAnalysisApi + fun `test - classReferencingDependencyClassAsReturnType`() { + doTest(headersTestDataDir.resolve("classReferencingDependencyClassAsReturnType")) + } + + @Test + fun `test - interfaceReferencingOtherInterfaceAsReturnType`() { + doTest(headersTestDataDir.resolve("interfaceReferencingOtherInterfaceAsReturnType")) + } + + @Test + fun `test - interfaceImplementingInterfaceOrder`() { + doTest(headersTestDataDir.resolve("interfaceImplementingInterfaceOrder")) + } + + /** + * Extension functions aren't supported KT-65630 + */ + @Test + @TodoAnalysisApi + fun `test - extensionFunctions`() { + doTest(headersTestDataDir.resolve("extensionFunctions")) + } + private fun doTest(root: File, configuration: Configuration = Configuration()) { if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory") val generatedHeaders = generator.generateHeaders(root, configuration).toString() diff --git a/native/objcexport-header-generator/testData/dependencies/array/!array.h b/native/objcexport-header-generator/testData/dependencies/array/!array.h new file mode 100644 index 00000000000..66f6ab68d21 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/array/!array.h @@ -0,0 +1,49 @@ +#import +#import +#import +#import +#import +#import +#import + +@class Array; + +@protocol Iterator; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +__attribute__((objc_subclassing_restricted)) +@interface FooKt : Base +@property (class, readonly) Array *a __attribute__((swift_name("a"))); +@end + +__attribute__((objc_subclassing_restricted)) +@interface Array : Base ++ (instancetype)arrayWithSize:(int32_t)size init:(T _Nullable (^)(Int *))init __attribute__((swift_name("init(size:init:)"))); ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); +- (T _Nullable)getIndex:(int32_t)index __attribute__((swift_name("get(index:)"))); +- (id)iterator __attribute__((swift_name("iterator()"))); +- (void)setIndex:(int32_t)index value:(T _Nullable)value __attribute__((swift_name("set(index:value:)"))); +@property (readonly) int32_t size __attribute__((swift_name("size"))); +@end + +@protocol Iterator +@required +- (BOOL)hasNext __attribute__((swift_name("hasNext()"))); +- (id _Nullable)next __attribute__((swift_name("next()"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/dependencies/array/Foo.kt b/native/objcexport-header-generator/testData/dependencies/array/Foo.kt new file mode 100644 index 00000000000..453f3a6a0de --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/array/Foo.kt @@ -0,0 +1 @@ +val a: Array \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/dependencies/iterator/!iterator.h b/native/objcexport-header-generator/testData/dependencies/iterator/!iterator.h new file mode 100644 index 00000000000..302893b6b45 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/iterator/!iterator.h @@ -0,0 +1,36 @@ +#import +#import +#import +#import +#import +#import +#import + +@protocol Iterator; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +__attribute__((objc_subclassing_restricted)) +@interface FooKt : Base +@property (class, readonly) id a __attribute__((swift_name("a"))); +@end + +@protocol Iterator +@required +- (BOOL)hasNext __attribute__((swift_name("hasNext()"))); +- (id _Nullable)next __attribute__((swift_name("next()"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/dependencies/iterator/Foo.kt b/native/objcexport-header-generator/testData/dependencies/iterator/Foo.kt new file mode 100644 index 00000000000..625904aab88 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/iterator/Foo.kt @@ -0,0 +1 @@ +val a: Iterator \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/dependencies/stringBuilder/!stringBuilder.h b/native/objcexport-header-generator/testData/dependencies/stringBuilder/!stringBuilder.h new file mode 100644 index 00000000000..596d918a3c3 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/stringBuilder/!stringBuilder.h @@ -0,0 +1,194 @@ +#import +#import +#import +#import +#import +#import +#import + +@class StringBuilder, CharArray, CharIterator; + +@protocol CharSequence, Appendable, Iterator; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +__attribute__((objc_subclassing_restricted)) +@interface FooKt : Base +@property (class, readonly) StringBuilder *a __attribute__((swift_name("a"))); +@end + +@protocol CharSequence +@required +- (unichar)getIndex:(int32_t)index __attribute__((swift_name("get(index:)"))); +- (id)subSequenceStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("subSequence(startIndex:endIndex:)"))); +@property (readonly) int32_t length __attribute__((swift_name("length"))); +@end + +@protocol Appendable +@required +- (id)appendValue:(unichar)value __attribute__((swift_name("append(value:)"))); +- (id)appendValue_:(id _Nullable)value __attribute__((swift_name("append(value_:)"))); +- (id)appendValue:(id _Nullable)value startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("append(value:startIndex:endIndex:)"))); +@end + +__attribute__((objc_subclassing_restricted)) +@interface StringBuilder : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (instancetype)initWithContent:(id)content __attribute__((swift_name("init(content:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)initWithCapacity:(int32_t)capacity __attribute__((swift_name("init(capacity:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)initWithContent_:(NSString *)content __attribute__((swift_name("init(content_:)"))) __attribute__((objc_designated_initializer)); +- (StringBuilder *)appendValue__:(id _Nullable)value __attribute__((swift_name("append(value__:)"))); +- (StringBuilder *)appendValue___:(BOOL)value __attribute__((swift_name("append(value___:)"))); +- (StringBuilder *)appendValue____:(int8_t)value __attribute__((swift_name("append(value____:)"))); +- (StringBuilder *)appendValue:(unichar)value __attribute__((swift_name("append(value:)"))); +- (StringBuilder *)appendValue_____:(CharArray *)value __attribute__((swift_name("append(value_____:)"))); +- (StringBuilder *)appendValue_:(id _Nullable)value __attribute__((swift_name("append(value_:)"))); +- (StringBuilder *)appendValue______:(double)value __attribute__((swift_name("append(value______:)"))); +- (StringBuilder *)appendValue_______:(float)value __attribute__((swift_name("append(value_______:)"))); +- (StringBuilder *)appendValue________:(int32_t)value __attribute__((swift_name("append(value________:)"))); +- (StringBuilder *)appendValue_________:(int64_t)value __attribute__((swift_name("append(value_________:)"))); +- (StringBuilder *)appendValue__________:(int16_t)value __attribute__((swift_name("append(value__________:)"))); +- (StringBuilder *)appendValue___________:(NSString * _Nullable)value __attribute__((swift_name("append(value___________:)"))); +- (StringBuilder *)appendValue:(id _Nullable)value startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("append(value:startIndex:endIndex:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (StringBuilder *)appendRangeValue:(CharArray *)value startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("appendRange(value:startIndex:endIndex:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (StringBuilder *)appendRangeValue:(id)value startIndex:(int32_t)startIndex endIndex_:(int32_t)endIndex __attribute__((swift_name("appendRange(value:startIndex:endIndex_:)"))); +- (int32_t)capacity __attribute__((swift_name("capacity()"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (StringBuilder *)deleteAtIndex:(int32_t)index __attribute__((swift_name("deleteAt(index:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (StringBuilder *)deleteRangeStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("deleteRange(startIndex:endIndex:)"))); +- (void)ensureCapacityMinimumCapacity:(int32_t)minimumCapacity __attribute__((swift_name("ensureCapacity(minimumCapacity:)"))); +- (unichar)getIndex:(int32_t)index __attribute__((swift_name("get(index:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (int32_t)indexOfString:(NSString *)string __attribute__((swift_name("indexOf(string:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (int32_t)indexOfString:(NSString *)string startIndex:(int32_t)startIndex __attribute__((swift_name("indexOf(string:startIndex:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value:(id _Nullable)value __attribute__((swift_name("insert(index:value:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value_:(BOOL)value __attribute__((swift_name("insert(index:value_:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value__:(int8_t)value __attribute__((swift_name("insert(index:value__:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value___:(unichar)value __attribute__((swift_name("insert(index:value___:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value____:(CharArray *)value __attribute__((swift_name("insert(index:value____:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value_____:(id _Nullable)value __attribute__((swift_name("insert(index:value_____:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value______:(double)value __attribute__((swift_name("insert(index:value______:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value_______:(float)value __attribute__((swift_name("insert(index:value_______:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value________:(int32_t)value __attribute__((swift_name("insert(index:value________:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value_________:(int64_t)value __attribute__((swift_name("insert(index:value_________:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value__________:(int16_t)value __attribute__((swift_name("insert(index:value__________:)"))); +- (StringBuilder *)insertIndex:(int32_t)index value___________:(NSString * _Nullable)value __attribute__((swift_name("insert(index:value___________:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (StringBuilder *)insertRangeIndex:(int32_t)index value:(CharArray *)value startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("insertRange(index:value:startIndex:endIndex:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (StringBuilder *)insertRangeIndex:(int32_t)index value:(id)value startIndex:(int32_t)startIndex endIndex_:(int32_t)endIndex __attribute__((swift_name("insertRange(index:value:startIndex:endIndex_:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (int32_t)lastIndexOfString:(NSString *)string __attribute__((swift_name("lastIndexOf(string:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (int32_t)lastIndexOfString:(NSString *)string startIndex:(int32_t)startIndex __attribute__((swift_name("lastIndexOf(string:startIndex:)"))); +- (StringBuilder *)reverse __attribute__((swift_name("reverse()"))); +- (void)setIndex:(int32_t)index value:(unichar)value __attribute__((swift_name("set(index:value:)"))); +- (void)setLengthNewLength:(int32_t)newLength __attribute__((swift_name("setLength(newLength:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (StringBuilder *)setRangeStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex value:(NSString *)value __attribute__((swift_name("setRange(startIndex:endIndex:value:)"))); +- (id)subSequenceStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("subSequence(startIndex:endIndex:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (NSString *)substringStartIndex:(int32_t)startIndex __attribute__((swift_name("substring(startIndex:)"))); +- (NSString *)substringStartIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("substring(startIndex:endIndex:)"))); + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ +- (void)toCharArrayDestination:(CharArray *)destination destinationOffset:(int32_t)destinationOffset startIndex:(int32_t)startIndex endIndex:(int32_t)endIndex __attribute__((swift_name("toCharArray(destination:destinationOffset:startIndex:endIndex:)"))); +- (NSString *)description __attribute__((swift_name("description()"))); +- (void)trimToSize __attribute__((swift_name("trimToSize()"))); +@property (readonly) int32_t length __attribute__((swift_name("length"))); +@end + +__attribute__((objc_subclassing_restricted)) +@interface CharArray : Base ++ (instancetype)arrayWithSize:(int32_t)size __attribute__((swift_name("init(size:)"))); ++ (instancetype)arrayWithSize:(int32_t)size init:(id _Nullable (^)(id _Nullable))init __attribute__((swift_name("init(size:init:)"))); ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); +- (unichar)getIndex:(int32_t)index __attribute__((swift_name("get(index:)"))); +- (CharIterator *)iterator __attribute__((swift_name("iterator()"))); +- (void)setIndex:(int32_t)index value:(unichar)value __attribute__((swift_name("set(index:value:)"))); +@property (readonly) int32_t size __attribute__((swift_name("size"))); +@end + +@protocol Iterator +@required +- (BOOL)hasNext __attribute__((swift_name("hasNext()"))); +- (id _Nullable)next __attribute__((swift_name("next()"))); +@end + +@interface CharIterator : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (id)next __attribute__((swift_name("next()"))); +- (unichar)nextChar __attribute__((swift_name("nextChar()"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/dependencies/stringBuilder/Foo.kt b/native/objcexport-header-generator/testData/dependencies/stringBuilder/Foo.kt new file mode 100644 index 00000000000..81908f43121 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/stringBuilder/Foo.kt @@ -0,0 +1 @@ +val a: StringBuilder \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/!classReferencingDependencyClassAsReturnType.h b/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/!classReferencingDependencyClassAsReturnType.h new file mode 100644 index 00000000000..bb82bec8de6 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/!classReferencingDependencyClassAsReturnType.h @@ -0,0 +1,47 @@ +#import +#import +#import +#import +#import +#import +#import + +@class IntIterator; + +@protocol Iterator; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +__attribute__((objc_subclassing_restricted)) +@interface Foo : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (IntIterator *)foo __attribute__((swift_name("foo()"))); +@end + +@protocol Iterator +@required +- (BOOL)hasNext __attribute__((swift_name("hasNext()"))); +- (id _Nullable)next __attribute__((swift_name("next()"))); +@end + +@interface IntIterator : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (Int *)next __attribute__((swift_name("next()"))); +- (int32_t)nextInt __attribute__((swift_name("nextInt()"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/Foo.kt b/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/Foo.kt new file mode 100644 index 00000000000..dff95a3055c --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classReferencingDependencyClassAsReturnType/Foo.kt @@ -0,0 +1,3 @@ +class Foo { + fun foo(): IntIterator = error("stub") +} \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/!classReferencingOtherClassAsReturnType.h b/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/!classReferencingOtherClassAsReturnType.h new file mode 100644 index 00000000000..3b1b8535250 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/!classReferencingOtherClassAsReturnType.h @@ -0,0 +1,38 @@ +#import +#import +#import +#import +#import +#import +#import + +@class B; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +__attribute__((objc_subclassing_restricted)) +@interface A : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (B *)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +@interface B : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/Foo.kt b/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/Foo.kt new file mode 100644 index 00000000000..7e1981387b8 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classReferencingOtherClassAsReturnType/Foo.kt @@ -0,0 +1,5 @@ +class A { + fun foo(): B = B() +} + +class B \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h b/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h new file mode 100644 index 00000000000..e7524ce886b --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h @@ -0,0 +1,36 @@ +#import +#import +#import +#import +#import +#import +#import + +@class Foo; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +__attribute__((objc_subclassing_restricted)) +@interface Foo : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (void)funA __attribute__((swift_name("funA()"))); +@end + +@interface Foo (Extensions) +- (void)funB __attribute__((swift_name("funB()"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt b/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt new file mode 100644 index 00000000000..bc3042b3a6d --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt @@ -0,0 +1,5 @@ +class Foo { + fun funA() {} +} + +fun Foo.funB() {} diff --git a/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/!interfaceImplementingInterfaceOrder.h b/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/!interfaceImplementingInterfaceOrder.h new file mode 100644 index 00000000000..3e24dea066e --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/!interfaceImplementingInterfaceOrder.h @@ -0,0 +1,33 @@ +#import +#import +#import +#import +#import +#import +#import + +@protocol B; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +@protocol B +@required +@end + +@protocol A +@required +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/Foo.kt b/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/Foo.kt new file mode 100644 index 00000000000..e1cf18b4689 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/interfaceImplementingInterfaceOrder/Foo.kt @@ -0,0 +1,3 @@ +interface A : B + +interface B \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/!interfaceReferencingOtherInterfaceAsReturnType.h b/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/!interfaceReferencingOtherInterfaceAsReturnType.h new file mode 100644 index 00000000000..b0df59f4791 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/!interfaceReferencingOtherInterfaceAsReturnType.h @@ -0,0 +1,34 @@ +#import +#import +#import +#import +#import +#import +#import + +@protocol B; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +@protocol A +@required +- (id)foo __attribute__((swift_name("foo()"))); +@end + +@protocol B +@required +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/Foo.kt b/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/Foo.kt new file mode 100644 index 00000000000..a0832a1bc29 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/interfaceReferencingOtherInterfaceAsReturnType/Foo.kt @@ -0,0 +1,5 @@ +interface A { + fun foo(): B +} + +interface B \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/nestedClass/!nestedClass.h b/native/objcexport-header-generator/testData/headers/nestedClass/!nestedClass.h index 5f14e624838..54bc5a3dbed 100644 --- a/native/objcexport-header-generator/testData/headers/nestedClass/!nestedClass.h +++ b/native/objcexport-header-generator/testData/headers/nestedClass/!nestedClass.h @@ -25,8 +25,29 @@ __attribute__((objc_subclassing_restricted)) @end __attribute__((objc_subclassing_restricted)) -__attribute__((swift_name("A.B"))) -@interface AB : Base +__attribute__((swift_name("A.A1"))) +@interface AA1 : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("A.A1B1"))) +@interface AA1B1 : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("A.A2"))) +@interface AA2 : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("A.A2B2"))) +@interface AA2B2 : Base - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); @end diff --git a/native/objcexport-header-generator/testData/headers/nestedClass/Foo.kt b/native/objcexport-header-generator/testData/headers/nestedClass/Foo.kt index 4e610ee6db7..032a4f2d262 100644 --- a/native/objcexport-header-generator/testData/headers/nestedClass/Foo.kt +++ b/native/objcexport-header-generator/testData/headers/nestedClass/Foo.kt @@ -1,3 +1,8 @@ class A { - class B + class A1 { + class B1 + } + class A2 { + class B2 + } } \ No newline at end of file