Native: support objcExportReportNameCollisions=true binary option
This binary option makes the compiler emit a warning whenever it mangles a name emitted by ObjCExport. ^KT-63153 Fixed
This commit is contained in:
committed by
Space Team
parent
656b61b945
commit
8d20d5b16c
+2
@@ -33,6 +33,8 @@ object BinaryOptions : BinaryOptionRegistry() {
|
||||
|
||||
val objcExportIgnoreInterfaceMethodCollisions by booleanOption()
|
||||
|
||||
val objcExportReportNameCollisions by booleanOption()
|
||||
|
||||
val gc by option<GC>(shortcut = { it.shortcut })
|
||||
|
||||
val gcSchedulerType by option<GCSchedulerType>(hideValue = { it.deprecatedWithReplacement != null })
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportLazyImpl
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportProblemCollector
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.dumpObjCHeader
|
||||
import org.jetbrains.kotlin.container.*
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
@@ -23,7 +24,7 @@ internal fun StorageComponentContainer.initContainer(config: KonanConfig) {
|
||||
useImpl<ObjCExportLazyImpl>()
|
||||
useInstance(object : ObjCExportProblemCollector {
|
||||
override fun reportWarning(text: String) {}
|
||||
override fun reportWarning(method: FunctionDescriptor, text: String) {}
|
||||
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {}
|
||||
override fun reportException(throwable: Throwable) = throw throwable
|
||||
})
|
||||
|
||||
|
||||
+8
-1
@@ -50,17 +50,23 @@ internal fun produceObjCExportInterface(
|
||||
val objcGenerics = config.configuration.getBoolean(KonanConfigKeys.OBJC_GENERICS)
|
||||
val disableSwiftMemberNameMangling = config.configuration.getBoolean(BinaryOptions.objcExportDisableSwiftMemberNameMangling)
|
||||
val ignoreInterfaceMethodCollisions = config.configuration.getBoolean(BinaryOptions.objcExportIgnoreInterfaceMethodCollisions)
|
||||
val reportNameCollisions = config.configuration.getBoolean(BinaryOptions.objcExportReportNameCollisions)
|
||||
|
||||
val problemCollector = ObjCExportHeaderGeneratorImpl.ProblemCollector(context)
|
||||
|
||||
val namer = ObjCExportNamerImpl(
|
||||
moduleDescriptors.toSet(),
|
||||
moduleDescriptor.builtIns,
|
||||
mapper,
|
||||
problemCollector,
|
||||
topLevelNamePrefix,
|
||||
local = false,
|
||||
objcGenerics = objcGenerics,
|
||||
disableSwiftMemberNameMangling = disableSwiftMemberNameMangling,
|
||||
ignoreInterfaceMethodCollisions = ignoreInterfaceMethodCollisions,
|
||||
reportNameCollisions = reportNameCollisions,
|
||||
)
|
||||
val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer, objcGenerics)
|
||||
val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer, problemCollector, objcGenerics)
|
||||
headerGenerator.translateModule()
|
||||
return headerGenerator.buildInterface()
|
||||
}
|
||||
@@ -128,6 +134,7 @@ internal class ObjCExport(
|
||||
setOf(moduleDescriptor),
|
||||
moduleDescriptor.builtIns,
|
||||
mapper,
|
||||
ObjCExportProblemCollector.SILENT,
|
||||
topLevelNamePrefix,
|
||||
local = false
|
||||
)
|
||||
|
||||
+2
-2
@@ -42,12 +42,12 @@ interface ObjCExportTranslator {
|
||||
|
||||
interface ObjCExportProblemCollector {
|
||||
fun reportWarning(text: String)
|
||||
fun reportWarning(method: FunctionDescriptor, text: String)
|
||||
fun reportWarning(declaration: DeclarationDescriptor, text: String)
|
||||
fun reportException(throwable: Throwable)
|
||||
|
||||
object SILENT : ObjCExportProblemCollector {
|
||||
override fun reportWarning(text: String) {}
|
||||
override fun reportWarning(method: FunctionDescriptor, text: String) {}
|
||||
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {}
|
||||
override fun reportException(throwable: Throwable) {}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
|
||||
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageUtil
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
@@ -21,20 +22,21 @@ internal class ObjCExportHeaderGeneratorImpl(
|
||||
moduleDescriptors: List<ModuleDescriptor>,
|
||||
mapper: ObjCExportMapper,
|
||||
namer: ObjCExportNamer,
|
||||
problemCollector: ObjCExportProblemCollector,
|
||||
objcGenerics: Boolean
|
||||
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics, ProblemCollector(context)) {
|
||||
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics, problemCollector) {
|
||||
|
||||
override val shouldExportKDoc = context.shouldExportKDoc()
|
||||
|
||||
private class ProblemCollector(val context: PhaseContext) : ObjCExportProblemCollector {
|
||||
internal class ProblemCollector(val context: PhaseContext) : ObjCExportProblemCollector {
|
||||
override fun reportWarning(text: String) {
|
||||
context.reportCompilationWarning(text)
|
||||
}
|
||||
|
||||
override fun reportWarning(method: FunctionDescriptor, text: String) {
|
||||
val psi = (method as? DeclarationDescriptorWithSource)?.source?.getPsi()
|
||||
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {
|
||||
val psi = (declaration as? DeclarationDescriptorWithSource)?.source?.getPsi()
|
||||
?: return reportWarning(
|
||||
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(method)})"
|
||||
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})"
|
||||
)
|
||||
|
||||
val location = MessageUtil.psiElementToMessageLocation(psi)
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ internal class ObjCExportLazyImpl(
|
||||
|
||||
private val mapper = ObjCExportMapper(deprecationResolver, local = true, configuration.unitSuspendFunctionExport)
|
||||
|
||||
private val namer = ObjCExportNamerImpl(namerConfiguration, builtIns, mapper, local = true)
|
||||
private val namer = ObjCExportNamerImpl(namerConfiguration, builtIns, mapper, problemCollector, local = true)
|
||||
|
||||
private val translator: ObjCExportTranslator = ObjCExportTranslatorImpl(
|
||||
null,
|
||||
|
||||
+22
@@ -58,6 +58,9 @@ interface ObjCExportNamer {
|
||||
get() = false
|
||||
val ignoreInterfaceMethodCollisions: Boolean
|
||||
get() = false
|
||||
|
||||
val reportNameCollisions: Boolean
|
||||
get() = false
|
||||
}
|
||||
|
||||
val topLevelNamePrefix: String
|
||||
@@ -103,6 +106,7 @@ fun createNamer(
|
||||
(exportedDependencies + moduleDescriptor).toSet(),
|
||||
moduleDescriptor.builtIns,
|
||||
ObjCExportMapper(local = true, unitSuspendFunctionExport = UnitSuspendFunctionObjCExport.DEFAULT),
|
||||
ObjCExportProblemCollector.SILENT,
|
||||
topLevelNamePrefix,
|
||||
local = true
|
||||
)
|
||||
@@ -273,6 +277,7 @@ internal class ObjCExportNamerImpl(
|
||||
private val configuration: ObjCExportNamer.Configuration,
|
||||
builtIns: KotlinBuiltIns,
|
||||
private val mapper: ObjCExportMapper,
|
||||
private val problemCollector: ObjCExportProblemCollector,
|
||||
private val local: Boolean
|
||||
) : ObjCExportNamer {
|
||||
|
||||
@@ -280,11 +285,13 @@ internal class ObjCExportNamerImpl(
|
||||
moduleDescriptors: Set<ModuleDescriptor>,
|
||||
builtIns: KotlinBuiltIns,
|
||||
mapper: ObjCExportMapper,
|
||||
problemCollector: ObjCExportProblemCollector,
|
||||
topLevelNamePrefix: String,
|
||||
local: Boolean,
|
||||
objcGenerics: Boolean = false,
|
||||
disableSwiftMemberNameMangling: Boolean = false,
|
||||
ignoreInterfaceMethodCollisions: Boolean = false,
|
||||
reportNameCollisions: Boolean = false,
|
||||
) : this(
|
||||
object : ObjCExportNamer.Configuration {
|
||||
override val topLevelNamePrefix: String
|
||||
@@ -301,9 +308,13 @@ internal class ObjCExportNamerImpl(
|
||||
|
||||
override val ignoreInterfaceMethodCollisions: Boolean
|
||||
get() = ignoreInterfaceMethodCollisions
|
||||
|
||||
override val reportNameCollisions: Boolean
|
||||
get() = reportNameCollisions
|
||||
},
|
||||
builtIns,
|
||||
mapper,
|
||||
problemCollector,
|
||||
local
|
||||
)
|
||||
|
||||
@@ -826,10 +837,21 @@ internal class ObjCExportNamerImpl(
|
||||
inline fun getOrPut(element: T, nameCandidates: () -> Sequence<N>): N {
|
||||
getIfAssigned(element)?.let { return it }
|
||||
|
||||
var reportedCollision = false
|
||||
|
||||
nameCandidates().forEach {
|
||||
if (tryAssign(element, it)) {
|
||||
return it
|
||||
}
|
||||
|
||||
if (configuration.reportNameCollisions && !reportedCollision) {
|
||||
if (element is DeclarationDescriptor) {
|
||||
problemCollector.reportWarning(element, "name is mangled when generating Objective-C header")
|
||||
} else {
|
||||
problemCollector.reportWarning("name \"$it\" is mangled when generating Objective-C header")
|
||||
}
|
||||
reportedCollision = true
|
||||
}
|
||||
}
|
||||
|
||||
error("name candidates run out")
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ class ObjcExportHeaderGeneratorMobile internal constructor(
|
||||
restrictToLocalModules: Boolean = false): ObjCExportHeaderGenerator {
|
||||
val mapper = ObjCExportMapper(deprecationResolver, local, configuration.unitSuspendFunctionExport)
|
||||
val namerConfiguration = createNamerConfiguration(configuration)
|
||||
val namer = ObjCExportNamerImpl(namerConfiguration, builtIns, mapper, local)
|
||||
val namer = ObjCExportNamerImpl(namerConfiguration, builtIns, mapper, problemCollector, local)
|
||||
|
||||
return ObjcExportHeaderGeneratorMobile(
|
||||
moduleDescriptors,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
@file:OptIn(kotlin.experimental.ExperimentalObjCName::class)
|
||||
|
||||
package pkg.lib1
|
||||
|
||||
interface InterfaceClash
|
||||
class ClassClash
|
||||
|
||||
interface InterfaceClash2
|
||||
class ClassClash2
|
||||
|
||||
@ObjCName("InterfaceClash2")
|
||||
interface InterfaceClashWithObjCName
|
||||
@ObjCName("ClassClash2")
|
||||
class ClassClashWithObjCName
|
||||
|
||||
class Lib1Kt
|
||||
|
||||
enum class E {
|
||||
ONE,
|
||||
one,
|
||||
@ObjCName("one")
|
||||
TWO,
|
||||
ENTRIES,
|
||||
@ObjCName("values") VALUES1;
|
||||
}
|
||||
|
||||
interface I1 {
|
||||
val prop: Int
|
||||
fun method()
|
||||
}
|
||||
|
||||
interface I2 {
|
||||
val prop: String
|
||||
fun method()
|
||||
}
|
||||
|
||||
fun topLevel(arg: Int) {}
|
||||
fun topLevel(arg: Long) {}
|
||||
@@ -0,0 +1,4 @@
|
||||
package pkg.lib2
|
||||
|
||||
interface InterfaceClash
|
||||
class ClassClash
|
||||
@@ -0,0 +1,25 @@
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at class ClassClashWithObjCName defined in pkg.lib1)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at enum entry one defined in pkg.lib1.E)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at enum entry TWO defined in pkg.lib1.E)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at fun values(): Array<E> defined in pkg.lib1.E)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at val entries: EnumEntries<E> defined in pkg.lib1.E)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at val prop: String defined in pkg.lib1.I2)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at fun `<get-prop>`(): String defined in pkg.lib1.I2)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at interface InterfaceClashWithObjCName defined in pkg.lib1)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at class ClassClash defined in pkg.lib2)
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at interface InterfaceClash defined in pkg.lib2)
|
||||
warning: name "TOCEDLib1Kt" is mangled when generating Objective-C header
|
||||
warning: name "Lib1Kt" is mangled when generating Objective-C header
|
||||
warning: name is mangled when generating Objective-C header
|
||||
(at fun topLevel(arg: Long): Unit defined in pkg.lib1)
|
||||
OK
|
||||
+32
-2
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.konan.test.blackbox.support.EnforcedProperty
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.LoggedData
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.TestCompilerArgs
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.LibraryCompilation
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.ObjCFrameworkCompilation
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.TestCompilationArtifact
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.TestCompilationResult
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.TestCompilationResult.Companion.assertSuccess
|
||||
@@ -21,6 +22,7 @@ import org.jetbrains.kotlin.konan.test.blackbox.support.group.FirPipeline
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.settings.PipelineType
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.settings.Settings
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.junit.jupiter.api.Assumptions
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
@@ -79,11 +81,39 @@ abstract class CompilerOutputTestBase : AbstractNativeSimpleTest() {
|
||||
KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObjCExportDiagnostics() {
|
||||
Assumptions.assumeTrue(targets.hostTarget.family.isAppleFamily)
|
||||
|
||||
val rootDir = File("native/native.tests/testData/compilerOutput/ObjCExportDiagnostics")
|
||||
val settings = testRunSettings
|
||||
val lib1 = compileLibrary(settings, rootDir.resolve("lib1.kt")).assertSuccess().resultingArtifact
|
||||
val lib2 = compileLibrary(settings, rootDir.resolve("lib2.kt")).assertSuccess().resultingArtifact
|
||||
|
||||
val freeCompilerArgs = TestCompilerArgs(
|
||||
"-Xinclude=${lib1.path}",
|
||||
"-Xinclude=${lib2.path}",
|
||||
"-Xbinary=objcExportReportNameCollisions=true"
|
||||
)
|
||||
val expectedArtifact = TestCompilationArtifact.ObjCFramework(buildDir, "testObjCExportDiagnostics")
|
||||
|
||||
val compilationResult = ObjCFrameworkCompilation(
|
||||
settings,
|
||||
freeCompilerArgs,
|
||||
sourceModules = emptyList(),
|
||||
dependencies = emptyList(),
|
||||
expectedArtifact
|
||||
).result
|
||||
val goldenData = rootDir.resolve("output.txt")
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput())
|
||||
}
|
||||
|
||||
private fun compileLibrary(
|
||||
settings: Settings,
|
||||
source: File,
|
||||
freeCompilerArgs: List<String>,
|
||||
dependencies: List<TestCompilationArtifact.KLIB>
|
||||
freeCompilerArgs: List<String> = emptyList(),
|
||||
dependencies: List<TestCompilationArtifact.KLIB> = emptyList(),
|
||||
): TestCompilationResult<out TestCompilationArtifact.KLIB> {
|
||||
val testCase = generateTestCaseWithSingleModule(source, TestCompilerArgs(freeCompilerArgs))
|
||||
val compilation = LibraryCompilation(
|
||||
|
||||
Reference in New Issue
Block a user