[ObjCExport] Properly detect Array constructors

^KT-65225 Fixed
This commit is contained in:
Sebastian Sellmair
2024-02-08 10:08:17 +01:00
committed by Space Team
parent 926c0a9136
commit c22149c642
4 changed files with 94 additions and 21 deletions
@@ -43,8 +43,9 @@ internal fun KtPropertySymbol.getPropertyMethodBridge(): MethodBridge {
)
}
context(KtAnalysisSession)
private val KtCallableSymbol.receiverType: MethodBridgeReceiver
get() = if (isConstructor && isArray) {
get() = if (isArrayConstructor) {
MethodBridgeReceiver.Factory
} else if (isTopLevel) {
MethodBridgeReceiver.Static
@@ -129,7 +130,7 @@ private fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
val convertExceptionsToErrors = false // TODO: Add exception handling and return MethodBridge.ReturnValue.WithError.ZeroForError
if (isArray) {
if (isArrayConstructor) {
return MethodBridge.ReturnValue.Instance.FactoryResult
} else if (isConstructor) {
val result = MethodBridge.ReturnValue.Instance.InitResult
@@ -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.analysisApiUtils
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtConstructorSymbol
import org.jetbrains.kotlin.backend.konan.descriptors.arrayTypes
context(KtAnalysisSession)
internal val KtCallableSymbol.isArrayConstructor: Boolean
get() = this is KtConstructorSymbol && getContainingSymbol()
?.let { containingSymbol -> containingSymbol as? KtClassOrObjectSymbol }
?.let { classSymbol -> classSymbol.classIdIfNonLocal?.asFqNameString() in arrayTypes } ?: false
@@ -13,15 +13,13 @@ 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.psi.KtFile
internal val KtCallableSymbol.isConstructor: Boolean
get() = this is KtConstructorSymbol
internal val KtCallableSymbol.isArray: Boolean
get() = false //TODO: temp k2 workaround
context(KtAnalysisSession, KtObjCExportSession)
fun KtFunctionSymbol.translateToObjCMethod(
): ObjCMethod? {
@@ -31,7 +29,6 @@ fun KtFunctionSymbol.translateToObjCMethod(
return buildObjCMethod()
}
context(KtAnalysisSession, KtObjCExportSession)
fun KtFileSymbol.getObjCFileClassOrProtocolName(): ObjCExportFileName? {
val ktFile = this.psi as? KtFile ?: return null
@@ -65,7 +62,7 @@ internal fun KtFunctionLikeSymbol.buildObjCMethod(
attributes += "swift_error(nonnull_error)" // Means "failure <=> (error != nil)".
}
if (this.isConstructor && !isArray) { // TODO: check methodBridge instead.
if (this.isConstructor && !isArrayConstructor) { // TODO: check methodBridge instead.
attributes += "objc_designated_initializer"
}
@@ -239,7 +236,7 @@ context(KtAnalysisSession, KtObjCExportSession)
private fun KtFunctionLikeSymbol.getMangledName(forSwift: Boolean): String {
if (this.isConstructor) {
return if (isArray && !forSwift) "array" else "init"
return if (isArrayConstructor && !forSwift) "array" else "init"
}
val candidate = when (this) {
@@ -292,21 +289,9 @@ private fun String.startsWithWords(words: String) = this.startsWith(words) &&
fun MethodBridge.valueParametersAssociated(
function: KtFunctionLikeSymbol,
): List<Pair<MethodBridgeValueParameter, KtValueParameterSymbol?>> {
val skipFirstKotlinParameter = when (this.receiver) {
MethodBridgeReceiver.Static -> false
MethodBridgeReceiver.Instance -> false
MethodBridgeReceiver.Factory -> true
}
val allParameters = function.valueParameters.let { valueParameters ->
if (skipFirstKotlinParameter) valueParameters.drop(1)
else valueParameters
}
val allParameters = function.valueParameters
if (allParameters.isEmpty()) return emptyList()
return this.valueParameters.mapIndexed { index, valueParameterBridge ->
when (valueParameterBridge) {
is MethodBridgeValueParameter.Mapped -> valueParameterBridge to allParameters[index]
@@ -0,0 +1,69 @@
/*
* 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.name.ClassId
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isArrayConstructor
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.assertFalse
import kotlin.test.assertTrue
import kotlin.test.fail
class IsArrayConstructorTest(
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
) {
@Test
fun `test - regular function`() {
val file = inlineSourceCodeAnalysis.createKtFile("fun foo() = Unit")
analyze(file) {
val foo = file.getFunctionOrFail("foo")
assertFalse(foo.isArrayConstructor)
}
}
@Test
fun `test - regular constructor`() {
val file = inlineSourceCodeAnalysis.createKtFile("class Foo(val x: Int)")
analyze(file) {
val foo = file.getClassOrFail("Foo")
val constructor = foo.getMemberScope().getConstructors().singleOrNull() ?: fail("No single constructor found")
assertFalse(constructor.isArrayConstructor)
}
}
@Test
fun `test - IntArray constructor`() = doTestForArray(ClassId.fromString("kotlin/IntArray"))
@Test
fun `test - ByteArray constructor`() = doTestForArray(ClassId.fromString("kotlin/ByteArray"))
@Test
fun `test - Array - constructor`() = doTestForArray(ClassId.fromString("kotlin/Array"))
@Test
fun `test - NativePtrArray - constructor`() = doTestForArray(ClassId.fromString("kotlin/native/internal/NativePtrArray"))
private fun doTestForArray(classId: ClassId) {
val file = inlineSourceCodeAnalysis.createKtFile("")
analyze(file) {
val arraySymbol = getClassOrObjectSymbolByClassId(classId)
?: fail("Missing $$classId symbol")
arraySymbol.getMemberScope().getConstructors()
.ifEmpty { fail("No constructors found on $classId") }
.forEach { constructor ->
assertTrue(
constructor.isArrayConstructor,
"Expected $classId constructor to be detected as array constructor"
)
}
}
}
}