Sort class members in jvm-abi-gen

#KT-64589 Fixed
This commit is contained in:
Vladimir Tagakov
2023-12-26 17:44:12 -07:00
committed by Space Team
parent 1a46b053f2
commit ad9c100137
11 changed files with 95 additions and 37 deletions
@@ -6,10 +6,7 @@
package org.jetbrains.kotlin.jvm.abi
import kotlinx.metadata.*
import kotlinx.metadata.jvm.JvmMetadataVersion
import kotlinx.metadata.jvm.KotlinClassMetadata
import kotlinx.metadata.jvm.Metadata
import kotlinx.metadata.jvm.localDelegatedProperties
import kotlinx.metadata.jvm.*
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.*
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
@@ -166,6 +163,9 @@ private fun KmDeclarationContainer.removePrivateDeclarations() {
functions.removeIf { it.visibility.isPrivate }
properties.removeIf { it.visibility.isPrivate }
functions.sortWith(compareBy(KmFunction::name, { it.signature.toString() }))
properties.sortWith(compareBy(KmProperty::name, { it.getterSignature.toString() }))
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.
if (!property.isConst) {
@@ -18,6 +18,8 @@ import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.ClassRemapper
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.commons.Remapper
import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.io.File
class JvmAbiOutputExtension(
@@ -76,7 +78,6 @@ class JvmAbiOutputExtension(
else -> /* abiInfo is AbiClassInfo.Stripped */ {
val methodInfo = (abiInfo as AbiClassInfo.Stripped).methodInfo
val innerClassInfos = mutableMapOf<String, InnerClassInfo>()
val innerClassesToKeep = mutableSetOf<String>()
val writer = ClassWriter(0)
val remapper = ClassRemapper(writer, object : Remapper() {
@@ -84,6 +85,10 @@ class JvmAbiOutputExtension(
internalName.also { innerClassesToKeep.add(it) }
})
ClassReader(outputFile.asByteArray()).accept(object : ClassVisitor(Opcodes.API_VERSION, remapper) {
private val keptFields = mutableListOf<FieldNode>()
private val keptMethods = mutableListOf<MethodNode>()
private val innerClassInfos = mutableMapOf<String, InnerClassInfo>()
// Strip private fields.
override fun visitField(
access: Int,
@@ -94,7 +99,9 @@ class JvmAbiOutputExtension(
): FieldVisitor? {
if (access and Opcodes.ACC_PRIVATE != 0)
return null
return super.visitField(access, name, descriptor, signature, value)
return FieldNode(access, name, descriptor, signature, value).also {
keptFields += it
}
}
override fun visitMethod(
@@ -104,15 +111,17 @@ class JvmAbiOutputExtension(
signature: String?,
exceptions: Array<out String>?
): MethodVisitor? {
val info = methodInfo[Method(name, descriptor)]
?: return null
val info = methodInfo[Method(name, descriptor)] ?: return null
val visitor = super.visitMethod(access, name, descriptor, signature, exceptions)
if (info == AbiMethodInfo.KEEP || access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) != 0) {
return if (removeDebugInfo) DebugInfoRemovingMethodVisitor(visitor) else visitor
val node = MethodNode(access, name, descriptor, signature, exceptions).also {
keptMethods += it
}
return BodyStrippingMethodVisitor(visitor)
if (info == AbiMethodInfo.KEEP || access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) != 0) {
return if (removeDebugInfo) DebugInfoRemovingMethodVisitor(node) else node
}
return BodyStrippingMethodVisitor(node)
}
override fun visitSource(source: String?, debug: String?) {
@@ -145,20 +154,23 @@ class JvmAbiOutputExtension(
return abiMetadataProcessor(delegate)
}
override fun visitEnd() {}
override fun visitEnd() {
// 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) }
keptMethods.sortedWith(compareBy(MethodNode::name, MethodNode::desc)).forEach { it.accept(cv) }
innerClassesToKeep.addInnerClasses(innerClassInfos, internalName)
innerClassesToKeep.addOuterClasses(innerClassInfos)
for (name in innerClassesToKeep.sorted()) {
innerClassInfos[name]?.let { cv.visitInnerClass(it.name, it.outerName, it.innerName, it.access) }
}
super.visitEnd()
}
}, 0)
innerClassesToKeep.addInnerClasses(innerClassInfos, internalName)
innerClassesToKeep.addOuterClasses(innerClassInfos)
// Output classes in sorted order so that changes in original ordering due to method bodies, etc.
// don't affect the ABI JAR.
for (name in innerClassesToKeep.sorted()) {
innerClassInfos[name]?.let { writer.visitInnerClass(it.name, it.outerName, it.innerName, it.access) }
}
writer.visitEnd()
SimpleOutputBinaryFile(outputFile.sourceFiles, outputFile.relativePath, writer.toByteArray())
}
}
@@ -90,6 +90,16 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderPrivateInline/");
}
@TestMetadata("fieldOrder")
public void testFieldOrder() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/fieldOrder/");
}
@TestMetadata("funOrder")
public void testFunOrder() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/funOrder/");
}
@TestMetadata("functionBody")
public void testFunctionBody() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/functionBody/");
@@ -0,0 +1,11 @@
package test
class Class {
@JvmField
val first = Unit
@JvmField
val second = Unit
val String.first
get() = this
}
@@ -0,0 +1,10 @@
package test
class Class {
val String.first
get() = this
@JvmField
val second = Unit
@JvmField
val first = Unit
}
@@ -0,0 +1,8 @@
package test
class Class {
fun first() = Unit
fun first(a: Any) = Unit
fun second() = Unit
}
@@ -0,0 +1,7 @@
package test
class Class {
fun second() = Unit
fun first(a: Any) = Unit
fun first() = Unit
}
@@ -3,9 +3,9 @@ public final enum class test/E {
// source: 'test.kt'
public final enum static field A: test.E
public final enum static field B: test.E
public static method values(): test.E[]
public static method valueOf(p0: java.lang.String): test.E
public static @org.jetbrains.annotations.NotNull method getEntries(): kotlin.enums.EnumEntries
public static method valueOf(p0: java.lang.String): test.E
public static method values(): test.E[]
}
@kotlin.Metadata
public final class test/Test {
+4 -4
View File
@@ -11,12 +11,12 @@ public class test/BaseClass {
public final static @org.jetbrains.annotations.NotNull field Companion: test.BaseClass$Companion
public final static field basePublicConst: int
public method <init>(): void
public method getBaseClassPublicVal(): int
public final method baseClassInternalFun$main(): int
protected final method baseClassProtectedFun(): int
public method baseClassPublicFun(): int
public final method getBaseClassInternalVal$main(): int
public final method baseClassInternalFun$main(): int
protected final method getBaseClassProtectedVal(): int
protected final method baseClassProtectedFun(): int
public method getBaseClassPublicVal(): int
}
@kotlin.Metadata
public final class test/Class$NestedInnerClass$NestedNestedInnerClass {
@@ -47,4 +47,4 @@ public interface test/Interface {
final class test/PrivateClass {
// source: 'classes.kt'
public method <init>(): void
}
}
@@ -1,12 +1,12 @@
@kotlin.Metadata
public final class test/A {
// source: 'test.kt'
public final @kotlin.jvm.JvmField field b: int
public field a: java.lang.String
public final @kotlin.jvm.JvmField field b: int
public method <init>(): void
public final @org.jetbrains.annotations.NotNull method getA(): java.lang.String
public final method setA(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
public final method g(): void
public final method f(): void
public final method g(): void
public final @org.jetbrains.annotations.NotNull method getA(): java.lang.String
public final method h(): void
}
public final method setA(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
}
@@ -3,9 +3,9 @@ public final enum class test/E {
// source: 'test.kt'
public final enum static field A: test.E
public final enum static field B: test.E
public static method values(): test.E[]
public static method valueOf(p0: java.lang.String): test.E
public static @org.jetbrains.annotations.NotNull method getEntries(): kotlin.enums.EnumEntries
public static method valueOf(p0: java.lang.String): test.E
public static method values(): test.E[]
}
@kotlin.Metadata
public synthetic final class test/Test$WhenMappings {