Sort class members in jvm-abi-gen
#KT-64589 Fixed
This commit is contained in:
committed by
Space Team
parent
1a46b053f2
commit
ad9c100137
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user