[K/N] Add binary option to produce error on mangled objc names (#5264)
objcExportErrorOnNameCollisions=true binary option makes the compiler emit an error whenever it mangles a name emitted by ObjCExport. ^KT-65863 Fixed
This commit is contained in:
+2
@@ -37,6 +37,8 @@ object BinaryOptions : BinaryOptionRegistry() {
|
|||||||
|
|
||||||
val objcExportReportNameCollisions by booleanOption()
|
val objcExportReportNameCollisions by booleanOption()
|
||||||
|
|
||||||
|
val objcExportErrorOnNameCollisions by booleanOption()
|
||||||
|
|
||||||
val gc by option<GC>(shortcut = { it.shortcut })
|
val gc by option<GC>(shortcut = { it.shortcut })
|
||||||
|
|
||||||
val gcSchedulerType by option<GCSchedulerType>(hideValue = { it.deprecatedWithReplacement != null })
|
val gcSchedulerType by option<GCSchedulerType>(hideValue = { it.deprecatedWithReplacement != null })
|
||||||
|
|||||||
+2
@@ -24,6 +24,8 @@ internal fun StorageComponentContainer.initContainer(config: KonanConfig) {
|
|||||||
useInstance(object : ObjCExportProblemCollector {
|
useInstance(object : ObjCExportProblemCollector {
|
||||||
override fun reportWarning(text: String) {}
|
override fun reportWarning(text: String) {}
|
||||||
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {}
|
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {}
|
||||||
|
override fun reportError(text: String) {}
|
||||||
|
override fun reportError(declaration: DeclarationDescriptor, text: String) {}
|
||||||
override fun reportException(throwable: Throwable) = throw throwable
|
override fun reportException(throwable: Throwable) = throw throwable
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+24
-7
@@ -52,6 +52,7 @@ internal fun produceObjCExportInterface(
|
|||||||
val disableSwiftMemberNameMangling = config.configuration.getBoolean(BinaryOptions.objcExportDisableSwiftMemberNameMangling)
|
val disableSwiftMemberNameMangling = config.configuration.getBoolean(BinaryOptions.objcExportDisableSwiftMemberNameMangling)
|
||||||
val ignoreInterfaceMethodCollisions = config.configuration.getBoolean(BinaryOptions.objcExportIgnoreInterfaceMethodCollisions)
|
val ignoreInterfaceMethodCollisions = config.configuration.getBoolean(BinaryOptions.objcExportIgnoreInterfaceMethodCollisions)
|
||||||
val reportNameCollisions = config.configuration.getBoolean(BinaryOptions.objcExportReportNameCollisions)
|
val reportNameCollisions = config.configuration.getBoolean(BinaryOptions.objcExportReportNameCollisions)
|
||||||
|
val errorOnNameCollisions = config.configuration.getBoolean(BinaryOptions.objcExportErrorOnNameCollisions)
|
||||||
|
|
||||||
val problemCollector = ObjCExportCompilerProblemCollector(context)
|
val problemCollector = ObjCExportCompilerProblemCollector(context)
|
||||||
|
|
||||||
@@ -65,7 +66,11 @@ internal fun produceObjCExportInterface(
|
|||||||
objcGenerics = objcGenerics,
|
objcGenerics = objcGenerics,
|
||||||
disableSwiftMemberNameMangling = disableSwiftMemberNameMangling,
|
disableSwiftMemberNameMangling = disableSwiftMemberNameMangling,
|
||||||
ignoreInterfaceMethodCollisions = ignoreInterfaceMethodCollisions,
|
ignoreInterfaceMethodCollisions = ignoreInterfaceMethodCollisions,
|
||||||
reportNameCollisions = reportNameCollisions,
|
nameCollisionMode = when {
|
||||||
|
errorOnNameCollisions -> ObjCExportNameCollisionMode.ERROR
|
||||||
|
reportNameCollisions -> ObjCExportNameCollisionMode.WARNING
|
||||||
|
else -> ObjCExportNameCollisionMode.NONE
|
||||||
|
}
|
||||||
)
|
)
|
||||||
val shouldExportKDoc = context.shouldExportKDoc()
|
val shouldExportKDoc = context.shouldExportKDoc()
|
||||||
val additionalImports = context.config.configuration.getNotNull(KonanConfigKeys.FRAMEWORK_IMPORT_HEADERS)
|
val additionalImports = context.config.configuration.getNotNull(KonanConfigKeys.FRAMEWORK_IMPORT_HEADERS)
|
||||||
@@ -77,21 +82,33 @@ internal fun produceObjCExportInterface(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ObjCExportCompilerProblemCollector(val context: PhaseContext) : ObjCExportProblemCollector {
|
private class ObjCExportCompilerProblemCollector(val context: PhaseContext) : ObjCExportProblemCollector {
|
||||||
|
private val DeclarationDescriptor.psiLocation
|
||||||
|
get() = (this@psiLocation as? DeclarationDescriptorWithSource)?.source?.getPsi()?.let { MessageUtil.psiElementToMessageLocation(it) }
|
||||||
|
|
||||||
override fun reportWarning(text: String) {
|
override fun reportWarning(text: String) {
|
||||||
context.reportCompilationWarning(text)
|
context.reportCompilationWarning(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {
|
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {
|
||||||
val psi = (declaration as? DeclarationDescriptorWithSource)?.source?.getPsi()
|
val location = declaration.psiLocation ?: return reportWarning(
|
||||||
?: return reportWarning(
|
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})"
|
||||||
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})"
|
)
|
||||||
)
|
|
||||||
|
|
||||||
val location = MessageUtil.psiElementToMessageLocation(psi)
|
|
||||||
|
|
||||||
context.messageCollector.report(CompilerMessageSeverity.WARNING, text, location)
|
context.messageCollector.report(CompilerMessageSeverity.WARNING, text, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun reportError(text: String) {
|
||||||
|
context.messageCollector.report(CompilerMessageSeverity.ERROR, text, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reportError(declaration: DeclarationDescriptor, text: String) {
|
||||||
|
val location = declaration.psiLocation ?: return reportError(
|
||||||
|
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})"
|
||||||
|
)
|
||||||
|
|
||||||
|
context.messageCollector.report(CompilerMessageSeverity.ERROR, text, location)
|
||||||
|
}
|
||||||
|
|
||||||
override fun reportException(throwable: Throwable) {
|
override fun reportException(throwable: Throwable) {
|
||||||
throw throwable
|
throw throwable
|
||||||
}
|
}
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.backend.konan
|
||||||
|
|
||||||
|
enum class ObjCExportNameCollisionMode {
|
||||||
|
/*
|
||||||
|
* In this mode, exported ObjC symbol name collisions will not produce any compiler output.
|
||||||
|
*/
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In this mode, exported ObjC symbol name collisions will produce a compiler warning.
|
||||||
|
*/
|
||||||
|
WARNING,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In this mode, exported ObjC symbol name collisions will produce a compiler error.
|
||||||
|
*/
|
||||||
|
ERROR,
|
||||||
|
;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at class ClassClashWithObjCName defined in pkg.lib1)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at enum entry one defined in pkg.lib1.E)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at enum entry TWO defined in pkg.lib1.E)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at fun values(): Array<E> defined in pkg.lib1.E)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at val entries: EnumEntries<E> defined in pkg.lib1.E)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at val prop: String defined in pkg.lib1.I2)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at fun `<get-prop>`(): String defined in pkg.lib1.I2)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at interface InterfaceClashWithObjCName defined in pkg.lib1)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at class ClassClash defined in pkg.lib2)
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at interface InterfaceClash defined in pkg.lib2)
|
||||||
|
error: name "TOCEDLib1Kt" is mangled when generating Objective-C header
|
||||||
|
error: name "Lib1Kt" is mangled when generating Objective-C header
|
||||||
|
error: name is mangled when generating Objective-C header
|
||||||
|
(at fun topLevel(arg: Long): Unit defined in pkg.lib1)
|
||||||
|
COMPILATION_ERROR
|
||||||
+24
-8
@@ -26,6 +26,7 @@ import org.junit.jupiter.api.Assumptions
|
|||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlin.test.assertIs
|
||||||
|
|
||||||
abstract class CompilerOutputTestBase : AbstractNativeSimpleTest() {
|
abstract class CompilerOutputTestBase : AbstractNativeSimpleTest() {
|
||||||
@Test
|
@Test
|
||||||
@@ -83,30 +84,45 @@ abstract class CompilerOutputTestBase : AbstractNativeSimpleTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testObjCExportDiagnostics() {
|
fun testObjCExportDiagnostics() {
|
||||||
|
val rootDir = File("native/native.tests/testData/compilerOutput/ObjCExportDiagnostics")
|
||||||
|
val compilationResult = doBuildObjCFrameworkWithNameCollisions(rootDir, listOf("-Xbinary=objcExportReportNameCollisions=true"))
|
||||||
|
val goldenData = rootDir.resolve("output.txt")
|
||||||
|
|
||||||
|
KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testObjCExportDiagnosticsErrors() {
|
||||||
|
val rootDir = File("native/native.tests/testData/compilerOutput/ObjCExportDiagnostics")
|
||||||
|
val compilationResult = doBuildObjCFrameworkWithNameCollisions(rootDir, listOf("-Xbinary=objcExportErrorOnNameCollisions=true"))
|
||||||
|
assertIs<TestCompilationResult.Failure>(compilationResult)
|
||||||
|
val goldenData = rootDir.resolve("error.txt")
|
||||||
|
|
||||||
|
KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doBuildObjCFrameworkWithNameCollisions(rootDir: File, additionalOptions: List<String>): TestCompilationResult<out TestCompilationArtifact.ObjCFramework> {
|
||||||
Assumptions.assumeTrue(targets.hostTarget.family.isAppleFamily)
|
Assumptions.assumeTrue(targets.hostTarget.family.isAppleFamily)
|
||||||
|
|
||||||
val rootDir = File("native/native.tests/testData/compilerOutput/ObjCExportDiagnostics")
|
|
||||||
val settings = testRunSettings
|
val settings = testRunSettings
|
||||||
val lib1 = compileLibrary(settings, rootDir.resolve("lib1.kt")).assertSuccess().resultingArtifact
|
val lib1 = compileLibrary(settings, rootDir.resolve("lib1.kt")).assertSuccess().resultingArtifact
|
||||||
val lib2 = compileLibrary(settings, rootDir.resolve("lib2.kt")).assertSuccess().resultingArtifact
|
val lib2 = compileLibrary(settings, rootDir.resolve("lib2.kt")).assertSuccess().resultingArtifact
|
||||||
|
|
||||||
val freeCompilerArgs = TestCompilerArgs(
|
val freeCompilerArgs = TestCompilerArgs(
|
||||||
"-Xinclude=${lib1.path}",
|
listOf(
|
||||||
"-Xinclude=${lib2.path}",
|
"-Xinclude=${lib1.path}",
|
||||||
"-Xbinary=objcExportReportNameCollisions=true"
|
"-Xinclude=${lib2.path}"
|
||||||
|
) + additionalOptions
|
||||||
)
|
)
|
||||||
val expectedArtifact = TestCompilationArtifact.ObjCFramework(buildDir, "testObjCExportDiagnostics")
|
val expectedArtifact = TestCompilationArtifact.ObjCFramework(buildDir, "testObjCExportDiagnostics")
|
||||||
|
|
||||||
val compilationResult = ObjCFrameworkCompilation(
|
return ObjCFrameworkCompilation(
|
||||||
settings,
|
settings,
|
||||||
freeCompilerArgs,
|
freeCompilerArgs,
|
||||||
sourceModules = emptyList(),
|
sourceModules = emptyList(),
|
||||||
dependencies = emptyList(),
|
dependencies = emptyList(),
|
||||||
expectedArtifact
|
expectedArtifact
|
||||||
).result
|
).result
|
||||||
val goldenData = rootDir.resolve("output.txt")
|
|
||||||
|
|
||||||
KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun compileLibrary(
|
internal fun compileLibrary(
|
||||||
|
|||||||
+24
-11
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.konan.objcexport
|
|||||||
import org.jetbrains.kotlin.backend.common.serialization.findSourceFile
|
import org.jetbrains.kotlin.backend.common.serialization.findSourceFile
|
||||||
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ObjCExportNameCollisionMode
|
||||||
import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport
|
import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport
|
||||||
import org.jetbrains.kotlin.backend.konan.cKeywords
|
import org.jetbrains.kotlin.backend.konan.cKeywords
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||||
@@ -67,8 +68,8 @@ interface ObjCExportNamer {
|
|||||||
val ignoreInterfaceMethodCollisions: Boolean
|
val ignoreInterfaceMethodCollisions: Boolean
|
||||||
get() = false
|
get() = false
|
||||||
|
|
||||||
val reportNameCollisions: Boolean
|
val nameCollisionMode: ObjCExportNameCollisionMode
|
||||||
get() = false
|
get() = ObjCExportNameCollisionMode.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
val topLevelNamePrefix: String
|
val topLevelNamePrefix: String
|
||||||
@@ -308,7 +309,7 @@ class ObjCExportNamerImpl(
|
|||||||
objcGenerics: Boolean = false,
|
objcGenerics: Boolean = false,
|
||||||
disableSwiftMemberNameMangling: Boolean = false,
|
disableSwiftMemberNameMangling: Boolean = false,
|
||||||
ignoreInterfaceMethodCollisions: Boolean = false,
|
ignoreInterfaceMethodCollisions: Boolean = false,
|
||||||
reportNameCollisions: Boolean = false,
|
nameCollisionMode: ObjCExportNameCollisionMode = ObjCExportNameCollisionMode.NONE,
|
||||||
) : this(
|
) : this(
|
||||||
object : ObjCExportNamer.Configuration {
|
object : ObjCExportNamer.Configuration {
|
||||||
override val topLevelNamePrefix: String
|
override val topLevelNamePrefix: String
|
||||||
@@ -326,8 +327,8 @@ class ObjCExportNamerImpl(
|
|||||||
override val ignoreInterfaceMethodCollisions: Boolean
|
override val ignoreInterfaceMethodCollisions: Boolean
|
||||||
get() = ignoreInterfaceMethodCollisions
|
get() = ignoreInterfaceMethodCollisions
|
||||||
|
|
||||||
override val reportNameCollisions: Boolean
|
override val nameCollisionMode: ObjCExportNameCollisionMode
|
||||||
get() = reportNameCollisions
|
get() = nameCollisionMode
|
||||||
},
|
},
|
||||||
builtIns,
|
builtIns,
|
||||||
mapper,
|
mapper,
|
||||||
@@ -863,13 +864,25 @@ class ObjCExportNamerImpl(
|
|||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configuration.reportNameCollisions && !reportedCollision) {
|
if (!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
|
reportedCollision = true
|
||||||
|
when (configuration.nameCollisionMode) {
|
||||||
|
ObjCExportNameCollisionMode.ERROR -> {
|
||||||
|
if (element is DeclarationDescriptor) {
|
||||||
|
problemCollector.reportError(element, "name is mangled when generating Objective-C header")
|
||||||
|
} else {
|
||||||
|
problemCollector.reportError("name \"$it\" is mangled when generating Objective-C header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjCExportNameCollisionMode.WARNING -> {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjCExportNameCollisionMode.NONE -> Unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
@@ -10,11 +10,15 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|||||||
interface ObjCExportProblemCollector {
|
interface ObjCExportProblemCollector {
|
||||||
fun reportWarning(text: String)
|
fun reportWarning(text: String)
|
||||||
fun reportWarning(declaration: DeclarationDescriptor, text: String)
|
fun reportWarning(declaration: DeclarationDescriptor, text: String)
|
||||||
|
fun reportError(text: String)
|
||||||
|
fun reportError(declaration: DeclarationDescriptor, text: String)
|
||||||
fun reportException(throwable: Throwable)
|
fun reportException(throwable: Throwable)
|
||||||
|
|
||||||
object SILENT : ObjCExportProblemCollector {
|
object SILENT : ObjCExportProblemCollector {
|
||||||
override fun reportWarning(text: String) {}
|
override fun reportWarning(text: String) {}
|
||||||
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {}
|
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {}
|
||||||
|
override fun reportError(text: String) {}
|
||||||
|
override fun reportError(declaration: DeclarationDescriptor, text: String) {}
|
||||||
override fun reportException(throwable: Throwable) {}
|
override fun reportException(throwable: Throwable) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user