diff --git a/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/SirGenerator.kt b/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/SirGenerator.kt index cd92794797d..f283060ff38 100644 --- a/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/SirGenerator.kt +++ b/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/SirGenerator.kt @@ -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.toListString() = map { it.asString() } diff --git a/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/transformers/NamedFunction.kt b/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/transformers/NamedFunction.kt new file mode 100644 index 00000000000..249bfc33aa6 --- /dev/null +++ b/native/swift/sir-analysis-api/src/org/jetbrains/kotlin/sir/analysisapi/transformers/NamedFunction.kt @@ -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 + get() = originalFunction.fqName?.pathSegments()?.toListString() ?: emptyList() + + override val parameters: List + 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.toListString() = map { it.asString() } diff --git a/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/AbstractKotlinSirContextTest.kt b/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/AbstractKotlinSirContextTest.kt index 4c8f47e1e5e..4303320d5f7 100644 --- a/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/AbstractKotlinSirContextTest.kt +++ b/native/swift/sir-analysis-api/tests/org/jetbrains/kotlin/sir/analysisapi/AbstractKotlinSirContextTest.kt @@ -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() .forEach { - appendLine("${(it.origin as SirOrigin.KotlinEntity).path}") + val function = (it.origin as SirOrigin.ForeignEntity).entity as KotlinFunction + appendLine("${function.fqName}") } } diff --git a/native/swift/sir-passes/build.gradle.kts b/native/swift/sir-passes/build.gradle.kts index 3d2f9338ff2..5c31d4d3940 100644 --- a/native/swift/sir-passes/build.gradle.kts +++ b/native/swift/sir-passes/build.gradle.kts @@ -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")) } diff --git a/native/swift/sir-passes/src/org/jetbrains/sir/passes/translation/ForeignIntoSwiftFunctionTranslationPass.kt b/native/swift/sir-passes/src/org/jetbrains/sir/passes/translation/ForeignIntoSwiftFunctionTranslationPass.kt new file mode 100644 index 00000000000..a7d76fdb72e --- /dev/null +++ b/native/swift/sir-passes/src/org/jetbrains/sir/passes/translation/ForeignIntoSwiftFunctionTranslationPass.kt @@ -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 { + + private class Transformer : SirTransformer() { + override fun 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") + } +) diff --git a/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/SirPassTests.kt b/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/SirPassTests.kt index 0d9e4a47b96..48dec6b1d9c 100644 --- a/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/SirPassTests.kt +++ b/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/SirPassTests.kt @@ -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?, Unit> { - override fun run(element: SirElement, data: Unit): List? { - 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) } -} \ No newline at end of file + + @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) + } +} diff --git a/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/asserts/functionEquality.kt b/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/asserts/functionEquality.kt new file mode 100644 index 00000000000..833476accbd --- /dev/null +++ b/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/asserts/functionEquality.kt @@ -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 + ) +} diff --git a/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/mocks/MockSirFunction.kt b/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/mocks/MockSirFunction.kt new file mode 100644 index 00000000000..e46baea3a2e --- /dev/null +++ b/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/mocks/MockSirFunction.kt @@ -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, + override val returnType: SirType, +) : SirFunction() { + override fun acceptChildren(visitor: SirVisitor, data: D) = Unit + override fun transformChildren(transformer: SirTransformer, data: D) = Unit +} diff --git a/native/swift/sir/build.gradle.kts b/native/swift/sir/build.gradle.kts index cda8a66696f..4d3362b4ffc 100644 --- a/native/swift/sir/build.gradle.kts +++ b/native/swift/sir/build.gradle.kts @@ -27,6 +27,8 @@ sourceSets { "test" { projectDefault() } } +testsJar() + projectTest(jUnitMode = JUnitMode.JUnit5) { workingDir = rootDir useJUnitPlatform { } diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/KotlinEntity.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/KotlinEntity.kt new file mode 100644 index 00000000000..62d86fda1f0 --- /dev/null +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/KotlinEntity.kt @@ -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 + val parameters: List + val returnType: KotlinType +} + +interface KotlinParameter : KotlinEntity { + val name: String + val type: KotlinType +} + +interface KotlinType : KotlinEntity { + val name: String +} diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirOrigin.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirOrigin.kt index 18d4b915f9b..e984ad00f6f 100644 --- a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirOrigin.kt +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirOrigin.kt @@ -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) : 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 -} \ No newline at end of file + data object Unknown : SirOrigin +} diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirType.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirType.kt index fef7f25471e..f1a920341c2 100644 --- a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirType.kt +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirType.kt @@ -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 diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/constants/NumericTypes.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/constants/NumericTypes.kt new file mode 100644 index 00000000000..2567cbf885a --- /dev/null +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/constants/NumericTypes.kt @@ -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" diff --git a/native/swift/sir/tests/org/jetbrains/kotlin/sir/SirTest.kt b/native/swift/sir/tests/org/jetbrains/kotlin/sir/SirTest.kt index 4ced8315ae1..fccc3a59b24 100644 --- a/native/swift/sir/tests/org/jetbrains/kotlin/sir/SirTest.kt +++ b/native/swift/sir/tests/org/jetbrains/kotlin/sir/SirTest.kt @@ -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 } diff --git a/native/swift/sir/tests/org/jetbrains/kotlin/sir/mock/KotlinEntity.kt b/native/swift/sir/tests/org/jetbrains/kotlin/sir/mock/KotlinEntity.kt new file mode 100644 index 00000000000..eac034efd0e --- /dev/null +++ b/native/swift/sir/tests/org/jetbrains/kotlin/sir/mock/KotlinEntity.kt @@ -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, + override val parameters: List, + 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