jvm-abi-gen: Allow disabling class members sorting in abi.jar
#KT-64589
This commit is contained in:
committed by
Space Team
parent
c0152ccf0f
commit
dab9ac3b1c
@@ -42,13 +42,27 @@ class JvmAbiCommandLineProcessor : CommandLineProcessor {
|
|||||||
"outside of the compilation unit where it's declared.",
|
"outside of the compilation unit where it's declared.",
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val PRESERVE_DECLARATION_ORDER_OPTION: CliOption =
|
||||||
|
CliOption(
|
||||||
|
"preserveDeclarationOrder",
|
||||||
|
"true/false",
|
||||||
|
"Do not sort class members in output abi.jar. Legacy flag that allows tools that rely on methods/fields order in the source" +
|
||||||
|
" to keep working. Flipping this to true reduces stability of the ABI.",
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val pluginId: String
|
override val pluginId: String
|
||||||
get() = COMPILER_PLUGIN_ID
|
get() = COMPILER_PLUGIN_ID
|
||||||
|
|
||||||
override val pluginOptions: Collection<CliOption>
|
override val pluginOptions: Collection<CliOption>
|
||||||
get() = listOf(OUTPUT_PATH_OPTION, REMOVE_DEBUG_INFO_OPTION, REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION)
|
get() = listOf(
|
||||||
|
OUTPUT_PATH_OPTION,
|
||||||
|
REMOVE_DEBUG_INFO_OPTION,
|
||||||
|
REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION,
|
||||||
|
PRESERVE_DECLARATION_ORDER_OPTION,
|
||||||
|
)
|
||||||
|
|
||||||
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) {
|
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) {
|
||||||
when (option) {
|
when (option) {
|
||||||
@@ -58,6 +72,7 @@ class JvmAbiCommandLineProcessor : CommandLineProcessor {
|
|||||||
JvmAbiConfigurationKeys.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE,
|
JvmAbiConfigurationKeys.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE,
|
||||||
value == "true"
|
value == "true"
|
||||||
)
|
)
|
||||||
|
PRESERVE_DECLARATION_ORDER_OPTION -> configuration.put(JvmAbiConfigurationKeys.PRESERVE_DECLARATION_ORDER, value == "true")
|
||||||
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
|
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class JvmAbiComponentRegistrar : CompilerPluginRegistrar() {
|
|||||||
File(outputPath), builderExtension.abiClassInfo, messageCollector,
|
File(outputPath), builderExtension.abiClassInfo, messageCollector,
|
||||||
configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_DEBUG_INFO),
|
configuration.getBoolean(JvmAbiConfigurationKeys.REMOVE_DEBUG_INFO),
|
||||||
removeDataClassCopy,
|
removeDataClassCopy,
|
||||||
|
configuration.getBoolean(JvmAbiConfigurationKeys.PRESERVE_DECLARATION_ORDER),
|
||||||
)
|
)
|
||||||
|
|
||||||
ClassGeneratorExtension.registerExtension(builderExtension)
|
ClassGeneratorExtension.registerExtension(builderExtension)
|
||||||
|
|||||||
@@ -14,4 +14,6 @@ object JvmAbiConfigurationKeys {
|
|||||||
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DEBUG_INFO_OPTION.description)
|
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DEBUG_INFO_OPTION.description)
|
||||||
val REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE: CompilerConfigurationKey<Boolean> =
|
val REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE: CompilerConfigurationKey<Boolean> =
|
||||||
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION.description)
|
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION.description)
|
||||||
|
val PRESERVE_DECLARATION_ORDER: CompilerConfigurationKey<Boolean> =
|
||||||
|
CompilerConfigurationKey.create(JvmAbiCommandLineProcessor.PRESERVE_DECLARATION_ORDER_OPTION.description)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes
|
|||||||
fun abiMetadataProcessor(
|
fun abiMetadataProcessor(
|
||||||
annotationVisitor: AnnotationVisitor,
|
annotationVisitor: AnnotationVisitor,
|
||||||
removeDataClassCopyIfConstructorIsPrivate: Boolean,
|
removeDataClassCopyIfConstructorIsPrivate: Boolean,
|
||||||
|
preserveDeclarationOrder: Boolean,
|
||||||
): AnnotationVisitor =
|
): AnnotationVisitor =
|
||||||
kotlinClassHeaderVisitor { header ->
|
kotlinClassHeaderVisitor { header ->
|
||||||
// kotlinx-metadata only supports writing Kotlin metadata of version >= 1.4, so we need to
|
// kotlinx-metadata only supports writing Kotlin metadata of version >= 1.4, so we need to
|
||||||
@@ -32,13 +33,13 @@ fun abiMetadataProcessor(
|
|||||||
KotlinClassMetadata.transform(header) { metadata ->
|
KotlinClassMetadata.transform(header) { metadata ->
|
||||||
when (metadata) {
|
when (metadata) {
|
||||||
is KotlinClassMetadata.Class -> {
|
is KotlinClassMetadata.Class -> {
|
||||||
metadata.kmClass.removePrivateDeclarations(removeDataClassCopyIfConstructorIsPrivate)
|
metadata.kmClass.removePrivateDeclarations(removeDataClassCopyIfConstructorIsPrivate, preserveDeclarationOrder)
|
||||||
}
|
}
|
||||||
is KotlinClassMetadata.FileFacade -> {
|
is KotlinClassMetadata.FileFacade -> {
|
||||||
metadata.kmPackage.removePrivateDeclarations()
|
metadata.kmPackage.removePrivateDeclarations(preserveDeclarationOrder)
|
||||||
}
|
}
|
||||||
is KotlinClassMetadata.MultiFileClassPart -> {
|
is KotlinClassMetadata.MultiFileClassPart -> {
|
||||||
metadata.kmPackage.removePrivateDeclarations()
|
metadata.kmPackage.removePrivateDeclarations(preserveDeclarationOrder)
|
||||||
}
|
}
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
@@ -149,27 +150,27 @@ private fun AnnotationVisitor.visitKotlinMetadata(header: Metadata) {
|
|||||||
visitEnd()
|
visitEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KmClass.removePrivateDeclarations(removeCopyAlongWithConstructor: Boolean) {
|
private fun KmClass.removePrivateDeclarations(removeCopyAlongWithConstructor: Boolean, preserveDeclarationOrder: Boolean) {
|
||||||
constructors.removeIf { it.visibility.isPrivate }
|
constructors.removeIf { it.visibility.isPrivate }
|
||||||
(this as KmDeclarationContainer).removePrivateDeclarations(
|
(this as KmDeclarationContainer).removePrivateDeclarations(copyFunShouldBeDeleted(removeCopyAlongWithConstructor), preserveDeclarationOrder)
|
||||||
copyFunShouldBeDeleted(removeCopyAlongWithConstructor)
|
|
||||||
)
|
|
||||||
localDelegatedProperties.clear()
|
localDelegatedProperties.clear()
|
||||||
// TODO: do not serialize private type aliases once KT-17229 is fixed.
|
// TODO: do not serialize private type aliases once KT-17229 is fixed.
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KmPackage.removePrivateDeclarations() {
|
private fun KmPackage.removePrivateDeclarations(preserveDeclarationOrder: Boolean) {
|
||||||
(this as KmDeclarationContainer).removePrivateDeclarations(false)
|
(this as KmDeclarationContainer).removePrivateDeclarations(false, preserveDeclarationOrder)
|
||||||
localDelegatedProperties.clear()
|
localDelegatedProperties.clear()
|
||||||
// TODO: do not serialize private type aliases once KT-17229 is fixed.
|
// TODO: do not serialize private type aliases once KT-17229 is fixed.
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KmDeclarationContainer.removePrivateDeclarations(copyFunShouldBeDeleted: Boolean) {
|
private fun KmDeclarationContainer.removePrivateDeclarations(copyFunShouldBeDeleted: Boolean, preserveDeclarationOrder: Boolean) {
|
||||||
functions.removeIf { it.visibility.isPrivate || (copyFunShouldBeDeleted && it.name == "copy") }
|
functions.removeIf { it.visibility.isPrivate || (copyFunShouldBeDeleted && it.name == "copy") }
|
||||||
properties.removeIf { it.visibility.isPrivate }
|
properties.removeIf { it.visibility.isPrivate }
|
||||||
|
|
||||||
functions.sortWith(compareBy(KmFunction::name, { it.signature.toString() }))
|
if (!preserveDeclarationOrder) {
|
||||||
properties.sortWith(compareBy(KmProperty::name, { it.getterSignature.toString() }))
|
functions.sortWith(compareBy(KmFunction::name, { it.signature.toString() }))
|
||||||
|
properties.sortWith(compareBy(KmProperty::name, { it.getterSignature.toString() }))
|
||||||
|
}
|
||||||
|
|
||||||
for (property in properties) {
|
for (property in properties) {
|
||||||
// Whether or not the *non-const* property is initialized by a compile-time constant is not a part of the ABI.
|
// Whether or not the *non-const* property is initialized by a compile-time constant is not a part of the ABI.
|
||||||
|
|||||||
@@ -28,11 +28,13 @@ class JvmAbiOutputExtension(
|
|||||||
private val messageCollector: MessageCollector,
|
private val messageCollector: MessageCollector,
|
||||||
private val removeDebugInfo: Boolean,
|
private val removeDebugInfo: Boolean,
|
||||||
private val removeDataClassCopyIfConstructorIsPrivate: Boolean,
|
private val removeDataClassCopyIfConstructorIsPrivate: Boolean,
|
||||||
|
private val preserveDeclarationOrder: Boolean,
|
||||||
) : ClassFileFactoryFinalizerExtension {
|
) : ClassFileFactoryFinalizerExtension {
|
||||||
override fun finalizeClassFactory(factory: ClassFileFactory) {
|
override fun finalizeClassFactory(factory: ClassFileFactory) {
|
||||||
// We need to wait until the end to produce any output in order to strip classes
|
// We need to wait until the end to produce any output in order to strip classes
|
||||||
// from the InnerClasses attributes.
|
// from the InnerClasses attributes.
|
||||||
val outputFiles = AbiOutputFiles(abiClassInfos, factory, removeDebugInfo, removeDataClassCopyIfConstructorIsPrivate)
|
val outputFiles =
|
||||||
|
AbiOutputFiles(abiClassInfos, factory, removeDebugInfo, removeDataClassCopyIfConstructorIsPrivate, preserveDeclarationOrder)
|
||||||
if (outputPath.extension == "jar") {
|
if (outputPath.extension == "jar") {
|
||||||
// We don't include the runtime or main class in interface jars and always reset time stamps.
|
// We don't include the runtime or main class in interface jars and always reset time stamps.
|
||||||
CompileEnvironmentUtil.writeToJar(
|
CompileEnvironmentUtil.writeToJar(
|
||||||
@@ -56,6 +58,7 @@ class JvmAbiOutputExtension(
|
|||||||
val outputFiles: OutputFileCollection,
|
val outputFiles: OutputFileCollection,
|
||||||
val removeDebugInfo: Boolean,
|
val removeDebugInfo: Boolean,
|
||||||
val removeCopyAlongWithConstructor: Boolean,
|
val removeCopyAlongWithConstructor: Boolean,
|
||||||
|
val preserveDeclarationOrder: Boolean,
|
||||||
) : OutputFileCollection {
|
) : OutputFileCollection {
|
||||||
override fun get(relativePath: String): OutputFile? {
|
override fun get(relativePath: String): OutputFile? {
|
||||||
error("AbiOutputFiles does not implement `get`.")
|
error("AbiOutputFiles does not implement `get`.")
|
||||||
@@ -155,14 +158,19 @@ class JvmAbiOutputExtension(
|
|||||||
if (descriptor != JvmAnnotationNames.METADATA_DESC)
|
if (descriptor != JvmAnnotationNames.METADATA_DESC)
|
||||||
return delegate
|
return delegate
|
||||||
// Strip private declarations from the Kotlin Metadata annotation.
|
// Strip private declarations from the Kotlin Metadata annotation.
|
||||||
return abiMetadataProcessor(delegate, removeCopyAlongWithConstructor)
|
return abiMetadataProcessor(delegate, removeCopyAlongWithConstructor, preserveDeclarationOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitEnd() {
|
override fun visitEnd() {
|
||||||
// Output class members in sorted order so that changes in original ordering don't affect the ABI JAR.
|
// Output class members in sorted order so that changes in original ordering don't affect the ABI JAR.
|
||||||
|
|
||||||
keptFields.sortedWith(compareBy(FieldNode::name, FieldNode::desc)).forEach { it.accept(cv) }
|
keptFields
|
||||||
keptMethods.sortedWith(compareBy(MethodNode::name, MethodNode::desc)).forEach { it.accept(cv) }
|
.apply { if (!preserveDeclarationOrder) sortWith(compareBy(FieldNode::name, FieldNode::desc)) }
|
||||||
|
.forEach { it.accept(cv) }
|
||||||
|
|
||||||
|
keptMethods
|
||||||
|
.apply { if (!preserveDeclarationOrder) sortWith(compareBy(MethodNode::name, MethodNode::desc)) }
|
||||||
|
.forEach { it.accept(cv) }
|
||||||
|
|
||||||
innerClassesToKeep.addInnerClasses(innerClassInfos, internalName)
|
innerClassesToKeep.addInnerClasses(innerClassInfos, internalName)
|
||||||
innerClassesToKeep.addOuterClasses(innerClassInfos)
|
innerClassesToKeep.addOuterClasses(innerClassInfos)
|
||||||
|
|||||||
@@ -84,7 +84,10 @@ abstract class BaseJvmAbiTest : TestCase() {
|
|||||||
JvmAbiCommandLineProcessor.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION.optionName, true.toString()
|
JvmAbiCommandLineProcessor.REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE_OPTION.optionName, true.toString()
|
||||||
).takeIf {
|
).takeIf {
|
||||||
InTextDirectivesUtils.findStringWithPrefixes(directives, "// REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE") != null
|
InTextDirectivesUtils.findStringWithPrefixes(directives, "// REMOVE_DATA_CLASS_COPY_IF_CONSTRUCTOR_IS_PRIVATE") != null
|
||||||
}
|
},
|
||||||
|
abiOption(JvmAbiCommandLineProcessor.PRESERVE_DECLARATION_ORDER_OPTION.optionName, true.toString()).takeIf {
|
||||||
|
InTextDirectivesUtils.findStringWithPrefixes(directives, "// PRESERVE_DECLARATION_ORDER") != null
|
||||||
|
},
|
||||||
).toTypedArray()
|
).toTypedArray()
|
||||||
destination = compilation.destinationDir.canonicalPath
|
destination = compilation.destinationDir.canonicalPath
|
||||||
noSourceDebugExtension = InTextDirectivesUtils.findStringWithPrefixes(directives, "// NO_SOURCE_DEBUG_EXTENSION") != null
|
noSourceDebugExtension = InTextDirectivesUtils.findStringWithPrefixes(directives, "// NO_SOURCE_DEBUG_EXTENSION") != null
|
||||||
|
|||||||
+5
@@ -145,6 +145,11 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest {
|
|||||||
runTest("plugins/jvm-abi-gen/testData/compare/parameterName/");
|
runTest("plugins/jvm-abi-gen/testData/compare/parameterName/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("preserveDeclarationOrder")
|
||||||
|
public void testPreserveDeclarationOrder() throws Exception {
|
||||||
|
runTest("plugins/jvm-abi-gen/testData/compare/preserveDeclarationOrder/");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("privateTypealias")
|
@TestMetadata("privateTypealias")
|
||||||
public void testPrivateTypealias() throws Exception {
|
public void testPrivateTypealias() throws Exception {
|
||||||
runTest("plugins/jvm-abi-gen/testData/compare/privateTypealias/");
|
runTest("plugins/jvm-abi-gen/testData/compare/privateTypealias/");
|
||||||
|
|||||||
+5
@@ -65,6 +65,11 @@ public class JvmAbiContentTestGenerated extends AbstractJvmAbiContentTest {
|
|||||||
runTest("plugins/jvm-abi-gen/testData/content/kt50005/");
|
runTest("plugins/jvm-abi-gen/testData/content/kt50005/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("preserveDeclarationOrderKeepsClassIntact")
|
||||||
|
public void testPreserveDeclarationOrderKeepsClassIntact() throws Exception {
|
||||||
|
runTest("plugins/jvm-abi-gen/testData/content/preserveDeclarationOrderKeepsClassIntact/");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("whenMapping")
|
@TestMetadata("whenMapping")
|
||||||
public void testWhenMapping() throws Exception {
|
public void testWhenMapping() throws Exception {
|
||||||
runTest("plugins/jvm-abi-gen/testData/content/whenMapping/");
|
runTest("plugins/jvm-abi-gen/testData/content/whenMapping/");
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Chaos {
|
||||||
|
fun a() = Unit
|
||||||
|
fun b() = Unit
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val a = Unit
|
||||||
|
@JvmField
|
||||||
|
val b = Unit
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Chaos {
|
||||||
|
fun b() = Unit
|
||||||
|
fun a() = Unit
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val b = Unit
|
||||||
|
@JvmField
|
||||||
|
val a = Unit
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// PRESERVE_DECLARATION_ORDER
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
// PRESERVE_DECLARATION_ORDER
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Chaos {
|
||||||
|
fun b() = Unit
|
||||||
|
fun a() = Unit
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val b = Unit
|
||||||
|
@JvmField
|
||||||
|
val a = Unit
|
||||||
|
}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public final class test/Chaos {
|
||||||
|
// source: 'preserveDeclarationOrder.kt'
|
||||||
|
public final @kotlin.jvm.JvmField @org.jetbrains.annotations.NotNull field b: kotlin.Unit
|
||||||
|
public final @kotlin.jvm.JvmField @org.jetbrains.annotations.NotNull field a: kotlin.Unit
|
||||||
|
public method <init>(): void
|
||||||
|
public final method b(): void
|
||||||
|
public final method a(): void
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user