[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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user