KT-64931: add convertion of kotlin top-lvl functions into swift static functions

Merge-request: KT-MR-13878
Merged-by: Artem Olkov <artem.olkov@jetbrains.com>
This commit is contained in:
Artem Olkov
2024-01-17 20:30:04 +00:00
committed by Space Team
parent 01824a336c
commit 37a8723a79
24 changed files with 133 additions and 25 deletions
@@ -7,7 +7,7 @@ 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.name.FqName
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
@@ -24,8 +24,8 @@ private fun KtValueParameterSymbol.toSirParam(): SirKotlinOrigin.Parameter = AAP
private class AAFunction(
private val originalFunction: KtNamedFunction
) : SirKotlinOrigin.Function {
override val fqName: List<String>
get() = originalFunction.fqName?.pathSegments()?.toListString() ?: emptyList()
override val fqName: FqName
get() = originalFunction.fqName ?: FqName.fromSegments(emptyList())
override val parameters: List<SirKotlinOrigin.Parameter>
get() = analyze(originalFunction) {
@@ -48,5 +48,3 @@ private data class AAParameter(
private data class AAKotlinType(
override val name: String
) : SirKotlinOrigin.Type
private fun List<Name>.toListString() = map { it.asString() }
@@ -45,7 +45,7 @@ abstract class AbstractKotlinSirContextTestBase : AbstractAnalysisApiBasedTest()
.filterIsInstance<SirForeignFunction>()
.forEach {
val function = it.origin as SirKotlinOrigin.Function
appendLine("${function.fqName}")
appendLine("${function.path}")
}
}
@@ -90,6 +90,7 @@ private fun readRequestFromFile(file: File): BridgeRequest {
this.name = fqName.last()
this.returnType = returnType
this.parameters += parameters
this.isStatic = false
}
return BridgeRequest(function, bridgeName, fqName)
}
@@ -35,7 +35,9 @@ public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothi
return buildFunction {
origin = function.origin
visibility = function.visibility
name = kotlinOrigin.fqName.last()
isStatic = function.parent is SirDeclaration
name = kotlinOrigin.path.last()
kotlinOrigin.parameters.mapTo(parameters) { it.toSir() }
returnType = kotlinOrigin.returnType.toSir()
@@ -29,6 +29,7 @@ class SirParentPatcherTests {
val function = buildFunction {
name = "foo"
returnType = SirNominalType(SirSwiftModule.bool)
isStatic = false
}
function.parent = wrongEnum
val wrongModule = buildModule { name = "wrongModule" }
@@ -5,10 +5,12 @@
package org.jetbrains.kotlin.sir.passes
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.sir.SirFunction
import org.jetbrains.kotlin.sir.SirNominalType
import org.jetbrains.kotlin.sir.SirParameter
import org.jetbrains.kotlin.sir.SirVisibility
import org.jetbrains.kotlin.sir.builder.buildEnum
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.sir.constants.*
@@ -19,9 +21,7 @@ import org.jetbrains.kotlin.sir.passes.asserts.assertSirFunctionsEquals
import org.jetbrains.kotlin.sir.passes.mocks.MockSirFunction
import org.jetbrains.kotlin.sir.passes.util.runWithAsserts
import org.jetbrains.kotlin.sir.util.SirSwiftModule
import org.jetbrains.sir.passes.run
import org.jetbrains.sir.passes.translation.ForeignIntoSwiftFunctionTranslationPass
import org.jetbrains.sir.passes.utility.assertValid
import kotlin.test.Test
import kotlin.test.assertNotNull
@@ -33,7 +33,7 @@ class SirPassTests {
}
val mySirElement = buildForeignFunction {
origin = MockFunction(
fqName = listOf("foo"),
fqName = FqName.fromSegments(listOf("foo")),
parameters = emptyList(),
returnType = MockKotlinType(BOOLEAN),
)
@@ -48,6 +48,38 @@ class SirPassTests {
parameters = emptyList(),
returnType = SirNominalType(SirSwiftModule.bool),
parent = module,
isStatic = false,
)
assertSirFunctionsEquals(actual = result, expected = exp)
}
@Test
fun `foreign toplevel function without params with package should be translated as static`() {
val module = buildModule {
name = "demo"
}
val mySirEnum = buildEnum {
name = "bar"
}
mySirEnum.parent = module
val mySirElement = buildForeignFunction {
origin = MockFunction(
fqName = FqName.fromSegments(listOf("bar", "foo")),
parameters = emptyList(),
returnType = MockKotlinType(BOOLEAN),
)
visibility = SirVisibility.PUBLIC
}
mySirElement.parent = mySirEnum
val myPass = ForeignIntoSwiftFunctionTranslationPass()
val result = myPass.runWithAsserts(mySirElement, null) as? SirFunction
assertNotNull(result, "SirFunction should be produced")
val exp = MockSirFunction(
name = "foo",
parameters = emptyList(),
returnType = SirNominalType(SirSwiftModule.bool),
parent = mySirEnum,
isStatic = true,
)
assertSirFunctionsEquals(actual = result, expected = exp)
}
@@ -59,7 +91,7 @@ class SirPassTests {
}
val mySirElement = buildForeignFunction {
origin = MockFunction(
fqName = listOf("foo"),
fqName = FqName.fromSegments(listOf("foo")),
parameters = listOf(
MockParameter(
name = "arg1",
@@ -112,7 +144,8 @@ class SirPassTests {
SirParameter(argumentName = "arg7", type = SirNominalType(SirSwiftModule.bool)),
),
returnType = SirNominalType(SirSwiftModule.int8),
parent = module
parent = module,
isStatic = false,
)
assertSirFunctionsEquals(actual = result, expected = exp)
}
@@ -5,12 +5,11 @@
package org.jetbrains.kotlin.sir.passes
import org.jetbrains.kotlin.sir.SirOrigin
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.sir.mock.MockFunction
import org.jetbrains.kotlin.sir.mock.MockKotlinType
import org.jetbrains.sir.passes.utility.PatchDeclarationParentVisitor
import org.jetbrains.sir.passes.utility.SirValidatorConfig
import org.jetbrains.sir.passes.utility.ValidationError
import org.jetbrains.sir.passes.utility.validate
@@ -40,9 +39,9 @@ class SirValidatorTests {
}
val foreignFunction = buildForeignFunction {
val kotlinEntity = MockFunction(
fqName = listOf("foo"),
fqName = FqName.fromSegments(listOf("foo")),
parameters = emptyList(),
returnType = MockKotlinType("kotlin/Byte")
returnType = MockKotlinType("kotlin/Byte"),
)
origin = kotlinEntity
}
@@ -60,4 +59,4 @@ class SirValidatorTests {
assertEquals(foreignFunction, error.declaration)
assertEquals(module, error.expectedParent)
}
}
}
@@ -25,4 +25,8 @@ fun assertSirFunctionsEquals(expected: SirFunction, actual: SirFunction) {
actual = actual.returnType,
expected = expected.returnType
)
assertEquals(
actual = actual.isStatic,
expected = expected.isStatic
)
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.sir.visitors.SirVisitor
class MockSirFunction(
override val origin: SirOrigin = SirOrigin.Unknown,
override val isStatic: Boolean,
override val visibility: SirVisibility = SirVisibility.PUBLIC,
override var parent: SirDeclarationParent,
override val name: String,
@@ -31,6 +31,7 @@ public class SirAsSwiftSourcesPrinter(private val printer: SmartPrinter) : SirVi
override fun visitFunction(function: SirFunction): Unit = with(printer) {
print(
function.visibility.takeIf { it != SirVisibility.INTERNAL }?.let { "${it.swift} " } ?: "",
if (function.isStatic) { "static " } else { "" },
"func ",
function.name.swiftIdentifier,
"("
@@ -0,0 +1,5 @@
public static func foo(
arg1: Swift.Int32
) -> Swift.Bool {
return foo_wrapped(arg1)
}
@@ -37,6 +37,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = false
visibility = SirVisibility.PUBLIC
name = "foo"
returnType = SirNominalType(SirSwiftModule.bool)
@@ -57,6 +58,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = false
visibility = SirVisibility.PUBLIC
name = "foo1"
returnType = SirNominalType(SirSwiftModule.bool)
@@ -65,6 +67,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = false
visibility = SirVisibility.PUBLIC
name = "foo2"
returnType = SirNominalType(SirSwiftModule.bool)
@@ -85,6 +88,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = false
visibility = SirVisibility.PUBLIC
name = "foo"
parameters.add(
@@ -112,6 +116,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = false
visibility = SirVisibility.PUBLIC
name = "foo"
parameters.add(
@@ -146,6 +151,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = false
visibility = SirVisibility.PUBLIC
name = "foo"
parameters.addAll(
@@ -199,6 +205,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = false
visibility = SirVisibility.PUBLIC
name = "foo"
parameters.add(
@@ -219,6 +226,35 @@ class SirAsSwiftSourcesPrinterTests {
)
}
@Test
fun `should print static`() {
val module = buildModule {
name = "Test"
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = true
visibility = SirVisibility.PUBLIC
name = "foo"
parameters.add(
SirParameter(
argumentName = "arg1",
type = SirNominalType(SirSwiftModule.int32)
)
)
returnType = SirNominalType(SirSwiftModule.bool)
body = SirFunctionBody(listOf("return foo_wrapped(arg1)"))
}
)
}
runTest(
module,
"testData/static_function"
)
}
private fun runTest(module: SirModule, goldenDataFile: String) {
val expectedSwiftSrc = File(KtTestUtil.getHomeDirectory()).resolve("$goldenDataFile.golden.swift")
+2
View File
@@ -9,6 +9,8 @@ description = "Swift Intermediate Representation"
dependencies {
compileOnly(kotlinStdlib())
api(project(":core:compiler.common"))
if (kotlinBuildProperties.isInIdeaSync) {
compileOnly(project("tree-generator")) // Provided, so that IDEA can recognize references to this module in KDoc.
}
@@ -18,6 +18,7 @@ abstract class SirFunction : SirElementBase(), SirCallable {
abstract override val origin: SirOrigin
abstract override val visibility: SirVisibility
abstract override var parent: SirDeclarationParent
abstract val isStatic: Boolean
abstract val name: String
abstract val parameters: List<SirParameter>
abstract val returnType: SirType
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.sir.impl.SirFunctionImpl
class SirFunctionBuilder {
var origin: SirOrigin = SirOrigin.Unknown
var visibility: SirVisibility = SirVisibility.PUBLIC
var isStatic: Boolean by kotlin.properties.Delegates.notNull<Boolean>()
lateinit var name: String
val parameters: MutableList<SirParameter> = mutableListOf()
lateinit var returnType: SirType
@@ -27,6 +28,7 @@ class SirFunctionBuilder {
return SirFunctionImpl(
origin,
visibility,
isStatic,
name,
parameters,
returnType,
@@ -52,6 +54,7 @@ inline fun buildFunctionCopy(original: SirFunction, init: SirFunctionBuilder.()
val copyBuilder = SirFunctionBuilder()
copyBuilder.origin = original.origin
copyBuilder.visibility = original.visibility
copyBuilder.isStatic = original.isStatic
copyBuilder.name = original.name
copyBuilder.parameters.addAll(original.parameters)
copyBuilder.returnType = original.returnType
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.sir.visitors.SirVisitor
internal class SirFunctionImpl(
override val origin: SirOrigin,
override val visibility: SirVisibility,
override val isStatic: Boolean,
override val name: String,
override val parameters: MutableList<SirParameter>,
override val returnType: SirType,
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.sir
import org.jetbrains.kotlin.name.FqName
sealed interface SirKotlinOrigin : SirOrigin.Foreign {
val fqName: List<String>
val fqName: FqName
override val path: List<String>
get() = fqName
get() = fqName.pathSegments().map { it.asString() }
interface Function : SirKotlinOrigin {
val parameters: List<Parameter>
@@ -24,4 +26,4 @@ sealed interface SirKotlinOrigin : SirOrigin.Foreign {
interface Type {
val name: String
}
}
}
@@ -5,10 +5,11 @@
package org.jetbrains.kotlin.sir.mock
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.sir.SirKotlinOrigin
data class MockFunction(
override val fqName: List<String>,
override val fqName: FqName,
override val parameters: List<SirKotlinOrigin.Parameter>,
override val returnType: SirKotlinOrigin.Type,
) : SirKotlinOrigin.Function
@@ -7,6 +7,7 @@
package org.jetbrains.kotlin.sir.tree.generator
import org.jetbrains.kotlin.generators.tree.StandardTypes.boolean
import org.jetbrains.kotlin.generators.tree.StandardTypes.string
import org.jetbrains.kotlin.sir.tree.generator.config.AbstractSwiftIrTreeBuilder
import org.jetbrains.kotlin.sir.tree.generator.model.Element
@@ -81,6 +82,7 @@ object SwiftIrTree : AbstractSwiftIrTreeBuilder() {
customParentInVisitor = callable
parent(callable)
+field("isStatic", boolean) // todo: KT-65046 Method|function distinction in SIR
+field("name", string)
+listField("parameters", parameterType)
+field("returnType", typeType)
@@ -164,7 +164,7 @@ class SwiftExportExtension(
}
override fun visitFunction(function: SirFunction) {
val fqName = (function.origin as? SirKotlinOrigin.Function)?.fqName
val fqName = (function.origin as? SirKotlinOrigin.Function)?.path
?: return
val fqNameForBridge = if (fqName.count() == 1) {
listOf("__root__", fqName.first()) // todo: should be changed with correct mangling KT-64970
+2
View File
@@ -5,3 +5,5 @@ int32_t namespace1_main_foobar(int32_t param);
int32_t namespace1_foo();
int32_t namespace2_bar();
int32_t __root___foo();
+6
View File
@@ -17,3 +17,9 @@ public fun namespace2_bar(): Int {
val result = namespace2.bar()
return result
}
@ExportedBridge("__root___foo")
public fun __root___foo(): Int {
val result = foo()
return result
}
+7 -3
View File
@@ -1,18 +1,22 @@
enum namespace1 {
enum main {
public func foobar(
public static func foobar(
param: Swift.Int32
) -> Swift.Int32 {
return namespace1_main_foobar(param)
}
}
public func foo() -> Swift.Int32 {
public static func foo() -> Swift.Int32 {
return namespace1_foo()
}
}
enum namespace2 {
public func bar() -> Swift.Int32 {
public static func bar() -> Swift.Int32 {
return namespace2_bar()
}
}
public func foo() -> Swift.Int32 {
return __root___foo()
}
+3
View File
@@ -1,5 +1,8 @@
// WITH_STDLIB
// FILE: no_package.kt
fun foo(): Int = 123
// FILE: foo.kt
package namespace1
fun foo(): Int = 123