[ObjCExport] Fix data class predefined methods translation
KT-65800
This commit is contained in:
committed by
Space Team
parent
3e5afcaaba
commit
83ddf904aa
+6
@@ -4,9 +4,15 @@ 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.symbols.KtEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
/**
|
||||
* Note: At the time of writing this function (and comment) we found it easiest
|
||||
* to construct the functions manually. Potentially, there is a way to get those functions from
|
||||
* the Analysis API by requesting the combined member scope and looking for [KtSymbolOrigin.SOURCE_MEMBER_GENERATED].
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtClassOrObjectSymbol.translateEnumMembers(): List<ObjCExportStub> {
|
||||
if (classKind != KtClassKind.ENUM_CLASS) return emptyList()
|
||||
|
||||
+46
-5
@@ -1,10 +1,8 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
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.symbols.*
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality
|
||||
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
|
||||
@@ -41,7 +39,8 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
this += cloneMethod
|
||||
}
|
||||
|
||||
this += getDeclaredMemberScope().getCallableSymbols().sortedWith(StableCallableOrder)
|
||||
this += getCallableSymbolsForObjCMemberTranslation()
|
||||
.sortedWith(StableCallableOrder)
|
||||
.mapNotNull { it.translateToObjCExportStub() }
|
||||
|
||||
if (classKind == KtClassKind.ENUM_CLASS) {
|
||||
@@ -72,6 +71,48 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all [KtCallableSymbol] symbols that are to be translated to ObjC for [this] [KtClassOrObjectSymbol].
|
||||
* Note: This will return only 'declared' members (aka members written on this class/interface/object) and 'synthetic'/'generated' members.
|
||||
*
|
||||
* ## Example regular class
|
||||
* ```kotlin
|
||||
* open class Base {
|
||||
* fun base() = Unit
|
||||
* }
|
||||
*
|
||||
* class Foo : Base() {
|
||||
* fun foo() = Unit
|
||||
* ```
|
||||
*
|
||||
* In this example `Foo` will return the function `foo` (as declared in `Foo`), but not the function `base` (as declared in `Base` and
|
||||
* not directly in `Foo`).
|
||||
*
|
||||
* ## Example data class
|
||||
* ```kotlin
|
||||
* data class Foo(val x: Int)
|
||||
* ```
|
||||
*
|
||||
* Will return `x` as directly declared in `Foo`, but also the `copy`, `equals`,`hashCode`, `toString` and `componentX` functions that
|
||||
* are generated by the compiler for the *data* class `Foo`
|
||||
*
|
||||
* Note: Some methods like `hashCode`, `toString`, ... have predefined selectors and ObjC names.
|
||||
* @see [Predefined]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
internal fun KtClassOrObjectSymbol.getCallableSymbolsForObjCMemberTranslation(): Set<KtCallableSymbol> {
|
||||
val generatedCallableSymbols = getMemberScope()
|
||||
.getCallableSymbols()
|
||||
.filter { it.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED }
|
||||
.toSet()
|
||||
|
||||
val declaredCallableSymbols = getDeclaredMemberScope()
|
||||
.getCallableSymbols()
|
||||
.toSet()
|
||||
|
||||
return generatedCallableSymbols + declaredCallableSymbols
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtNonErrorClassType.getSuperClassName(): ObjCExportClassOrProtocolName? {
|
||||
val classSymbol = expandedClassSymbol ?: return null
|
||||
@@ -86,4 +127,4 @@ private val cloneMethod = ObjCMethod(
|
||||
parameters = emptyList(),
|
||||
isInstanceMethod = true,
|
||||
attributes = listOf(swiftNameAttribute("clone()"))
|
||||
)
|
||||
)
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.analysis.api.symbols.KtFunctionSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
|
||||
import org.jetbrains.kotlin.objcexport.StableCallableOrder
|
||||
import org.jetbrains.kotlin.objcexport.getCallableSymbolsForObjCMemberTranslation
|
||||
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 GetCallableSymbolsForObjCMemberTranslationTest(
|
||||
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
|
||||
) {
|
||||
@Test
|
||||
fun `test - regular class`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
abstract class Base {
|
||||
open fun base() = Unit
|
||||
abstract fun abstractFun()
|
||||
}
|
||||
class Foo: Base() {
|
||||
fun bar() {}
|
||||
override fun abstractFun(): Unit = error("stub")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
analyze(file) {
|
||||
val fooSymbol = file.getClassOrFail("Foo")
|
||||
assertEquals(
|
||||
listOf("bar", "abstractFun"),
|
||||
fooSymbol.getCallableSymbolsForObjCMemberTranslation()
|
||||
.map { it as KtFunctionSymbol }
|
||||
.map { it.name.asString() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - data class`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
data class Foo(val a: Int)
|
||||
""".trimIndent()
|
||||
)
|
||||
analyze(file) {
|
||||
val foo = file.getClassOrFail("Foo")
|
||||
assertEquals(
|
||||
listOf("component1", "copy", "equals", "hashCode", "toString", "a"),
|
||||
foo.getCallableSymbolsForObjCMemberTranslation()
|
||||
.sortedWith(StableCallableOrder)
|
||||
.map { it as KtNamedSymbol }
|
||||
.map { it.name.asString() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - enum class`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
enum class Foo {
|
||||
A, B, C
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
analyze(file) {
|
||||
val foo = file.getClassOrFail("Foo")
|
||||
assertEquals(
|
||||
emptyList(),
|
||||
foo.getCallableSymbolsForObjCMemberTranslation()
|
||||
.sortedWith(StableCallableOrder)
|
||||
.map { it as KtNamedSymbol }
|
||||
.map { it.name.asString() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.getFunctionOrFail
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@@ -56,12 +57,12 @@ class IsHashCodeTest(
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - Any equals`() {
|
||||
fun `test - Any equals method is returning false for isHashCode`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile("")
|
||||
analyze(file) {
|
||||
val anySymbol = getClassOrObjectSymbolByClassId(StandardClassIds.Any) ?: error("Missing kotlin.Any")
|
||||
val hashCodeSymbol = anySymbol.getFunctionOrFail("equals")
|
||||
assertFalse(hashCodeSymbol.isHashCode)
|
||||
val equalsSymbol = anySymbol.getFunctionOrFail("equals")
|
||||
assertFalse(equalsSymbol.isHashCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
-4
@@ -297,11 +297,7 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
|
||||
doTest(headersTestDataDir.resolve("samInterface"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires some fixes: KT-65800
|
||||
*/
|
||||
@Test
|
||||
@TodoAnalysisApi
|
||||
fun `test - simple data class`() {
|
||||
doTest(headersTestDataDir.resolve("simpleDataClass"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user