KT-63273: Swift IR passes to support functions with numeric parameters

Merge-request: KT-MR-13290
Merged-by: Artem Olkov <artem.olkov@jetbrains.com>
This commit is contained in:
Artem Olkov
2023-12-08 14:07:42 +00:00
committed by Space Team
parent 7d685522ce
commit 38d5e56530
15 changed files with 386 additions and 41 deletions
@@ -6,17 +6,13 @@
package org.jetbrains.kotlin.sir.analysisapi
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.isPublic
import org.jetbrains.kotlin.sir.SirOrigin
import org.jetbrains.kotlin.sir.SirDeclaration
import org.jetbrains.kotlin.sir.SirForeignFunction
import org.jetbrains.kotlin.sir.SirVisibility
import org.jetbrains.kotlin.sir.builder.SirForeignFunctionBuilder
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
import org.jetbrains.kotlin.sir.analysisapi.transformers.toForeignFunction
/**
* A root interface for classes that produce Swift IR elements.
@@ -37,17 +33,8 @@ class SirGenerator : SirFactory {
super.visitNamedFunction(function)
function
.takeIf { function.isPublic }
?.fqName
?.pathSegments()
?.toListString()
?.let { names -> buildForeignFunction {
origin = SirOrigin.KotlinEntity(names)
visibility = SirVisibility.PUBLIC
}
}
?.toForeignFunction()
?.let { res.add(it) }
}
}
}
private fun List<Name>.toListString() = map { it.asString() }
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2023 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.sir.analysisapi.transformers
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
internal fun KtNamedFunction.toForeignFunction(): SirForeignFunction = buildForeignFunction {
origin = SirOrigin.ForeignEntity(
AAFunction(this@toForeignFunction)
)
}
private fun KtValueParameterSymbol.toSirParam(): KotlinParameter = AAParameter(
name = name.toString(),
type = AAKotlinType(name = returnType.toString())
)
private class AAFunction(
private val originalFunction: KtNamedFunction
) : KotlinFunction {
override val fqName: List<String>
get() = originalFunction.fqName?.pathSegments()?.toListString() ?: emptyList()
override val parameters: List<KotlinParameter>
get() = analyze(originalFunction) {
val function = originalFunction.getFunctionLikeSymbol()
function.valueParameters.map { it.toSirParam() }
}
override val returnType: KotlinType
get() = analyze(originalFunction) {
val function = originalFunction.getFunctionLikeSymbol()
AAKotlinType(name = function.returnType.toString())
}
}
private data class AAParameter(
override val name: String,
override val type: KotlinType
) : KotlinParameter
private data class AAKotlinType(
override val name: String
) : KotlinType
private fun List<Name>.toListString() = map { it.asString() }
@@ -12,11 +12,9 @@ import org.jetbrains.kotlin.analysis.test.framework.test.configurators.*
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.sir.SirDeclaration
import org.jetbrains.kotlin.sir.SirForeignFunction
import org.jetbrains.kotlin.sir.SirModule
import org.jetbrains.kotlin.sir.KotlinFunction
import org.jetbrains.kotlin.sir.SirOrigin
import org.jetbrains.kotlin.sir.builder.SirModuleBuilder
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.services.TestModuleStructure
@@ -62,7 +60,8 @@ abstract class AbstractKotlinSirContextTestBase : AbstractAnalysisApiBasedTest()
module.declarations
.filterIsInstance<SirForeignFunction>()
.forEach {
appendLine("${(it.origin as SirOrigin.KotlinEntity).path}")
val function = (it.origin as SirOrigin.ForeignEntity).entity as KotlinFunction
appendLine("${function.fqName}")
}
}
+1
View File
@@ -14,6 +14,7 @@ dependencies {
testImplementation(libs.junit.jupiter.api)
testRuntimeOnly(libs.junit.jupiter.engine)
testImplementation(projectTests(":native:swift:sir"))
testImplementation(projectTests(":compiler:tests-common"))
}
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2023 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.sir.passes.translation
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildFunction
import org.jetbrains.kotlin.sir.util.SirSwiftModule
import org.jetbrains.kotlin.sir.KotlinFunction
import org.jetbrains.kotlin.sir.constants.*
import org.jetbrains.kotlin.sir.visitors.SirTransformer
import org.jetbrains.sir.passes.SirPass
import java.lang.IllegalStateException
/**
* Translates `SirForeignFunction` into `SirFunction`.
* Works only with Nominal Types and top-level functions, currently.
* If received `element` of different type than `SirForeignFunction`,
* or `element` does not contain origin of type `SirOrigin.KotlinEntity.Function`,
* returns original element.
*/
class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Unit> {
private class Transformer : SirTransformer<Unit>() {
override fun <E : SirElement> transformElement(element: E, data: Unit): E {
element.transformChildren(this, data)
return element
}
override fun transformForeignFunction(foreignFunction: SirForeignFunction, data: Unit): SirDeclaration {
val kotlinOrigin = (foreignFunction.origin as? SirOrigin.ForeignEntity)?.entity as? KotlinFunction
?: return foreignFunction
return buildFunction {
origin = foreignFunction.origin
visibility = foreignFunction.visibility
name = kotlinOrigin.fqName.last()
kotlinOrigin.parameters.mapTo(parameters) { it.toSir() }
returnType = kotlinOrigin.returnType.toSir()
}.apply {
parent = foreignFunction.parent
}
}
}
override fun run(element: SirElement, data: Unit): SirElement = element.accept(Transformer(), Unit)
}
private fun KotlinParameter.toSir(): SirParameter = SirParameter(
argumentName = name,
type = type.toSir(),
)
private fun KotlinType.toSir(): SirType = SirNominalType(
type = when (this.name) {
BYTE -> SirSwiftModule.int8
SHORT -> SirSwiftModule.int16
INT -> SirSwiftModule.int32
LONG -> SirSwiftModule.int64
BOOLEAN -> SirSwiftModule.bool
DOUBLE -> SirSwiftModule.double
FLOAT -> SirSwiftModule.float
else -> throw IllegalStateException("unknown externally defined type")
}
)
@@ -7,27 +7,113 @@ package org.jetbrains.kotlin.sir.passes
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
import org.jetbrains.sir.passes.SirPass
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.sir.constants.*
import org.jetbrains.kotlin.sir.mock.MockFunction
import org.jetbrains.kotlin.sir.mock.MockKotlinType
import org.jetbrains.kotlin.sir.mock.MockParameter
import org.jetbrains.kotlin.sir.passes.asserts.assertSirFunctionsEquals
import org.jetbrains.kotlin.sir.passes.mocks.MockSirFunction
import org.jetbrains.kotlin.sir.util.SirSwiftModule
import org.jetbrains.sir.passes.translation.ForeignIntoSwiftFunctionTranslationPass
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class SirPassTests {
@Test
fun smoke() {
val elementDescription = "mySirElement"
fun `foreign toplevel function without params should be translated`() {
val module = buildModule {
name = "demo"
}
val mySirElement = buildForeignFunction {
origin = SirOrigin.KotlinEntity(listOf(elementDescription))
origin = SirOrigin.ForeignEntity(
MockFunction(
fqName = listOf("foo"),
parameters = emptyList(),
returnType = MockKotlinType(BOOLEAN),
)
)
visibility = SirVisibility.PUBLIC
}
val myPass = object : SirPass<List<String>?, Unit> {
override fun run(element: SirElement, data: Unit): List<String>? {
if (element is SirDeclaration) {
return (element.origin as? SirOrigin.KotlinEntity)?.path
}
return null
}
}
val result = myPass.run(mySirElement, Unit)
assertEquals(elementDescription, result?.first())
mySirElement.parent = module
val myPass = ForeignIntoSwiftFunctionTranslationPass()
val result = myPass.run(mySirElement, Unit) as? SirFunction
assertNotNull(result, "SirFunction should be produced")
val exp = MockSirFunction(
name = "foo",
parameters = emptyList(),
returnType = SirNominalType(SirSwiftModule.bool),
parent = module,
)
assertSirFunctionsEquals(actual = result, expected = exp)
}
}
@Test
fun `foreign toplevel function with all params should be translated`() {
val module = buildModule {
name = "demo"
}
val mySirElement = buildForeignFunction {
origin = SirOrigin.ForeignEntity(
MockFunction(
fqName = listOf("foo"),
parameters = listOf(
MockParameter(
name = "arg1",
type = MockKotlinType(name = BYTE)
),
MockParameter(
name = "arg2",
type = MockKotlinType(name = SHORT)
),
MockParameter(
name = "arg3",
type = MockKotlinType(name = INT)
),
MockParameter(
name = "arg4",
type = MockKotlinType(name = LONG)
),
MockParameter(
name = "arg5",
type = MockKotlinType(name = DOUBLE)
),
MockParameter(
name = "arg6",
type = MockKotlinType(name = FLOAT)
),
MockParameter(
name = "arg7",
type = MockKotlinType(name = BOOLEAN)
)
),
returnType = MockKotlinType(name = BYTE),
)
)
visibility = SirVisibility.PUBLIC
}
mySirElement.parent = module
val myPass = ForeignIntoSwiftFunctionTranslationPass()
val result = myPass.run(mySirElement, Unit) as? SirFunction
assertNotNull(result, "SirFunction should be produced")
val exp = MockSirFunction(
name = "foo",
parameters = listOf(
SirParameter(argumentName = "arg1", type = SirNominalType(SirSwiftModule.int8)),
SirParameter(argumentName = "arg2", type = SirNominalType(SirSwiftModule.int16)),
SirParameter(argumentName = "arg3", type = SirNominalType(SirSwiftModule.int32)),
SirParameter(argumentName = "arg4", type = SirNominalType(SirSwiftModule.int64)),
SirParameter(argumentName = "arg5", type = SirNominalType(SirSwiftModule.double)),
SirParameter(argumentName = "arg6", type = SirNominalType(SirSwiftModule.float)),
SirParameter(argumentName = "arg7", type = SirNominalType(SirSwiftModule.bool)),
),
returnType = SirNominalType(SirSwiftModule.int8),
parent = module
)
assertSirFunctionsEquals(actual = result, expected = exp)
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2023 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.sir.passes.asserts
import org.jetbrains.kotlin.sir.SirFunction
import kotlin.test.assertEquals
fun assertSirFunctionsEquals(expected: SirFunction, actual: SirFunction) {
assertEquals(
actual = actual.parent,
expected = expected.parent
)
assertEquals(
actual = actual.name,
expected = expected.name
)
assertEquals(
actual = actual.parameters,
expected = expected.parameters
)
assertEquals(
actual = actual.returnType,
expected = expected.returnType
)
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2023 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.sir.passes.mocks
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.util.SirSwiftModule
import org.jetbrains.kotlin.sir.visitors.SirTransformer
import org.jetbrains.kotlin.sir.visitors.SirVisitor
class MockSirFunction(
override val origin: SirOrigin = SirOrigin.Unknown,
override val visibility: SirVisibility = SirVisibility.PUBLIC,
override var parent: SirDeclarationParent,
override val name: String,
override val parameters: List<SirParameter>,
override val returnType: SirType,
) : SirFunction() {
override fun <R, D> acceptChildren(visitor: SirVisitor<R, D>, data: D) = Unit
override fun <D> transformChildren(transformer: SirTransformer<D>, data: D) = Unit
}
+2
View File
@@ -27,6 +27,8 @@ sourceSets {
"test" { projectDefault() }
}
testsJar()
projectTest(jUnitMode = JUnitMode.JUnit5) {
workingDir = rootDir
useJUnitPlatform { }
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2023 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.sir
sealed interface KotlinEntity
interface KotlinFunction : KotlinEntity {
val fqName: List<String>
val parameters: List<KotlinParameter>
val returnType: KotlinType
}
interface KotlinParameter : KotlinEntity {
val name: String
val type: KotlinType
}
interface KotlinType : KotlinEntity {
val name: String
}
@@ -6,15 +6,15 @@
package org.jetbrains.kotlin.sir
sealed interface SirOrigin {
sealed interface Synthetic : SirOrigin
sealed interface Synthetic : SirOrigin
data class ExternallyDefined(val name: String) : Synthetic
data class KotlinEntity(val path: List<String>) : SirOrigin
data class ForeignEntity(val entity: KotlinEntity): SirOrigin
/**
* Value for nodes of unknown or non-viable origin
* (e.g. objects created in/for tests)
*/
data object Unknown: SirOrigin
}
data object Unknown : SirOrigin
}
@@ -10,7 +10,23 @@ sealed interface SirType
class SirNominalType(
val type: SirNamedDeclaration,
val parent: SirNominalType? = null,
) : SirType
) : SirType {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SirNominalType) return false
if (type != other.type) return false
if (parent != other.parent) return false
return true
}
override fun hashCode(): Int {
var result = type.hashCode()
result = 31 * result + (parent?.hashCode() ?: 0)
return result
}
}
class SirExistentialType(
// TODO: Protocols. For now, only `any Any` is supported
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2023 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.sir.constants
private const val KOTLIN_PACKAGE = "kotlin"
private const val DELIMITER = "/"
private const val KOTLIN_NUMERIC_TYPE_PREFIX = "${KOTLIN_PACKAGE}${DELIMITER}"
const val BYTE = "${KOTLIN_NUMERIC_TYPE_PREFIX}Byte"
const val SHORT = "${KOTLIN_NUMERIC_TYPE_PREFIX}Short"
const val INT = "${KOTLIN_NUMERIC_TYPE_PREFIX}Int"
const val LONG = "${KOTLIN_NUMERIC_TYPE_PREFIX}Long"
const val BOOLEAN = "${KOTLIN_NUMERIC_TYPE_PREFIX}Boolean"
const val DOUBLE = "${KOTLIN_NUMERIC_TYPE_PREFIX}Double"
const val FLOAT = "${KOTLIN_NUMERIC_TYPE_PREFIX}Float"
@@ -6,6 +6,10 @@
package org.jetbrains.kotlin.sir
import org.jetbrains.kotlin.sir.builder.buildEnum
import org.jetbrains.kotlin.sir.constants.BYTE
import org.jetbrains.kotlin.sir.mock.MockFunction
import org.jetbrains.kotlin.sir.mock.MockKotlinType
import org.jetbrains.kotlin.sir.mock.MockParameter
import kotlin.test.Test
import kotlin.test.assertTrue
@@ -22,7 +26,18 @@ class SirTest {
private fun produceSwiftElement(): Any {
return buildEnum {
origin = SirOrigin.KotlinEntity(path = listOf("org.me.MyEnum"))
origin = SirOrigin.ForeignEntity(
MockFunction(
fqName = listOf("foo"),
parameters = listOf(
MockParameter(
name = "arg1",
type = MockKotlinType(BYTE),
)
),
returnType = MockKotlinType("kotlin/Byte")
)
)
name = "MyEnum"
visibility = SirVisibility.PUBLIC
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2023 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.sir.mock
import org.jetbrains.kotlin.sir.KotlinFunction
import org.jetbrains.kotlin.sir.KotlinType
import org.jetbrains.kotlin.sir.KotlinParameter
data class MockFunction(
override val fqName: List<String>,
override val parameters: List<KotlinParameter>,
override val returnType: KotlinType,
) : KotlinFunction
data class MockParameter(
override val name: String,
override val type: KotlinType,
) : KotlinParameter
data class MockKotlinType(
override val name: String
) : KotlinType