[ObjCExport] Implement initial sorting and ordering for ObjC translation
^KT-65237
This commit is contained in:
committed by
Space Team
parent
27fa3ebfb3
commit
32da1b70f5
+5
-1
@@ -27,7 +27,11 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||
val origin: ObjCExportStubOrigin = getObjCExportStubOrigin()
|
||||
val superProtocols: List<String> = superProtocols()
|
||||
val members: List<ObjCExportStub> = getAllMembers().flatMap { it.translateToObjCExportStubs() }
|
||||
|
||||
val members: List<ObjCExportStub> = getAllMembers()
|
||||
.sortedWith(StableSymbolOrder)
|
||||
.flatMap { it.translateToObjCExportStubs() }
|
||||
|
||||
val categoryName: String? = null
|
||||
val generics: List<ObjCGenericTypeDeclaration> = emptyList()
|
||||
val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList()
|
||||
|
||||
+6
-5
@@ -17,7 +17,11 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun translateToObjCHeader(files: List<KtFile>): ObjCHeader {
|
||||
val declarations = files.flatMap { ktFile -> ktFile.translateToObjCExportStubs() }.toMutableList()
|
||||
val declarations = files
|
||||
.sortedWith(StableFileOrder)
|
||||
.flatMap { file -> file.getFileSymbol().translateToObjCExportStubs() }
|
||||
.toMutableList()
|
||||
|
||||
val classForwardDeclarations = getClassForwardDeclarations(declarations).toMutableSet()
|
||||
val protocolForwardDeclarations = getProtocolForwardDeclarations(declarations)
|
||||
|
||||
@@ -66,14 +70,11 @@ private fun getProtocolForwardDeclarations(declarations: List<ObjCExportStub>) =
|
||||
.flatMap { it.superProtocols }
|
||||
.toSet()
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFile.translateToObjCExportStubs(): List<ObjCExportStub> {
|
||||
return this.getFileSymbol().translateToObjCExportStubs()
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFileSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
|
||||
return listOfNotNull(translateToObjCTopLevelInterfaceFileFacade()) + getFileScope().getClassifierSymbols()
|
||||
.sortedWith(StableClassifierOrder)
|
||||
.flatMap { classifierSymbol -> classifierSymbol.translateToObjCExportStubs() }
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -26,7 +26,9 @@ fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol? {
|
||||
// TODO: Check error type!
|
||||
val name = getObjCClassOrProtocolName()
|
||||
|
||||
val members = getDeclaredMembers().flatMap { it.translateToObjCExportStubs() }
|
||||
val members = getDeclaredMembers()
|
||||
.sortedWith(StableSymbolOrder)
|
||||
.flatMap { it.translateToObjCExportStubs() }
|
||||
|
||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||
|
||||
|
||||
+1
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrPr
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFileSymbol.translateToObjCTopLevelInterfaceFileFacade(): ObjCInterface? {
|
||||
val topLevelCallableStubs = getFileScope().getCallableSymbols()
|
||||
.sortedWith(StableCallableOrder)
|
||||
.flatMap { callableSymbol -> callableSymbol.translateToObjCExportStubs() }
|
||||
.toList()
|
||||
/* If there are no top level functions or properties, we do not need to export a file facade */
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
internal val StableFileOrder: Comparator<KtFile>
|
||||
get() = compareBy<KtFile> { file -> file.packageFqName.asString() }
|
||||
.thenComparing { file -> file.name }
|
||||
|
||||
internal val StablePropertyOrder: Comparator<KtPropertySymbol> = compareBy { it.name }
|
||||
|
||||
internal val StableFunctionOrder: Comparator<KtFunctionSymbol>
|
||||
get() = compareBy(
|
||||
{ it.isConstructor },
|
||||
{ it.name },
|
||||
{ it.valueParameters.size },
|
||||
// TODO NOW! { KonanManglerDesc.run { it.signatureString(false) } }
|
||||
)
|
||||
|
||||
internal val StableClassifierOrder: Comparator<KtClassifierSymbol> =
|
||||
compareBy<KtClassifierSymbol> { classifier ->
|
||||
if (classifier !is KtClassOrObjectSymbol) return@compareBy 0
|
||||
else 2
|
||||
}.thenComparing { classifier ->
|
||||
if (classifier is KtClassLikeSymbol) classifier.classIdIfNonLocal?.toString().orEmpty()
|
||||
else ""
|
||||
}
|
||||
|
||||
internal val StableCallableOrder: Comparator<KtCallableSymbol> = compareBy<KtCallableSymbol> {
|
||||
when (it) {
|
||||
is KtConstructorSymbol -> 0
|
||||
is KtFunctionSymbol -> 1
|
||||
is KtPropertySymbol -> 2
|
||||
else -> 3
|
||||
}
|
||||
}
|
||||
.thenComparing(StablePropertyOrder)
|
||||
.thenComparing(StableFunctionOrder)
|
||||
|
||||
internal val StableSymbolOrder: Comparator<KtSymbol> = compareBy<KtSymbol> { symbol ->
|
||||
when (symbol) {
|
||||
is KtFileSymbol -> 0
|
||||
is KtClassifierSymbol -> 1
|
||||
is KtCallableSymbol -> 2
|
||||
else -> Int.MAX_VALUE
|
||||
}
|
||||
}
|
||||
.thenComparing(StableClassifierOrder)
|
||||
.thenComparing(StableCallableOrder)
|
||||
|
||||
private inline fun <T, reified R> Comparator<T>.thenComparing(comparator: Comparator<R>): Comparator<T> where R : T {
|
||||
return thenComparing { a, b ->
|
||||
if (a is R && b is R) comparator.compare(a, b) else 0
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -36,7 +36,16 @@ import java.nio.file.Files
|
||||
* ```
|
||||
*/
|
||||
interface InlineSourceCodeAnalysis {
|
||||
|
||||
fun createKtFile(@Language("kotlin") sourceCode: String): KtFile
|
||||
|
||||
fun createKtFiles(
|
||||
builder: KtModuleBuilder.() -> Unit,
|
||||
): Map</* File Name */ String, KtFile>
|
||||
|
||||
interface KtModuleBuilder {
|
||||
fun sourceFile(fileName: String, @Language("kotlin") sourceCode: String)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,8 +77,25 @@ class InlineSourceCodeAnalysisExtension : ParameterResolver, AfterEachCallback {
|
||||
*/
|
||||
private class InlineSourceCodeAnalysisImpl(private val tempDir: File) : InlineSourceCodeAnalysis {
|
||||
override fun createKtFile(@Language("kotlin") sourceCode: String): KtFile {
|
||||
return createStandaloneAnalysisApiSession(tempDir, listOf(sourceCode))
|
||||
return createStandaloneAnalysisApiSession(tempDir, mapOf("TestSources.kt" to sourceCode))
|
||||
.modulesWithFiles.entries.single()
|
||||
.value.single() as KtFile
|
||||
}
|
||||
|
||||
override fun createKtFiles(builder: InlineSourceCodeAnalysis.KtModuleBuilder.() -> Unit): Map<String, KtFile> {
|
||||
val sources = KtModuleBuilderImpl().also(builder).sources.toMap()
|
||||
return createStandaloneAnalysisApiSession(tempDir, sources)
|
||||
.modulesWithFiles.entries.single()
|
||||
.value.map { it as KtFile }
|
||||
.associateBy { it.name }
|
||||
}
|
||||
|
||||
private class KtModuleBuilderImpl : InlineSourceCodeAnalysis.KtModuleBuilder {
|
||||
val sources = mutableMapOf<String, String>()
|
||||
|
||||
override fun sourceFile(fileName: String, sourceCode: String) {
|
||||
check(fileName !in sourceCode)
|
||||
sources[fileName] = sourceCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -23,14 +23,14 @@ import kotlin.io.path.Path
|
||||
*/
|
||||
fun createStandaloneAnalysisApiSession(
|
||||
tempDir: File,
|
||||
kotlinSources: List<String>,
|
||||
kotlinSources: Map</* File Name */ String, /* Source Code */ String>,
|
||||
): StandaloneAnalysisAPISession {
|
||||
val testModuleRoot = tempDir.resolve("testModule")
|
||||
testModuleRoot.mkdirs()
|
||||
|
||||
kotlinSources.forEachIndexed { index, kotlinSource ->
|
||||
testModuleRoot.resolve("TestSources$index.kt").apply {
|
||||
writeText(kotlinSource)
|
||||
kotlinSources.forEach { (fileName, sourceCode) ->
|
||||
testModuleRoot.resolve(fileName).apply {
|
||||
writeText(sourceCode)
|
||||
}
|
||||
}
|
||||
return createStandaloneAnalysisApiSession(listOf(testModuleRoot))
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.objcexport.tests
|
||||
|
||||
import org.jetbrains.kotlin.objcexport.StableFileOrder
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
|
||||
class TranslationOrderTest(
|
||||
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
|
||||
) {
|
||||
|
||||
@Test
|
||||
fun `test - StableFileOrder - sort by packageName`() {
|
||||
val files = inlineSourceCodeAnalysis.createKtFiles {
|
||||
sourceFile("A.kt", "package com.a")
|
||||
sourceFile("B.kt", "package com.b")
|
||||
sourceFile("C.kt", "package com.c")
|
||||
}
|
||||
|
||||
val unsorted = listOf(files.getValue("C.kt"), files.getValue("B.kt"), files.getValue("A.kt"))
|
||||
val sorted = listOf(files.getValue("A.kt"), files.getValue("B.kt"), files.getValue("C.kt"))
|
||||
|
||||
assertNotEquals(sorted, unsorted)
|
||||
assertEquals(sorted, unsorted.sortedWith(StableFileOrder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - StableFileOrder - sort by packageName then FileName`() {
|
||||
val files = inlineSourceCodeAnalysis.createKtFiles {
|
||||
sourceFile("A.kt", "package com.a")
|
||||
sourceFile("B1.kt", "package com.b")
|
||||
sourceFile("B2.kt", "package com.b")
|
||||
}
|
||||
|
||||
val unsorted = listOf(files.getValue("B2.kt"), files.getValue("A.kt"), files.getValue("B1.kt"))
|
||||
val sorted = listOf(files.getValue("A.kt"), files.getValue("B1.kt"), files.getValue("B2.kt"))
|
||||
|
||||
assertNotEquals(sorted, unsorted)
|
||||
assertEquals(sorted, unsorted.sortedWith(StableFileOrder))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user