[Swift Export]: KT-63280: Package inflation

Merge-request: KT-MR-13331
Merged-by: Gleb Lukianets <Gleb.Lukianets@jetbrains.com>
This commit is contained in:
Gleb Lukianets
2023-12-12 16:05:22 +00:00
committed by Space Team
parent 0b1c4b836a
commit 64ebec7955
17 changed files with 644 additions and 116 deletions
@@ -13,29 +13,27 @@ import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
internal fun KtNamedFunction.toForeignFunction(): SirForeignFunction = buildForeignFunction {
origin = SirOrigin.ForeignEntity(
AAFunction(this@toForeignFunction)
)
origin = AAFunction(this@toForeignFunction)
}
private fun KtValueParameterSymbol.toSirParam(): KotlinParameter = AAParameter(
private fun KtValueParameterSymbol.toSirParam(): SirKotlinOrigin.Parameter = AAParameter(
name = name.toString(),
type = AAKotlinType(name = returnType.toString())
)
private class AAFunction(
private val originalFunction: KtNamedFunction
) : KotlinFunction {
) : SirKotlinOrigin.Function {
override val fqName: List<String>
get() = originalFunction.fqName?.pathSegments()?.toListString() ?: emptyList()
override val parameters: List<KotlinParameter>
override val parameters: List<SirKotlinOrigin.Parameter>
get() = analyze(originalFunction) {
val function = originalFunction.getFunctionLikeSymbol()
function.valueParameters.map { it.toSirParam() }
}
override val returnType: KotlinType
override val returnType: SirKotlinOrigin.Type
get() = analyze(originalFunction) {
val function = originalFunction.getFunctionLikeSymbol()
AAKotlinType(name = function.returnType.toString())
@@ -44,11 +42,11 @@ private class AAFunction(
}
private data class AAParameter(
override val name: String,
override val type: KotlinType
) : KotlinParameter
override val type: SirKotlinOrigin.Type
) : SirKotlinOrigin.Parameter
private data class AAKotlinType(
override val name: String
) : KotlinType
) : SirKotlinOrigin.Type
private fun List<Name>.toListString() = map { it.asString() }
@@ -10,13 +10,11 @@ import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBase
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
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.SirForeignFunction
import org.jetbrains.kotlin.sir.KotlinFunction
import org.jetbrains.kotlin.sir.SirKotlinOrigin
import org.jetbrains.kotlin.sir.SirOrigin
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.services.TestModuleStructure
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
@@ -50,7 +48,7 @@ abstract class AbstractKotlinSirContextTestBase : AbstractAnalysisApiBasedTest()
module.declarations
.filterIsInstance<SirForeignFunction>()
.forEach {
val function = (it.origin as SirOrigin.ForeignEntity).entity as KotlinFunction
val function = it.origin as SirKotlinOrigin.Function
appendLine("${function.fqName}")
}
}
@@ -0,0 +1,88 @@
/*
* 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
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildEnum
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.sir.visitors.SirTransformer
/**
* Pass that for every occurring declaration in package x.y.z generates a mirroring type scope and puts it there.
* Right now, enums without cases are used for namespace simulation.
*/
public class SirInflatePackagesPass : SirPass<SirModule, Unit, SirModule> {
private data class Namespace(
val elements: MutableList<SirDeclaration> = mutableListOf(),
val children: MutableMap<String, Namespace> = mutableMapOf(),
) {
fun <R> reduce(transform: (List<String>, List<SirDeclaration>, List<R>) -> R): R {
fun reduceFrom(
node: Namespace,
rootPath: List<String>,
transform: (List<String>, List<SirDeclaration>, List<R>) -> R,
): R = transform(
rootPath,
node.elements,
node.children.map { reduceFrom(it.value, rootPath + it.key, transform) }
)
return reduceFrom(this, listOf(""), transform)
}
fun getOrCreate(path: List<String>): Namespace {
if (path.isEmpty()) {
return this
}
val key = path.first()
val next = children.getOrPut(key) { Namespace() }
return next.getOrCreate(path.drop(1))
}
}
private class Context(val root: Namespace = Namespace())
private object Transformer : SirTransformer<Context>() {
override fun <E : SirElement> transformElement(element: E, data: Context): E = element
override fun transformModule(module: SirModule, data: Context): SirModule = buildModule {
name = module.name
for (declaration in module.declarations) {
if (declaration is SirForeignDeclaration) {
val origin = declaration.origin
if (origin is SirOrigin.Foreign) {
// FIXME: for now we assume everything before the last dot is a package name.
// This should change as we add type declarations into the mix
val path = origin.path.dropLast(1)
data.root.getOrCreate(path).elements.add(declaration)
continue
}
}
declarations += declaration
}
val additions = data.root.reduce { path, declarations, children ->
buildEnum {
origin = SirOrigin.Namespace(path.drop(1))
name = path.last()
this.declarations += children
this.declarations += declarations
}
}
declarations += additions.declarations
}.also(SirDeclarationContainer::fixParents)
}
public override fun run(element: SirModule, data: Unit): SirModule = element.transform(Transformer, Context())
}
private fun SirDeclarationContainer.fixParents() = declarations
.onEach { it.parent = this }
.filterIsInstance<SirDeclarationContainer>()
.forEach(SirDeclarationContainer::fixParents)
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.sir.SirElement
* Swift IR is supposed to be transformed by a series of passes.
* This is a base interface that all such passes should implement.
*/
public interface SirPass<out R, in T> {
public interface SirPass<in E : SirElement, in T, out R> {
/**
* Executes the pass over the given [SirElement].
@@ -20,5 +20,7 @@ public interface SirPass<out R, in T> {
* @param data Additional data that is required to run the pass.
* @return The result of the pass.
*/
public fun run(element: SirElement, data: T): R
}
public fun run(element: E, data: T): R
}
public fun <E : SirElement, R> SirPass<E, Unit, R>.run(element: E): R = this.run(element, Unit)
@@ -8,7 +8,6 @@ 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.SirTransformerVoid
import org.jetbrains.sir.passes.SirPass
@@ -22,7 +21,7 @@ import java.lang.IllegalStateException
* or `element` does not contain origin of type `SirOrigin.KotlinEntity.Function`,
* returns original element.
*/
public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothing?> {
public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothing?, SirElement> {
private class Transformer : SirTransformerVoid() {
override fun <E : SirElement> transformElement(element: E): E {
@@ -31,7 +30,7 @@ public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothi
}
override fun transformForeignFunction(function: SirForeignFunction): SirDeclaration {
val kotlinOrigin = (function.origin as? SirOrigin.ForeignEntity)?.entity as? KotlinFunction
val kotlinOrigin = function.origin as? SirKotlinOrigin.Function
?: return function
return buildFunction {
origin = function.origin
@@ -49,12 +48,12 @@ public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothi
override fun run(element: SirElement, data: Nothing?): SirElement = element.transform(Transformer())
}
private fun KotlinParameter.toSir(): SirParameter = SirParameter(
private fun SirKotlinOrigin.Parameter.toSir(): SirParameter = SirParameter(
argumentName = name,
type = type.toSir(),
)
private fun KotlinType.toSir(): SirType = SirNominalType(
private fun SirKotlinOrigin.Type.toSir(): SirType = SirNominalType(
type = when (this.name) {
BYTE -> SirSwiftModule.int8
SHORT -> SirSwiftModule.int16
@@ -0,0 +1,214 @@
/*
* 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
import org.jetbrains.kotlin.sir.SirModule
import org.jetbrains.kotlin.sir.SirOrigin
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.builder.buildStruct
import org.jetbrains.kotlin.sir.util.SirComparator
import org.jetbrains.kotlin.sir.util.SirPrinter
import org.jetbrains.sir.passes.SirInflatePackagesPass
import org.jetbrains.sir.passes.run
import kotlin.test.Test
class SirPackageInflaterTests {
@Test
fun `should pass on empty module`() {
fun buildModule(): SirModule = buildModule {
name = "Root"
}
val actual = buildModule()
val expected = buildModule()
val pass = SirInflatePackagesPass()
pass.run(actual)
assertEqual(expected, actual)
}
@Test
fun `should collect entities into a single flat namespace`() {
val original = buildModule {
name = "Root"
declarations += listOf(
makeFunction("com.foo"),
makeFunction("com.bar"),
makeFunction("com.baz"),
)
}
val expected = buildModule {
name = "Root"
declarations += buildEnum {
name = "com"
origin = SirOrigin.Namespace(path = listOf("com"))
declarations += listOf(
makeFunction("com.foo"),
makeFunction("com.bar"),
makeFunction("com.baz"),
)
}
}
val pass = SirInflatePackagesPass()
val actual = pass.run(original)
assertEqual(expected, actual)
}
@Test
fun `should leave other declarations alone`() {
val original = buildModule {
name = "Root"
declarations += listOf(
buildStruct { name = "Orphan" },
makeFunction("com.foo"),
makeFunction("com.bar"),
)
}
val expected = buildModule {
name = "Root"
declarations += listOf(
buildStruct { name = "Orphan" },
buildEnum {
name = "com"
origin = SirOrigin.Namespace(path = listOf("com"))
declarations += listOf(
makeFunction("com.foo"),
makeFunction("com.bar"),
)
},
)
}
val pass = SirInflatePackagesPass()
val actual = pass.run(original)
assertEqual(expected, actual)
}
@Test
fun `should collect entities into multiple namespaces`() {
val original = buildModule {
name = "Root"
declarations += listOf(
makeFunction("com.foo"),
makeFunction("org.bar"),
makeFunction("com.baz"),
)
}
val expected = buildModule {
name = "Root"
declarations += buildEnum {
name = "com"
origin = SirOrigin.Namespace(path = listOf("com"))
declarations += listOf(
makeFunction("com.foo"),
makeFunction("com.baz"),
)
}
declarations += buildEnum {
name = "org"
origin = SirOrigin.Namespace(path = listOf("org"))
declarations += listOf(
makeFunction("org.bar"),
)
}
}
val pass = SirInflatePackagesPass()
val actual = pass.run(original)
assertEqual(expected, actual)
}
@Test
fun `should collect entities into multiple nested namespaces`() {
val original = buildModule {
name = "Root"
declarations += listOf(
makeFunction("orphan"),
makeFunction("com.foo"),
makeFunction("org.bar"),
makeFunction("com.baz"),
makeFunction("org.jetbrains.baz"),
makeFunction("org.jetbrains.mascots.kotlin.kodee"),
)
}
val expected = buildModule {
name = "Root"
declarations += listOf(
makeFunction("orphan"),
buildEnum {
name = "com"
origin = SirOrigin.Namespace(path = listOf("com"))
declarations += listOf(
makeFunction("com.foo"),
makeFunction("com.baz"),
)
},
buildEnum {
name = "org"
origin = SirOrigin.Namespace(path = listOf("org"))
declarations += listOf(
makeFunction("org.bar"),
buildEnum {
name = "jetbrains"
origin = SirOrigin.Namespace(path = listOf("org", "jetbrains"))
declarations += listOf(
makeFunction("org.jetbrains.baz"),
buildEnum {
name = "mascots"
origin = SirOrigin.Namespace(path = listOf("org", "jetbrains", "mascots"))
declarations += buildEnum {
name = "kotlin"
origin = SirOrigin.Namespace(path = listOf("org", "jetbrains", "mascots", "kotlin"))
declarations += makeFunction("org.jetbrains.mascots.kotlin.kodee")
}
}
)
}
)
}
)
}
val pass = SirInflatePackagesPass()
val actual = pass.run(original)
assertEqual(expected, actual)
}
}
private fun makeFunction(fqName: String) = buildForeignFunction {
val path = fqName.split(".")
assert(path.isNotEmpty())
origin = SirOrigin.Foreign.Unknown(path)
}
private fun assertEqual(expected: SirModule, actual: SirModule) {
assert(SirComparator(options = setOf(SirComparator.Options.COMPARE_ORIGINS)).areEqual(expected, actual)) {
"\nExpected:\n\n${SirPrinter.toString(expected)}\n\nActual:\n\n${SirPrinter.toString(actual)}"
}
}
@@ -5,7 +5,10 @@
package org.jetbrains.kotlin.sir.passes
import org.jetbrains.kotlin.sir.*
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.buildForeignFunction
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.sir.constants.*
@@ -26,12 +29,10 @@ class SirPassTests {
name = "demo"
}
val mySirElement = buildForeignFunction {
origin = SirOrigin.ForeignEntity(
MockFunction(
fqName = listOf("foo"),
parameters = emptyList(),
returnType = MockKotlinType(BOOLEAN),
)
origin = MockFunction(
fqName = listOf("foo"),
parameters = emptyList(),
returnType = MockKotlinType(BOOLEAN),
)
visibility = SirVisibility.PUBLIC
}
@@ -55,41 +56,39 @@ class SirPassTests {
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)
)
origin = MockFunction(
fqName = listOf("foo"),
parameters = listOf(
MockParameter(
name = "arg1",
type = MockKotlinType(name = BYTE)
),
returnType = 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
}
@@ -1,23 +0,0 @@
/*
* 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
}
@@ -8,4 +8,22 @@ package org.jetbrains.kotlin.sir
class SirEnumCase(
val name: String,
val parameters: List<SirParameter>
)
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other != null && this::class != other::class) return false
other as SirEnumCase
if (name != other.name) return false
if (parameters != other.parameters) return false
return true
}
override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + parameters.hashCode()
return result
}
}
@@ -0,0 +1,27 @@
/*
* 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 SirKotlinOrigin : SirOrigin.Foreign {
val fqName: List<String>
override val path: List<String>
get() = fqName
interface Function : SirKotlinOrigin {
val parameters: List<Parameter>
val returnType: Type
}
interface Parameter {
val name: String
val type: Type
}
interface Type {
val name: String
}
}
@@ -10,11 +10,18 @@ sealed interface SirOrigin {
data class ExternallyDefined(val name: String) : Synthetic
data class ForeignEntity(val entity: KotlinEntity): SirOrigin
data class Namespace(val path: List<String>) : Synthetic
sealed interface Foreign : SirOrigin {
val path: List<String>
/** Value for nodes of origin unrepresentable or non-viable yet known to be foreign. */
data class Unknown(override val path: List<String> = emptyList()) : Foreign
}
/**
* Value for nodes of unknown or non-viable origin
* (e.g. objects created in/for tests)
*/
data object Unknown : SirOrigin
}
}
@@ -5,9 +5,29 @@
package org.jetbrains.kotlin.sir
data class SirParameter(
class SirParameter(
val argumentName: String? = null, // external function parameter (argument) name
val parameterName: String? = null, // internal function parameter name
val type: SirType,
/* TODO: val defaultValue: Expression? = null, */
)
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other != null && this::class != other::class) return false
other as SirParameter
if (argumentName != other.argumentName) return false
if (parameterName != other.parameterName) return false
if (type != other.type) return false
return true
}
override fun hashCode(): Int {
var result = argumentName?.hashCode() ?: 0
result = 31 * result + (parameterName?.hashCode() ?: 0)
result = 31 * result + type.hashCode()
return result
}
}
@@ -13,7 +13,9 @@ class SirNominalType(
) : SirType {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SirNominalType) return false
if (other != null && this::class != other::class) return false
other as SirNominalType
if (type != other.type) return false
if (parent != other.parent) return false
@@ -30,4 +32,14 @@ class SirNominalType(
class SirExistentialType(
// TODO: Protocols. For now, only `any Any` is supported
) : SirType
) : SirType {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other != null && this::class != other::class) return false
return true
}
override fun hashCode(): Int {
return this::class.hashCode()
}
}
@@ -0,0 +1,97 @@
/*
* 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.util
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.visitors.SirVisitor
class SirComparator(val options: Set<Options> = emptySet()) : SirVisitor<Boolean, SirElement>() {
enum class Options {
POSITIONAL,
COMPARE_ORIGINS,
}
fun areEqual(lhs: SirElement, rhs: SirElement): Boolean = lhs.accept(this, rhs)
override fun visitElement(element: SirElement, data: SirElement): Boolean {
error("Comparison of ${element::class.simpleName} is unsupported")
}
override fun visitModule(module: SirModule, data: SirElement): Boolean {
return data is SirModule &&
module.name == data.name &&
visitDeclarationContainer(module, data)
}
override fun visitDeclarationContainer(declarationContainer: SirDeclarationContainer, data: SirElement): Boolean {
return data is SirDeclarationContainer && areEqual(declarationContainer.declarations, data.declarations)
}
override fun visitDeclaration(declaration: SirDeclaration, data: SirElement): Boolean {
return data is SirDeclaration &&
areEqual(data.origin, declaration.origin) &&
data.visibility == declaration.visibility
}
override fun visitEnum(enum: SirEnum, data: SirElement): Boolean {
return data is SirEnum &&
data.name == enum.name &&
data.cases == enum.cases &&
visitDeclaration(enum, data) &&
visitDeclarationContainer(enum, data)
}
override fun visitStruct(struct: SirStruct, data: SirElement): Boolean {
return data is SirStruct &&
data.name == struct.name &&
visitDeclaration(struct, data) &&
visitDeclarationContainer(struct, data)
}
override fun visitFunction(function: SirFunction, data: SirElement): Boolean {
return data is SirFunction &&
data.name == function.name &&
data.parameters == function.parameters &&
data.returnType == function.returnType &&
visitDeclaration(function, data)
}
override fun visitForeignFunction(foreignFunction: SirForeignFunction, data: SirElement): Boolean {
return data is SirForeignFunction &&
areEqual(data.origin, foreignFunction.origin) &&
visitDeclaration(foreignFunction, data)
}
}
private fun SirComparator.areEqual(lhs: List<SirElement>, rhs: List<SirElement>): Boolean {
return options.contains(SirComparator.Options.POSITIONAL) &&
lhs.size == rhs.size &&
lhs.zip(rhs).all { areEqual(it.first, it.second) } ||
areEqualNonPositionally(lhs, rhs)
}
private fun SirComparator.areEqual(lhs: SirOrigin, rhs: SirOrigin): Boolean {
return !options.contains(SirComparator.Options.COMPARE_ORIGINS) || lhs == rhs
}
private fun SirComparator.areEqualNonPositionally(lhs: List<SirElement>, rhs: List<SirElement>): Boolean {
// FIXME: in the worst case scenario, this function is quadratic
if (lhs.size != rhs.size)
return false
val probedIndices = rhs.indices.toMutableSet()
lloop@ for (i in lhs.indices) {
for (j in probedIndices) {
if (areEqual(lhs[i], rhs[j])) {
probedIndices.remove(j)
continue@lloop
}
}
return false
}
return true
}
@@ -0,0 +1,85 @@
/*
* 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.util
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.visitors.SirVisitor
object SirPrinter : SirVisitor<String, Unit>() {
fun toString(element: SirElement): String = element.accept(this, Unit)
override fun visitElement(element: SirElement, data: Unit): String {
return "UNKNOWN<${SirElement::class.simpleName}>($element)"
}
override fun visitModule(element: SirModule, data: Unit): String = render(
element,
listOf(
"name" to element.name
),
element.declarations
)
override fun visitEnum(element: SirEnum, data: Unit): String = render(
element,
listOf(
"origin" to element.origin,
"visibility" to element.visibility,
"name" to element.name,
"cases" to element.cases
),
element.declarations
)
override fun visitStruct(element: SirStruct, data: Unit): String = render(
element,
listOf(
"origin" to element.origin,
"visibility" to element.visibility,
"name" to element.name
),
element.declarations
)
override fun visitFunction(element: SirFunction, data: Unit): String = render(
element,
listOf(
"origin" to element.origin,
"visibility" to element.visibility,
"name" to element.name,
"parameters" to element.parameters,
"returnType" to element.returnType,
)
)
override fun visitForeignFunction(element: SirForeignFunction, data: Unit): String = render(
element,
listOf(
"origin" to element.origin,
"visibility" to element.visibility,
),
)
}
private fun render(
base: SirElement,
attributes: List<Pair<String, Any?>> = emptyList(),
children: List<SirElement> = emptyList(),
): String {
return "${base::class.simpleName}${attributes.renderAsAttributres()} ${children.renderAsChildren()}"
}
private fun List<SirElement>.renderAsChildren(): String {
return this.takeIf { isNotEmpty() }?.joinToString(prefix = "{\n", separator = "\n", postfix = "\n}") {
SirPrinter.toString(it).prependIndent(" ")
} ?: "{}"
}
private fun List<Pair<String, Any?>>.renderAsAttributres(): String {
return this.takeIf { isNotEmpty() }?.joinToString(prefix = "(\n", separator = "\n", postfix = "\n)") {
" ${it.first}: ${it.second}"
} ?: "()"
}
@@ -26,20 +26,9 @@ class SirTest {
private fun produceSwiftElement(): Any {
return buildEnum {
origin = SirOrigin.ForeignEntity(
MockFunction(
fqName = listOf("foo"),
parameters = listOf(
MockParameter(
name = "arg1",
type = MockKotlinType(BYTE),
)
),
returnType = MockKotlinType("kotlin/Byte")
)
)
name = "MyEnum"
origin = SirOrigin.Unknown
name = "name"
visibility = SirVisibility.PUBLIC
}
}
}
}
@@ -5,21 +5,19 @@
package org.jetbrains.kotlin.sir.mock
import org.jetbrains.kotlin.sir.KotlinFunction
import org.jetbrains.kotlin.sir.KotlinType
import org.jetbrains.kotlin.sir.KotlinParameter
import org.jetbrains.kotlin.sir.SirKotlinOrigin
data class MockFunction(
override val fqName: List<String>,
override val parameters: List<KotlinParameter>,
override val returnType: KotlinType,
) : KotlinFunction
override val parameters: List<SirKotlinOrigin.Parameter>,
override val returnType: SirKotlinOrigin.Type,
) : SirKotlinOrigin.Function
data class MockParameter(
override val name: String,
override val type: KotlinType,
) : KotlinParameter
override val type: SirKotlinOrigin.Type,
) : SirKotlinOrigin.Parameter
data class MockKotlinType(
override val name: String
) : KotlinType
) : SirKotlinOrigin.Type