diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getMethodBridge.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getMethodBridge.kt index 005be425d02..d5a96c580a7 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getMethodBridge.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/getMethodBridge.kt @@ -47,7 +47,7 @@ context(KtAnalysisSession) private val KtCallableSymbol.receiverType: MethodBridgeReceiver get() = if (isArrayConstructor) { MethodBridgeReceiver.Factory - } else if (isTopLevel) { + } else if (!isConstructor && isTopLevel) { MethodBridgeReceiver.Static } else { MethodBridgeReceiver.Instance diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/hasExportForCompilerAnnotation.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/hasExportForCompilerAnnotation.kt new file mode 100644 index 00000000000..7735d96173d --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/hasExportForCompilerAnnotation.kt @@ -0,0 +1,16 @@ +package org.jetbrains.kotlin.objcexport.analysisApiUtils + +import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotated +import org.jetbrains.kotlin.analysis.api.annotations.hasAnnotation +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName + +private val ExportForCompilerAnnotationClassId = ClassId.topLevel(FqName("kotlin.native.internal.ExportForCompiler")) + +/** + * [kotlin.native.internal.ExportForCompiler] is an internal compiler annotation. Annotated symbols must not be exposed. + */ +internal val KtAnnotated.hasExportForCompilerAnnotation: Boolean + get() { + return this.hasAnnotation(ExportForCompilerAnnotationClassId) + } \ 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 index a3182bd3311..98d1c79a671 100644 --- 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 @@ -1,8 +1,28 @@ 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.KtDeclarationSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds + +context(KtAnalysisSession) +internal val KtDeclarationSymbol?.implementsCloneable: Boolean + get() { + return (this as? KtClassOrObjectSymbol)?.implementsCloneable ?: false + } + +context(KtAnalysisSession) +internal val KtClassOrObjectSymbol.implementsCloneable: Boolean + get() { + return superTypes.any { + it.expandedClassSymbol?.isCloneable ?: false + } + } internal val KtClassOrObjectSymbol.isCloneable: Boolean get() { @@ -12,4 +32,13 @@ internal val KtClassOrObjectSymbol.isCloneable: Boolean internal val ClassId.isCloneable: Boolean get() { return asSingleFqName() == StandardNames.FqNames.cloneable.toSafe() + } + +context(KtAnalysisSession) +internal val KtFunctionSymbol.isClone: Boolean + get() { + val cloneCallableId = CallableId(StandardClassIds.Cloneable, Name.identifier("clone")) + if (this.callableIdIfNonLocal == cloneCallableId) return true + + return this.getAllOverriddenSymbols().any { it.callableIdIfNonLocal == cloneCallableId } } \ 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 893a8834a59..abe820f97ae 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 @@ -8,11 +8,12 @@ import org.jetbrains.kotlin.analysis.api.symbols.nameOrAnonymous import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasExportForCompilerAnnotation import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC context(KtAnalysisSession, KtObjCExportSession) fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? { - require(classKind == KtClassKind.CLASS) + require(classKind == KtClassKind.CLASS || classKind == KtClassKind.ENUM_CLASS) if (!isVisibleInObjC()) return null val enumKind = this.classKind == KtClassKind.ENUM_CLASS @@ -26,8 +27,9 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? { val superClass = translateSuperClass() val superProtocols: List = superProtocols() + val constructors = getMemberScope().getConstructors().filter { !it.hasExportForCompilerAnnotation } - val members: List = getMemberScope().getCallableSymbols().plus(getMemberScope().getConstructors()) + val members: List = getMemberScope().getCallableSymbols().plus(constructors) .sortedWith(StableCallableOrder) .flatMap { it.translateToObjCExportStubs() } .toList() diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCComment.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCComment.kt index b2d785b2eea..3c54ea31c36 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCComment.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCComment.kt @@ -6,9 +6,7 @@ package org.jetbrains.kotlin.objcexport import org.jetbrains.kotlin.analysis.api.KtAnalysisSession -import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo -import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationsList -import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue +import org.jetbrains.kotlin.analysis.api.annotations.* import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol @@ -97,12 +95,16 @@ private fun renderAnnotation(clazz: ClassId, arguments: List arg.render() } append(')') } } } +private fun KtNamedAnnotationValue.render(): String { + return "$name=${expression.renderAsSourceCode()}" +} + /** * Not implemented [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.getEffectiveThrows] */ diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCConstructor.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCConstructor.kt index 7bfec9a618e..787a8156155 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCConstructor.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCConstructor.kt @@ -4,6 +4,9 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.symbols.KtConstructorSymbol import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInstanceType import org.jetbrains.kotlin.backend.konan.objcexport.ObjCMethod +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCParameter +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCRawType +import org.jetbrains.kotlin.objcexport.analysisApiUtils.isArrayConstructor import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC context(KtAnalysisSession, KtObjCExportSession) @@ -13,19 +16,48 @@ fun KtConstructorSymbol.translateToObjCConstructors(): List { result.add(buildObjCMethod()) - result.add( - ObjCMethod( - comment = null, - origin = getObjCExportStubOrigin(), - isInstanceMethod = false, - returnType = ObjCInstanceType, - selectors = listOf("new"), - parameters = emptyList(), - attributes = listOf( - "availability(swift, unavailable, message=\"use object initializers instead\")" + if (isArrayConstructor) { + + result.add( + ObjCMethod( + comment = null, + origin = getObjCExportStubOrigin(), + isInstanceMethod = false, + returnType = ObjCInstanceType, + selectors = listOf("alloc"), + parameters = emptyList(), + attributes = listOf("unavailable") ) ) - ) + + result.add( + ObjCMethod( + comment = null, + origin = getObjCExportStubOrigin(), + isInstanceMethod = false, + returnType = ObjCInstanceType, + selectors = listOf("allocWithZone:"), + parameters = listOf(ObjCParameter("zone", null, ObjCRawType("struct _NSZone *"), null)), + attributes = listOf("unavailable") + ) + ) + } + + if (result.size == 1 && result.first().name == "init") { + result.add( + ObjCMethod( + comment = null, + origin = getObjCExportStubOrigin(), + isInstanceMethod = false, + returnType = ObjCInstanceType, + selectors = listOf("new"), + parameters = emptyList(), + attributes = listOf( + "availability(swift, unavailable, message=\"use object initializers instead\")" + ) + ) + ) + } return result } \ 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 73158fdfb20..4bd3c963e68 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 @@ -51,6 +51,7 @@ fun translateToObjCHeader(files: List): ObjCHeader { KtClassKind.INTERFACE -> symbol.translateToObjCProtocol() KtClassKind.CLASS -> symbol.translateToObjCClass() KtClassKind.OBJECT -> symbol.translateToObjCObject() + KtClassKind.ENUM_CLASS -> symbol.translateToObjCClass() else -> return result } ?: return result diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCMethod.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCMethod.kt index d6c85ae06f5..839c500ac3c 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCMethod.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCMethod.kt @@ -12,9 +12,7 @@ import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.NameUtils import org.jetbrains.kotlin.objcexport.Predefined.anyMethodSelectors -import org.jetbrains.kotlin.objcexport.analysisApiUtils.getFunctionMethodBridge -import org.jetbrains.kotlin.objcexport.analysisApiUtils.isArrayConstructor -import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC +import org.jetbrains.kotlin.objcexport.analysisApiUtils.* import org.jetbrains.kotlin.psi.KtFile internal val KtCallableSymbol.isConstructor: Boolean @@ -25,6 +23,7 @@ fun KtFunctionSymbol.translateToObjCMethod( ): ObjCMethod? { if (!isVisibleInObjC()) return null if (anyMethodSelectors.containsKey(this.name)) return null //temp, find replacement for org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.isReal + if (isClone) return null return buildObjCMethod() } @@ -78,7 +77,7 @@ internal fun KtFunctionLikeSymbol.buildObjCMethod( return ObjCMethod( comment = comment, origin = getObjCExportStubOrigin(), - isInstanceMethod = bridge.isInstance || isConstructor, + isInstanceMethod = bridge.isInstance, returnType = returnType, selectors = selectors, parameters = parameters, @@ -280,7 +279,7 @@ private fun String.mangleIfSpecialFamily(prefix: String): String { * [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.startsWithWords] */ private fun String.startsWithWords(words: String) = this.startsWith(words) && - (this.length == words.length || !this[words.length].isLowerCase()) + (this.length == words.length || !this[words.length].isLowerCase()) /** * [org.jetbrains.kotlin.backend.konan.objcexport.MethodBrideExtensionsKt.valueParametersAssociated] @@ -322,7 +321,7 @@ fun KtFunctionLikeSymbol.mapReturnType(returnBridge: MethodBridge.ReturnValue): if (!returnBridge.successMayBeZero) { check( successReturnType is ObjCNonNullReferenceType - || (successReturnType is ObjCPointerType && !successReturnType.nullable) + || (successReturnType is ObjCPointerType && !successReturnType.nullable) ) { "Unexpected return type: $successReturnType in $this" } diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCProperty.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCProperty.kt index afaca55c7f6..cea908e404c 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCProperty.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCProperty.kt @@ -7,7 +7,9 @@ package org.jetbrains.kotlin.objcexport import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCIdType import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProperty +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCType import org.jetbrains.kotlin.backend.konan.objcexport.swiftNameAttribute import org.jetbrains.kotlin.objcexport.analysisApiUtils.getPropertyMethodBridge import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC @@ -57,7 +59,7 @@ fun KtPropertySymbol.buildProperty(): ObjCProperty { name = name, comment = null, origin = getObjCExportStubOrigin(), - type = type!!, + type = type ?: ObjCIdType, //[ObjCIdType] temp fix, should be translated properly, see KT-65709 propertyAttributes = attributes, setterName = setterName, getterName = getterName, diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiAssertions.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiAssertions.kt index d0ddedeedf3..de46f9f7aab 100644 --- a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiAssertions.kt +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiAssertions.kt @@ -61,6 +61,6 @@ fun KtScope.getPropertyOrFail(name: String): KtPropertySymbol { } context(KtAnalysisSession) -fun KtClassOrObjectSymbol.getFunctionOrFail(name: String) : KtFunctionSymbol { +fun KtClassOrObjectSymbol.getFunctionOrFail(name: String): KtFunctionSymbol { return this.getMemberScope().getFunctionOrFail(name) -} +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/HasExportForCompilerAnnotationTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/HasExportForCompilerAnnotationTest.kt new file mode 100644 index 00000000000..e553e41339b --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/HasExportForCompilerAnnotationTest.kt @@ -0,0 +1,48 @@ +package org.jetbrains.kotlin.objcexport.tests + +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol +import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasExportForCompilerAnnotation +import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis +import org.jetbrains.kotlin.objcexport.testUtils.getPropertyOrFail +import org.junit.jupiter.api.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import kotlin.test.fail + +class HasExportForCompilerAnnotationTest( + private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis, +) { + @Test + fun `test - has ExportForCompiler annotation`() { + val ktFile = inlineSourceCodeAnalysis.createKtFile( + """ + class Foo + val array: Array + val iterator: Iterator + val foo: Foo + """.trimIndent() + ) + + analyze(ktFile) { + assertTrue( + verifyHasExportForCompilerAnnotation(ktFile.getPropertyOrFail("array")) + ) + assertFalse( + verifyHasExportForCompilerAnnotation(ktFile.getPropertyOrFail("iterator")) + ) + assertFalse( + verifyHasExportForCompilerAnnotation(ktFile.getPropertyOrFail("foo")) + ) + } + } +} + +context(KtAnalysisSession) +private fun verifyHasExportForCompilerAnnotation(property: KtPropertySymbol): Boolean { + return property + .returnType + .getTypeScope()?.getConstructors()?.toList()?.any { it.hasExportForCompilerAnnotation } + ?: fail("Property return type has no constructors: ${property.returnType}") +} \ 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 index 3fb5c5de9b6..5c0672986df 100644 --- 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 @@ -1,15 +1,16 @@ 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.implementsCloneable +import org.jetbrains.kotlin.objcexport.analysisApiUtils.isClone import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCloneable import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis +import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail +import org.jetbrains.kotlin.objcexport.testUtils.getFunctionOrFail import org.jetbrains.kotlin.objcexport.testUtils.getPropertyOrFail import org.junit.jupiter.api.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue class IsCloneableTest( private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis, @@ -20,7 +21,7 @@ class IsCloneableTest( val file = inlineSourceCodeAnalysis.createKtFile("val foo: Cloneable") analyze(file) { val foo = file.getPropertyOrFail("foo") - assertTrue(foo.type.isCloneable) + assertTrue(foo.getter?.returnType?.expandedClassSymbol?.isCloneable ?: true) } } @@ -34,13 +35,76 @@ class IsCloneableTest( ) analyze(file) { val foo = file.getPropertyOrFail("foo") - assertFalse(foo.type.isCloneable) + assertFalse(foo.getter?.returnType?.expandedClassSymbol?.isCloneable ?: true) } } -} -context(KtAnalysisSession) -private val KtPropertySymbol.type: KtClassOrObjectSymbol - get() { - return getter?.returnType?.expandedClassSymbol!! - } \ No newline at end of file + @Test + fun `test - fake clone method`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + interface FakeCloneable { + fun clone(): Any + } + class Foo : FakeCloneable { + override fun clone(): Any { + return "any" + } + } + """.trimIndent() + ) + analyze(file) { + val foo = file.getClassOrFail("Foo") + assertFalse(foo.getFunctionOrFail("clone").isClone) + } + } + + @Test + fun `test - implements cloneable`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + class Foo : Cloneable { + override fun clone(): Foo { + return this + } + } + """.trimIndent() + ) + analyze(file) { + val foo = file.getClassOrFail("Foo") + assertTrue(foo.implementsCloneable) + assertTrue(foo.getFunctionOrFail("clone").isClone) + } + } + + @Test + fun `test - fake clone function`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + fun clone(): Any {} + """.trimIndent() + ) + analyze(file) { + val clone = file.getFunctionOrFail("clone") + assertFalse(clone.isClone) + } + } + + @Test + fun `test - clone method`() { + val file = inlineSourceCodeAnalysis.createKtFile( + """ + val array: Array + """.trimIndent() + ) + analyze(file) { + assertTrue( + file + .getPropertyOrFail("array") + .getter?.returnType?.expandedClassSymbol?.getMemberScope() + ?.getFunctionOrFail("clone") + ?.isClone ?: false + ) + } + } +} \ 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 index bcc12530b55..8b8831b7a0a 100644 --- 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 @@ -26,8 +26,6 @@ class ObjCDependenciesTypesTest( ) { /** - * - Wrong translation of constructors KT-65365 - * - Bad parsing of versions (SinceKotlin Annotation) * - Missing implementation of mangling */ @Test @@ -41,14 +39,27 @@ class ObjCDependenciesTypesTest( doTest(dependenciesDir.resolve("iterator")) } + @Test + fun `test - array`() { + doTest(dependenciesDir.resolve("array")) + } + /** - * - Wrong translation of constructors KT-65365 - * - Exposing unwanted 'clone' method KT-65629 + * Fails because of KT-65709 */ @Test @TodoAnalysisApi - fun `test - array`() { - doTest(dependenciesDir.resolve("array")) + fun `test - arrayList`() { + doTest(dependenciesDir.resolve("arrayList")) + } + + /** + * Int* type conversion issue: KT-65687 + */ + @Test + @TodoAnalysisApi + fun `test - implementIterator`() { + doTest(dependenciesDir.resolve("implementIterator")) } private fun doTest(root: File) { 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 2e817a68a6d..9d98095d67e 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 @@ -101,6 +101,11 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { doTest(headersTestDataDir.resolve("classImplementingInterface")) } + @Test + fun `test - classExtendsAbstractClass`() { + doTest(headersTestDataDir.resolve("classExtendsAbstractClass")) + } + @Test @TodoAnalysisApi fun `test - interfaceImplementingInterface`() { @@ -258,6 +263,20 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { doTest(headersTestDataDir.resolve("objectWithGenericSuperclass")) } + @Test + fun `test - since version annotation`() { + doTest(headersTestDataDir.resolve("sinceVersionAnnotation")) + } + + /** + * - requires mangling + */ + @TodoAnalysisApi + @Test + fun `test - constructors`() { + doTest(headersTestDataDir.resolve("constructors")) + } + 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/arrayList/!arrayList.h b/native/objcexport-header-generator/testData/dependencies/arrayList/!arrayList.h new file mode 100644 index 00000000000..a716c4fe12b --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/arrayList/!arrayList.h @@ -0,0 +1,28 @@ +#import +#import +#import +#import +#import +#import +#import + +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) NSMutableArray *a __attribute__((swift_name("a"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/dependencies/arrayList/Foo.kt b/native/objcexport-header-generator/testData/dependencies/arrayList/Foo.kt new file mode 100644 index 00000000000..958e3c88932 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/arrayList/Foo.kt @@ -0,0 +1 @@ +val a: ArrayList \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/dependencies/implementIterator/!implementIterator.h b/native/objcexport-header-generator/testData/dependencies/implementIterator/!implementIterator.h new file mode 100644 index 00000000000..f3d448703b6 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/implementIterator/!implementIterator.h @@ -0,0 +1,39 @@ +#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 + +@protocol Iterator +@required +- (BOOL)hasNext __attribute__((swift_name("hasNext()"))); +- (id _Nullable)next __attribute__((swift_name("next()"))); +@end + +__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"))); +- (BOOL)hasNext __attribute__((swift_name("hasNext()"))); +- (Int *)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/implementIterator/Foo.kt b/native/objcexport-header-generator/testData/dependencies/implementIterator/Foo.kt new file mode 100644 index 00000000000..e077a44813c --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/implementIterator/Foo.kt @@ -0,0 +1,4 @@ +class Foo: Iterator { + override fun hasNext(): Boolean { return false } + override fun next(): Int { return 0 } +} \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/classExtendsAbstractClass/!classExtendsAbstractClass.h b/native/objcexport-header-generator/testData/headers/classExtendsAbstractClass/!classExtendsAbstractClass.h new file mode 100644 index 00000000000..e09df8708ee --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classExtendsAbstractClass/!classExtendsAbstractClass.h @@ -0,0 +1,43 @@ +#import +#import +#import +#import +#import +#import +#import + +@class A; + +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 + +@interface A : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (void)a0 __attribute__((swift_name("a0()"))); +- (void)a1I:(int32_t)i __attribute__((swift_name("a1(i:)"))); +- (void)a2B:(BOOL)b a:(id)a __attribute__((swift_name("a2(b:a:)"))); +@end + +__attribute__((objc_subclassing_restricted)) +@interface B : A +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (void)a0 __attribute__((swift_name("a0()"))); +- (void)a1I:(int32_t)i __attribute__((swift_name("a1(i:)"))); +- (void)a2B:(BOOL)b a:(id)a __attribute__((swift_name("a2(b:a:)"))); +- (void)b0 __attribute__((swift_name("b0()"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/classExtendsAbstractClass/Foo.kt b/native/objcexport-header-generator/testData/headers/classExtendsAbstractClass/Foo.kt new file mode 100644 index 00000000000..8c2ba2f85f3 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classExtendsAbstractClass/Foo.kt @@ -0,0 +1,13 @@ +abstract class A { + abstract fun a0() + abstract fun a1(i: Int) + abstract fun a2(b: Boolean, a: Any) +} + +class B: A() { + override fun a0() {} + override fun a1(i: Int) {} + override fun a2(b: Boolean, a: Any) {} + + fun b0() {} +} \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/constructors/!constructors.h b/native/objcexport-header-generator/testData/headers/constructors/!constructors.h new file mode 100644 index 00000000000..66e2f50bb55 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/constructors/!constructors.h @@ -0,0 +1,54 @@ +#import +#import +#import +#import +#import +#import +#import + +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 Constructor : Base +@end + +__attribute__((objc_subclassing_restricted)) +@interface ConstructorDouble : Base +- (instancetype)initWithA:(int32_t)a __attribute__((swift_name("init(a:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)initWithA_:(int32_t)a __attribute__((swift_name("init(a_:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)initWithA:(int32_t)a b:(int32_t)b __attribute__((swift_name("init(a:b:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)initWithA:(int32_t)a b:(int32_t)b c:(int32_t)c __attribute__((swift_name("init(a:b:c:)"))) __attribute__((objc_designated_initializer)); +@end + +__attribute__((objc_subclassing_restricted)) +@interface ConstructorParam0 : 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)) +@interface ConstructorParam1 : Base +- (instancetype)initWithA:(int32_t)a __attribute__((swift_name("init(a:)"))) __attribute__((objc_designated_initializer)); +@property (readonly) int32_t a __attribute__((swift_name("a"))); +@end + +__attribute__((objc_subclassing_restricted)) +@interface ConstructorParam2 : Base +- (instancetype)initWithA:(int32_t)a b:(int32_t)b __attribute__((swift_name("init(a:b:)"))) __attribute__((objc_designated_initializer)); +@property (readonly) int32_t a __attribute__((swift_name("a"))); +@property (readonly) int32_t b __attribute__((swift_name("b"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/constructors/Foo.kt b/native/objcexport-header-generator/testData/headers/constructors/Foo.kt new file mode 100644 index 00000000000..a87b5864218 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/constructors/Foo.kt @@ -0,0 +1,10 @@ +class Constructor private constructor() {} +class ConstructorParam0() {} +class ConstructorParam1(val a: Int) {} +class ConstructorParam2(val a: Int, val b: Int) {} + +class ConstructorDouble(a: Int) { + constructor(a: Int): this(a) + constructor(a: Int, b: Int): this(b) + constructor(a: Int, b: Int, c: Int): this(c) +} \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/sinceVersionAnnotation/!sinceVersionAnnotation.h b/native/objcexport-header-generator/testData/headers/sinceVersionAnnotation/!sinceVersionAnnotation.h new file mode 100644 index 00000000000..c9d92492e21 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/sinceVersionAnnotation/!sinceVersionAnnotation.h @@ -0,0 +1,33 @@ +#import +#import +#import +#import +#import +#import +#import + +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 + +/** + * @note annotations + * kotlin.SinceKotlin(version="1.4") +*/ ++ (void)foo __attribute__((swift_name("foo()"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/sinceVersionAnnotation/Foo.kt b/native/objcexport-header-generator/testData/headers/sinceVersionAnnotation/Foo.kt new file mode 100644 index 00000000000..24a1677a388 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/sinceVersionAnnotation/Foo.kt @@ -0,0 +1,2 @@ +@SinceKotlin(version="1.4") +fun foo() {} \ No newline at end of file