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 package org.jetbrains.kotlin.sir.analysisapi
import org.jetbrains.kotlin.analysis.api.analyze import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.isPublic import org.jetbrains.kotlin.psi.psiUtil.isPublic
import org.jetbrains.kotlin.sir.SirOrigin
import org.jetbrains.kotlin.sir.SirDeclaration import org.jetbrains.kotlin.sir.SirDeclaration
import org.jetbrains.kotlin.sir.SirForeignFunction import org.jetbrains.kotlin.sir.SirForeignFunction
import org.jetbrains.kotlin.sir.SirVisibility import org.jetbrains.kotlin.sir.analysisapi.transformers.toForeignFunction
import org.jetbrains.kotlin.sir.builder.SirForeignFunctionBuilder
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
/** /**
* A root interface for classes that produce Swift IR elements. * A root interface for classes that produce Swift IR elements.
@@ -37,17 +33,8 @@ class SirGenerator : SirFactory {
super.visitNamedFunction(function) super.visitNamedFunction(function)
function function
.takeIf { function.isPublic } .takeIf { function.isPublic }
?.fqName ?.toForeignFunction()
?.pathSegments()
?.toListString()
?.let { names -> buildForeignFunction {
origin = SirOrigin.KotlinEntity(names)
visibility = SirVisibility.PUBLIC
}
}
?.let { res.add(it) } ?.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.analysis.test.framework.test.configurators.FrontendKind
import org.jetbrains.kotlin.platform.konan.NativePlatforms import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.sir.SirDeclaration
import org.jetbrains.kotlin.sir.SirForeignFunction 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.SirOrigin
import org.jetbrains.kotlin.sir.builder.SirModuleBuilder
import org.jetbrains.kotlin.sir.builder.buildModule import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.services.TestModuleStructure import org.jetbrains.kotlin.test.services.TestModuleStructure
@@ -62,7 +60,8 @@ abstract class AbstractKotlinSirContextTestBase : AbstractAnalysisApiBasedTest()
module.declarations module.declarations
.filterIsInstance<SirForeignFunction>() .filterIsInstance<SirForeignFunction>()
.forEach { .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) testImplementation(libs.junit.jupiter.api)
testRuntimeOnly(libs.junit.jupiter.engine) testRuntimeOnly(libs.junit.jupiter.engine)
testImplementation(projectTests(":native:swift:sir"))
testImplementation(projectTests(":compiler:tests-common")) 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.*
import org.jetbrains.kotlin.sir.builder.buildForeignFunction 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.Test
import kotlin.test.assertEquals import kotlin.test.assertNotNull
class SirPassTests { class SirPassTests {
@Test @Test
fun smoke() { fun `foreign toplevel function without params should be translated`() {
val elementDescription = "mySirElement" val module = buildModule {
name = "demo"
}
val mySirElement = buildForeignFunction { val mySirElement = buildForeignFunction {
origin = SirOrigin.KotlinEntity(listOf(elementDescription)) origin = SirOrigin.ForeignEntity(
MockFunction(
fqName = listOf("foo"),
parameters = emptyList(),
returnType = MockKotlinType(BOOLEAN),
)
)
visibility = SirVisibility.PUBLIC visibility = SirVisibility.PUBLIC
} }
val myPass = object : SirPass<List<String>?, Unit> { mySirElement.parent = module
override fun run(element: SirElement, data: Unit): List<String>? {
if (element is SirDeclaration) { val myPass = ForeignIntoSwiftFunctionTranslationPass()
return (element.origin as? SirOrigin.KotlinEntity)?.path val result = myPass.run(mySirElement, Unit) as? SirFunction
} assertNotNull(result, "SirFunction should be produced")
return null val exp = MockSirFunction(
} name = "foo",
} parameters = emptyList(),
val result = myPass.run(mySirElement, Unit) returnType = SirNominalType(SirSwiftModule.bool),
assertEquals(elementDescription, result?.first()) 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() } "test" { projectDefault() }
} }
testsJar()
projectTest(jUnitMode = JUnitMode.JUnit5) { projectTest(jUnitMode = JUnitMode.JUnit5) {
workingDir = rootDir workingDir = rootDir
useJUnitPlatform { } 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 package org.jetbrains.kotlin.sir
sealed interface SirOrigin { sealed interface SirOrigin {
sealed interface Synthetic : SirOrigin sealed interface Synthetic : SirOrigin
data class ExternallyDefined(val name: String) : Synthetic 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 * Value for nodes of unknown or non-viable origin
* (e.g. objects created in/for tests) * (e.g. objects created in/for tests)
*/ */
data object Unknown: SirOrigin data object Unknown : SirOrigin
} }
@@ -10,7 +10,23 @@ sealed interface SirType
class SirNominalType( class SirNominalType(
val type: SirNamedDeclaration, val type: SirNamedDeclaration,
val parent: SirNominalType? = null, 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( class SirExistentialType(
// TODO: Protocols. For now, only `any Any` is supported // 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 package org.jetbrains.kotlin.sir
import org.jetbrains.kotlin.sir.builder.buildEnum 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.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue
@@ -22,7 +26,18 @@ class SirTest {
private fun produceSwiftElement(): Any { private fun produceSwiftElement(): Any {
return buildEnum { 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" name = "MyEnum"
visibility = SirVisibility.PUBLIC 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