[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
|
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
|
||||||
|
|
||||||
internal fun KtNamedFunction.toForeignFunction(): SirForeignFunction = buildForeignFunction {
|
internal fun KtNamedFunction.toForeignFunction(): SirForeignFunction = buildForeignFunction {
|
||||||
origin = SirOrigin.ForeignEntity(
|
origin = AAFunction(this@toForeignFunction)
|
||||||
AAFunction(this@toForeignFunction)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtValueParameterSymbol.toSirParam(): KotlinParameter = AAParameter(
|
private fun KtValueParameterSymbol.toSirParam(): SirKotlinOrigin.Parameter = AAParameter(
|
||||||
name = name.toString(),
|
name = name.toString(),
|
||||||
type = AAKotlinType(name = returnType.toString())
|
type = AAKotlinType(name = returnType.toString())
|
||||||
)
|
)
|
||||||
|
|
||||||
private class AAFunction(
|
private class AAFunction(
|
||||||
private val originalFunction: KtNamedFunction
|
private val originalFunction: KtNamedFunction
|
||||||
) : KotlinFunction {
|
) : SirKotlinOrigin.Function {
|
||||||
override val fqName: List<String>
|
override val fqName: List<String>
|
||||||
get() = originalFunction.fqName?.pathSegments()?.toListString() ?: emptyList()
|
get() = originalFunction.fqName?.pathSegments()?.toListString() ?: emptyList()
|
||||||
|
|
||||||
override val parameters: List<KotlinParameter>
|
override val parameters: List<SirKotlinOrigin.Parameter>
|
||||||
get() = analyze(originalFunction) {
|
get() = analyze(originalFunction) {
|
||||||
val function = originalFunction.getFunctionLikeSymbol()
|
val function = originalFunction.getFunctionLikeSymbol()
|
||||||
function.valueParameters.map { it.toSirParam() }
|
function.valueParameters.map { it.toSirParam() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val returnType: KotlinType
|
override val returnType: SirKotlinOrigin.Type
|
||||||
get() = analyze(originalFunction) {
|
get() = analyze(originalFunction) {
|
||||||
val function = originalFunction.getFunctionLikeSymbol()
|
val function = originalFunction.getFunctionLikeSymbol()
|
||||||
AAKotlinType(name = function.returnType.toString())
|
AAKotlinType(name = function.returnType.toString())
|
||||||
@@ -44,11 +42,11 @@ private class AAFunction(
|
|||||||
}
|
}
|
||||||
private data class AAParameter(
|
private data class AAParameter(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val type: KotlinType
|
override val type: SirKotlinOrigin.Type
|
||||||
) : KotlinParameter
|
) : SirKotlinOrigin.Parameter
|
||||||
|
|
||||||
private data class AAKotlinType(
|
private data class AAKotlinType(
|
||||||
override val name: String
|
override val name: String
|
||||||
) : KotlinType
|
) : SirKotlinOrigin.Type
|
||||||
|
|
||||||
private fun List<Name>.toListString() = map { it.asString() }
|
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.project.structure.ktModuleProvider
|
||||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.*
|
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.*
|
||||||
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind
|
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.psi.KtFile
|
||||||
import org.jetbrains.kotlin.sir.SirForeignFunction
|
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.SirOrigin
|
||||||
import org.jetbrains.kotlin.sir.builder.buildModule
|
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.TestModuleStructure
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
import org.jetbrains.kotlin.test.services.assertions
|
import org.jetbrains.kotlin.test.services.assertions
|
||||||
@@ -50,7 +48,7 @@ abstract class AbstractKotlinSirContextTestBase : AbstractAnalysisApiBasedTest()
|
|||||||
module.declarations
|
module.declarations
|
||||||
.filterIsInstance<SirForeignFunction>()
|
.filterIsInstance<SirForeignFunction>()
|
||||||
.forEach {
|
.forEach {
|
||||||
val function = (it.origin as SirOrigin.ForeignEntity).entity as KotlinFunction
|
val function = it.origin as SirKotlinOrigin.Function
|
||||||
appendLine("${function.fqName}")
|
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.
|
* Swift IR is supposed to be transformed by a series of passes.
|
||||||
* This is a base interface that all such passes should implement.
|
* 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].
|
* 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.
|
* @param data Additional data that is required to run the pass.
|
||||||
* @return The result of 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.*
|
||||||
import org.jetbrains.kotlin.sir.builder.buildFunction
|
import org.jetbrains.kotlin.sir.builder.buildFunction
|
||||||
import org.jetbrains.kotlin.sir.util.SirSwiftModule
|
import org.jetbrains.kotlin.sir.util.SirSwiftModule
|
||||||
import org.jetbrains.kotlin.sir.KotlinFunction
|
|
||||||
import org.jetbrains.kotlin.sir.constants.*
|
import org.jetbrains.kotlin.sir.constants.*
|
||||||
import org.jetbrains.kotlin.sir.visitors.SirTransformerVoid
|
import org.jetbrains.kotlin.sir.visitors.SirTransformerVoid
|
||||||
import org.jetbrains.sir.passes.SirPass
|
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`,
|
* or `element` does not contain origin of type `SirOrigin.KotlinEntity.Function`,
|
||||||
* returns original element.
|
* returns original element.
|
||||||
*/
|
*/
|
||||||
public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothing?> {
|
public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothing?, SirElement> {
|
||||||
|
|
||||||
private class Transformer : SirTransformerVoid() {
|
private class Transformer : SirTransformerVoid() {
|
||||||
override fun <E : SirElement> transformElement(element: E): E {
|
override fun <E : SirElement> transformElement(element: E): E {
|
||||||
@@ -31,7 +30,7 @@ public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun transformForeignFunction(function: SirForeignFunction): SirDeclaration {
|
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 function
|
||||||
return buildFunction {
|
return buildFunction {
|
||||||
origin = function.origin
|
origin = function.origin
|
||||||
@@ -49,12 +48,12 @@ public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothi
|
|||||||
override fun run(element: SirElement, data: Nothing?): SirElement = element.transform(Transformer())
|
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,
|
argumentName = name,
|
||||||
type = type.toSir(),
|
type = type.toSir(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun KotlinType.toSir(): SirType = SirNominalType(
|
private fun SirKotlinOrigin.Type.toSir(): SirType = SirNominalType(
|
||||||
type = when (this.name) {
|
type = when (this.name) {
|
||||||
BYTE -> SirSwiftModule.int8
|
BYTE -> SirSwiftModule.int8
|
||||||
SHORT -> SirSwiftModule.int16
|
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
|
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.buildForeignFunction
|
||||||
import org.jetbrains.kotlin.sir.builder.buildModule
|
import org.jetbrains.kotlin.sir.builder.buildModule
|
||||||
import org.jetbrains.kotlin.sir.constants.*
|
import org.jetbrains.kotlin.sir.constants.*
|
||||||
@@ -26,12 +29,10 @@ class SirPassTests {
|
|||||||
name = "demo"
|
name = "demo"
|
||||||
}
|
}
|
||||||
val mySirElement = buildForeignFunction {
|
val mySirElement = buildForeignFunction {
|
||||||
origin = SirOrigin.ForeignEntity(
|
origin = MockFunction(
|
||||||
MockFunction(
|
fqName = listOf("foo"),
|
||||||
fqName = listOf("foo"),
|
parameters = emptyList(),
|
||||||
parameters = emptyList(),
|
returnType = MockKotlinType(BOOLEAN),
|
||||||
returnType = MockKotlinType(BOOLEAN),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
visibility = SirVisibility.PUBLIC
|
visibility = SirVisibility.PUBLIC
|
||||||
}
|
}
|
||||||
@@ -55,41 +56,39 @@ class SirPassTests {
|
|||||||
name = "demo"
|
name = "demo"
|
||||||
}
|
}
|
||||||
val mySirElement = buildForeignFunction {
|
val mySirElement = buildForeignFunction {
|
||||||
origin = SirOrigin.ForeignEntity(
|
origin = MockFunction(
|
||||||
MockFunction(
|
fqName = listOf("foo"),
|
||||||
fqName = listOf("foo"),
|
parameters = listOf(
|
||||||
parameters = listOf(
|
MockParameter(
|
||||||
MockParameter(
|
name = "arg1",
|
||||||
name = "arg1",
|
type = MockKotlinType(name = BYTE)
|
||||||
type = MockKotlinType(name = BYTE)
|
|
||||||
),
|
|
||||||
MockParameter(
|
|
||||||
name = "arg2",
|
|
||||||
type = MockKotlinType(name = SHORT)
|
|
||||||
),
|
|
||||||
MockParameter(
|
|
||||||
name = "arg3",
|
|
||||||
type = MockKotlinType(name = INT)
|
|
||||||
),
|
|
||||||
MockParameter(
|
|
||||||
name = "arg4",
|
|
||||||
type = MockKotlinType(name = LONG)
|
|
||||||
),
|
|
||||||
MockParameter(
|
|
||||||
name = "arg5",
|
|
||||||
type = MockKotlinType(name = DOUBLE)
|
|
||||||
),
|
|
||||||
MockParameter(
|
|
||||||
name = "arg6",
|
|
||||||
type = MockKotlinType(name = FLOAT)
|
|
||||||
),
|
|
||||||
MockParameter(
|
|
||||||
name = "arg7",
|
|
||||||
type = MockKotlinType(name = BOOLEAN)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
returnType = MockKotlinType(name = BYTE),
|
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
|
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(
|
class SirEnumCase(
|
||||||
val name: String,
|
val name: String,
|
||||||
val parameters: List<SirParameter>
|
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,7 +10,14 @@ sealed interface SirOrigin {
|
|||||||
|
|
||||||
data class ExternallyDefined(val name: String) : Synthetic
|
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
|
* Value for nodes of unknown or non-viable origin
|
||||||
|
|||||||
@@ -5,9 +5,29 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.sir
|
package org.jetbrains.kotlin.sir
|
||||||
|
|
||||||
data class SirParameter(
|
class SirParameter(
|
||||||
val argumentName: String? = null, // external function parameter (argument) name
|
val argumentName: String? = null, // external function parameter (argument) name
|
||||||
val parameterName: String? = null, // internal function parameter name
|
val parameterName: String? = null, // internal function parameter name
|
||||||
val type: SirType,
|
val type: SirType,
|
||||||
/* TODO: val defaultValue: Expression? = null, */
|
/* 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 {
|
) : SirType {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
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 (type != other.type) return false
|
||||||
if (parent != other.parent) return false
|
if (parent != other.parent) return false
|
||||||
@@ -30,4 +32,14 @@ class SirNominalType(
|
|||||||
|
|
||||||
class SirExistentialType(
|
class SirExistentialType(
|
||||||
// TODO: Protocols. For now, only `any Any` is supported
|
// 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,19 +26,8 @@ class SirTest {
|
|||||||
|
|
||||||
private fun produceSwiftElement(): Any {
|
private fun produceSwiftElement(): Any {
|
||||||
return buildEnum {
|
return buildEnum {
|
||||||
origin = SirOrigin.ForeignEntity(
|
origin = SirOrigin.Unknown
|
||||||
MockFunction(
|
name = "name"
|
||||||
fqName = listOf("foo"),
|
|
||||||
parameters = listOf(
|
|
||||||
MockParameter(
|
|
||||||
name = "arg1",
|
|
||||||
type = MockKotlinType(BYTE),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
returnType = MockKotlinType("kotlin/Byte")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
name = "MyEnum"
|
|
||||||
visibility = SirVisibility.PUBLIC
|
visibility = SirVisibility.PUBLIC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.sir.mock
|
package org.jetbrains.kotlin.sir.mock
|
||||||
|
|
||||||
import org.jetbrains.kotlin.sir.KotlinFunction
|
import org.jetbrains.kotlin.sir.SirKotlinOrigin
|
||||||
import org.jetbrains.kotlin.sir.KotlinType
|
|
||||||
import org.jetbrains.kotlin.sir.KotlinParameter
|
|
||||||
|
|
||||||
data class MockFunction(
|
data class MockFunction(
|
||||||
override val fqName: List<String>,
|
override val fqName: List<String>,
|
||||||
override val parameters: List<KotlinParameter>,
|
override val parameters: List<SirKotlinOrigin.Parameter>,
|
||||||
override val returnType: KotlinType,
|
override val returnType: SirKotlinOrigin.Type,
|
||||||
) : KotlinFunction
|
) : SirKotlinOrigin.Function
|
||||||
|
|
||||||
data class MockParameter(
|
data class MockParameter(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val type: KotlinType,
|
override val type: SirKotlinOrigin.Type,
|
||||||
) : KotlinParameter
|
) : SirKotlinOrigin.Parameter
|
||||||
|
|
||||||
data class MockKotlinType(
|
data class MockKotlinType(
|
||||||
override val name: String
|
override val name: String
|
||||||
) : KotlinType
|
) : SirKotlinOrigin.Type
|
||||||
|
|||||||
Reference in New Issue
Block a user