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 index 249bfc33aa6..b8e34014bed 100644 --- 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 @@ -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 get() = originalFunction.fqName?.pathSegments()?.toListString() ?: emptyList() - override val parameters: List + override val parameters: List 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.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 dbf60cf6c71..17114134a0e 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 @@ -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() .forEach { - val function = (it.origin as SirOrigin.ForeignEntity).entity as KotlinFunction + val function = it.origin as SirKotlinOrigin.Function appendLine("${function.fqName}") } } diff --git a/native/swift/sir-passes/src/org/jetbrains/sir/passes/SirInflatePackagesPass.kt b/native/swift/sir-passes/src/org/jetbrains/sir/passes/SirInflatePackagesPass.kt new file mode 100644 index 00000000000..2990eef69dd --- /dev/null +++ b/native/swift/sir-passes/src/org/jetbrains/sir/passes/SirInflatePackagesPass.kt @@ -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 { + private data class Namespace( + val elements: MutableList = mutableListOf(), + val children: MutableMap = mutableMapOf(), + ) { + fun reduce(transform: (List, List, List) -> R): R { + fun reduceFrom( + node: Namespace, + rootPath: List, + transform: (List, List, List) -> 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): 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() { + override fun 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() + .forEach(SirDeclarationContainer::fixParents) \ No newline at end of file diff --git a/native/swift/sir-passes/src/org/jetbrains/sir/passes/SirPass.kt b/native/swift/sir-passes/src/org/jetbrains/sir/passes/SirPass.kt index 06172d196eb..44fafa6b241 100644 --- a/native/swift/sir-passes/src/org/jetbrains/sir/passes/SirPass.kt +++ b/native/swift/sir-passes/src/org/jetbrains/sir/passes/SirPass.kt @@ -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 { +public interface SirPass { /** * Executes the pass over the given [SirElement]. @@ -20,5 +20,7 @@ public interface SirPass { * @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 -} \ No newline at end of file + public fun run(element: E, data: T): R +} + +public fun SirPass.run(element: E): R = this.run(element, Unit) \ No newline at end of file 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 index 3a2cf7235aa..45260d4b0b9 100644 --- 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 @@ -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 { +public class ForeignIntoSwiftFunctionTranslationPass : SirPass { private class Transformer : SirTransformerVoid() { override fun transformElement(element: E): E { @@ -31,7 +30,7 @@ public class ForeignIntoSwiftFunctionTranslationPass : SirPass SirSwiftModule.int8 SHORT -> SirSwiftModule.int16 diff --git a/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/SirPackageInflaterTests.kt b/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/SirPackageInflaterTests.kt new file mode 100644 index 00000000000..878899ad027 --- /dev/null +++ b/native/swift/sir-passes/tests/org/jetbrains/kotlin/sir/passes/SirPackageInflaterTests.kt @@ -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)}" + } +} \ No newline at end of file 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 dc735ecea0d..b4a55d9c848 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 @@ -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 } diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/KotlinEntity.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/KotlinEntity.kt deleted file mode 100644 index 62d86fda1f0..00000000000 --- a/native/swift/sir/src/org/jetbrains/kotlin/sir/KotlinEntity.kt +++ /dev/null @@ -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 - 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/SirEnumCase.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirEnumCase.kt index 859d21518d6..c16eeb378ff 100644 --- a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirEnumCase.kt +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirEnumCase.kt @@ -8,4 +8,22 @@ package org.jetbrains.kotlin.sir class SirEnumCase( val name: String, val parameters: List -) \ No newline at end of file +) { + 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 + } +} \ No newline at end of file diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirKotlinOrigin.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirKotlinOrigin.kt new file mode 100644 index 00000000000..97041271792 --- /dev/null +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirKotlinOrigin.kt @@ -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 + + override val path: List + get() = fqName + + interface Function : SirKotlinOrigin { + val parameters: List + val returnType: Type + } + + interface Parameter { + val name: String + val type: Type + } + + interface Type { + val name: String + } +} \ No newline at end of file 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 e984ad00f6f..52369537fe4 100644 --- a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirOrigin.kt +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirOrigin.kt @@ -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) : Synthetic + + sealed interface Foreign : SirOrigin { + val path: List + + /** Value for nodes of origin unrepresentable or non-viable yet known to be foreign. */ + data class Unknown(override val path: List = emptyList()) : Foreign + } /** * 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 diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirParameter.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirParameter.kt index 785b88ec30b..2c144f604d3 100644 --- a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirParameter.kt +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirParameter.kt @@ -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, */ -) \ No newline at end of file +) { + 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 + } +} \ No newline at end of file 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 f1a920341c2..387a15f9c89 100644 --- a/native/swift/sir/src/org/jetbrains/kotlin/sir/SirType.kt +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/SirType.kt @@ -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() + } +} diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/util/SirComparator.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/util/SirComparator.kt new file mode 100644 index 00000000000..35f1c23999e --- /dev/null +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/util/SirComparator.kt @@ -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 = emptySet()) : SirVisitor() { + 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, rhs: List): 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, rhs: List): 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 +} \ No newline at end of file diff --git a/native/swift/sir/src/org/jetbrains/kotlin/sir/util/SirPrinter.kt b/native/swift/sir/src/org/jetbrains/kotlin/sir/util/SirPrinter.kt new file mode 100644 index 00000000000..5a17163b126 --- /dev/null +++ b/native/swift/sir/src/org/jetbrains/kotlin/sir/util/SirPrinter.kt @@ -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() { + 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> = emptyList(), + children: List = emptyList(), +): String { + return "${base::class.simpleName}${attributes.renderAsAttributres()} ${children.renderAsChildren()}" +} + +private fun List.renderAsChildren(): String { + return this.takeIf { isNotEmpty() }?.joinToString(prefix = "{\n", separator = "\n", postfix = "\n}") { + SirPrinter.toString(it).prependIndent(" ") + } ?: "{}" +} + +private fun List>.renderAsAttributres(): String { + return this.takeIf { isNotEmpty() }?.joinToString(prefix = "(\n", separator = "\n", postfix = "\n)") { + " ${it.first}: ${it.second}" + } ?: "()" +} \ No newline at end of file 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 fccc3a59b24..739985cd82a 100644 --- a/native/swift/sir/tests/org/jetbrains/kotlin/sir/SirTest.kt +++ b/native/swift/sir/tests/org/jetbrains/kotlin/sir/SirTest.kt @@ -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 } } -} \ No newline at end of file +} 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 index eac034efd0e..4d357d83c70 100644 --- a/native/swift/sir/tests/org/jetbrains/kotlin/sir/mock/KotlinEntity.kt +++ b/native/swift/sir/tests/org/jetbrains/kotlin/sir/mock/KotlinEntity.kt @@ -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, - override val parameters: List, - override val returnType: KotlinType, -) : KotlinFunction + override val parameters: List, + 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