[ObjCType] Add basic implementation of base method, fix tests
KT-65687
This commit is contained in:
committed by
Space Team
parent
1c2570ed7e
commit
078f0ff6e1
+2
@@ -36,6 +36,7 @@ internal fun KtCallableSymbol.isVisibleInObjC(): Boolean {
|
||||
if (this.isHiddenFromObjCByAnnotation()) return false
|
||||
if (this.isSealedClassConstructor()) return false
|
||||
if (this.isComponentNMethod() && this.getDirectlyOverriddenSymbols().isEmpty()) return false
|
||||
getContainingSymbol()?.let { if (!it.isVisibleInObjC()) return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -50,6 +51,7 @@ internal fun KtClassOrObjectSymbol.isVisibleInObjC(): Boolean {
|
||||
if (!this.classKind.isVisibleInObjC()) return false
|
||||
if (this.isExpect) return false
|
||||
if (this.isInlined()) return false
|
||||
getContainingSymbol()?.let { if (!it.isVisibleInObjC()) return false }
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||
|
||||
/**
|
||||
* Very basic implementation
|
||||
*
|
||||
* It covers edge case: if something isn't visible in inheritance chain it should be skipped
|
||||
* See [org.jetbrains.kotlin.objcexport.tests.IsContainingSymbolVisible]
|
||||
* See also K1:
|
||||
* - [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.getBaseMethods]
|
||||
* - [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.isBaseMethod]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
internal val KtFunctionSymbol.baseMethod: KtFunctionSymbol
|
||||
get() {
|
||||
val overriddenSymbols = getAllOverriddenSymbols().filter { symbol -> symbol.isVisibleInObjC() }
|
||||
return if (overriddenSymbols.isEmpty()) this
|
||||
else overriddenSymbols.last() as KtFunctionSymbol
|
||||
}
|
||||
+3
-1
@@ -143,7 +143,9 @@ private class KtObjCExportHeaderGenerator {
|
||||
2) Super interface / superclass symbol export stubs (result of translation) have to be present in the stubs list before the
|
||||
original stub
|
||||
*/
|
||||
symbol.getDeclaredSuperInterfaceSymbols().forEach { superInterfaceSymbol ->
|
||||
symbol.getDeclaredSuperInterfaceSymbols()
|
||||
.filter { it.isVisibleInObjC() }
|
||||
.forEach { superInterfaceSymbol ->
|
||||
translateClassOrObjectSymbol(superInterfaceSymbol)?.let {
|
||||
objCProtocolForwardDeclarations += it.name
|
||||
}
|
||||
|
||||
+12
-3
@@ -32,7 +32,17 @@ context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtFunctionLikeSymbol.buildObjCMethod(
|
||||
unavailable: Boolean = false,
|
||||
): ObjCMethod {
|
||||
val bridge = getFunctionMethodBridge()
|
||||
|
||||
val bridge = if (this is KtFunctionSymbol) {
|
||||
/**
|
||||
* Unlike constructor, a function can have base return type.
|
||||
* So in case of function we need to call [getFunctionMethodBridge] on [baseMethod]
|
||||
*/
|
||||
baseMethod.getFunctionMethodBridge()
|
||||
} else {
|
||||
this.getFunctionMethodBridge()
|
||||
}
|
||||
|
||||
val returnType: ObjCType = mapReturnType(bridge.returnBridge)
|
||||
val parameters = translateToObjCParameters(bridge)
|
||||
val selector = getSelector(bridge)
|
||||
@@ -75,7 +85,6 @@ internal fun KtFunctionLikeSymbol.buildObjCMethod(
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerKt.toValidObjCSwiftIdentifier]
|
||||
*/
|
||||
@@ -295,4 +304,4 @@ fun KtFunctionLikeSymbol.mapReturnType(returnBridge: MethodBridge.ReturnValue):
|
||||
MethodBridge.ReturnValue.Instance.FactoryResult,
|
||||
-> ObjCInstanceType
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -44,6 +44,7 @@ fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol? {
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtClassOrObjectSymbol.superProtocols(): List<String> {
|
||||
return getDeclaredSuperInterfaceSymbols()
|
||||
.filter { it.isVisibleInObjC() }
|
||||
.map { superInterface -> superInterface.getObjCClassOrProtocolName().objCName }
|
||||
.toList()
|
||||
}
|
||||
+134
@@ -181,4 +181,138 @@ class IsVisibleInObjCTest(
|
||||
assertFalse(fooSymbol.isVisibleInObjC())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - containing symbol visible`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class PublicClass {
|
||||
fun foo()
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
assertTrue(file.getClassOrFail("PublicClass").getFunctionOrFail("foo").isVisibleInObjC())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - invisible symbol inside private class`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
private class PrivateClass {
|
||||
fun foo()
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
assertFalse(file.getClassOrFail("PrivateClass").getFunctionOrFail("foo").isVisibleInObjC())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - nested visible function`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class PublicA {
|
||||
class PublicB {
|
||||
class PublicC {
|
||||
fun foo() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
val foo = file
|
||||
.getClassOrFail("PublicA").getMemberScope()
|
||||
.getClassOrFail("PublicB").getMemberScope()
|
||||
.getClassOrFail("PublicC")
|
||||
.getFunctionOrFail("foo")
|
||||
assertTrue(foo.isVisibleInObjC())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - nested invisible function`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class PublicA {
|
||||
private class PrivateB {
|
||||
class PublicC {
|
||||
fun foo() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
val foo = file
|
||||
.getClassOrFail("PublicA").getMemberScope()
|
||||
.getClassOrFail("PrivateB").getMemberScope()
|
||||
.getClassOrFail("PublicC")
|
||||
.getFunctionOrFail("foo")
|
||||
assertFalse(foo.isVisibleInObjC())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - invisible symbol inside public class marked with @HidesFromObjC`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
@kotlin.native.HidesFromObjC
|
||||
annotation class HideIt
|
||||
|
||||
@HideIt
|
||||
class PublicClass {
|
||||
fun foo()
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
assertFalse(file.getClassOrFail("PublicClass").getFunctionOrFail("foo").isVisibleInObjC())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - invisible member inside nested class marked with @HidesFromObjC`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
@kotlin.native.HidesFromObjC
|
||||
annotation class HideIt
|
||||
|
||||
|
||||
class PublicA {
|
||||
fun publicA() = Unit
|
||||
|
||||
@HideIt
|
||||
class HiddenB {
|
||||
fun hiddenB() = Unit
|
||||
|
||||
class HiddenC {
|
||||
fun hiddenC() = Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
val publicA = file.getClassOrFail("PublicA")
|
||||
val hiddenB = publicA.getMemberScope().getClassOrFail("HiddenB")
|
||||
val hiddenC = hiddenB.getMemberScope().getClassOrFail("HiddenC")
|
||||
|
||||
assertFalse(hiddenB.isVisibleInObjC())
|
||||
assertFalse(hiddenC.isVisibleInObjC())
|
||||
|
||||
assertTrue(publicA.getFunctionOrFail("publicA").isVisibleInObjC())
|
||||
assertFalse(hiddenB.getFunctionOrFail("hiddenB").isVisibleInObjC())
|
||||
assertFalse(hiddenC.getFunctionOrFail("hiddenC").isVisibleInObjC())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user