[Swift Export]: KT-63280: Package inflation
Merge-request: KT-MR-13331 Merged-by: Gleb Lukianets <Gleb.Lukianets@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
0b1c4b836a
commit
64ebec7955
+8
-10
@@ -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() }
|
||||
|
||||
+2
-4
@@ -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)
|
||||
+4
-5
@@ -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
|
||||
|
||||
+214
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user