jvm-abi-gen: Do not sort stripped methods and fields

The order is visible to annotation processors and should be considered
part of the ABI of a class.
This commit is contained in:
Steven Schäfer
2021-11-30 20:45:53 +01:00
committed by Alexander Udalov
parent 077d10edef
commit b8625cd233
10 changed files with 33 additions and 206 deletions
@@ -16,9 +16,6 @@ import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtensio
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.AnnotationNode
import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.ParameterNode
import java.io.File
class JvmAbiOutputExtension(
@@ -46,7 +43,8 @@ class JvmAbiOutputExtension(
}
}
private class AbiOutputFiles(val abiClassInfos: Map<String, AbiClassInfo>, val outputFiles: OutputFileCollection) : OutputFileCollection {
private class AbiOutputFiles(val abiClassInfos: Map<String, AbiClassInfo>, val outputFiles: OutputFileCollection) :
OutputFileCollection {
override fun get(relativePath: String): OutputFile? {
error("AbiOutputFiles does not implement `get`.")
}
@@ -72,29 +70,46 @@ class JvmAbiOutputExtension(
val methodInfo = (abiInfo as AbiClassInfo.Stripped).methodInfo
val writer = ClassWriter(0)
ClassReader(outputFile.asByteArray()).accept(object : ClassVisitor(Opcodes.API_VERSION, writer) {
// We remember all non-inline declarations in the stripped class so that we can
// order them deterministically after all inline methods.
private val fieldNodes = mutableListOf<FieldNode>()
private val methodNodes = mutableListOf<StrippedMethodNode>()
// Strip private fields.
override fun visitField(access: Int, name: String?, descriptor: String?, signature: String?, value: Any?): FieldVisitor? {
override fun visitField(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
value: Any?
): FieldVisitor? {
if (access and Opcodes.ACC_PRIVATE != 0)
return null
return FieldNode(Opcodes.API_VERSION, access, name, descriptor, signature, value).also {
fieldNodes += it
}
return super.visitField(access, name, descriptor, signature, value)
}
override fun visitMethod(access: Int, name: String, descriptor: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
override fun visitMethod(
access: Int,
name: String,
descriptor: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor? {
val info = methodInfo[Method(name, descriptor)]
?: return null
if (info == AbiMethodInfo.KEEP)
return super.visitMethod(access, name, descriptor, signature, exceptions)
val visitor = super.visitMethod(access, name, descriptor, signature, exceptions)
return StrippedMethodNode(access, name, descriptor, signature, exceptions).also {
methodNodes += it
if (info == AbiMethodInfo.KEEP || access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) != 0)
return visitor
return object : MethodVisitor(Opcodes.API_VERSION, visitor) {
override fun visitCode() {
with(mv) {
visitCode()
visitInsn(Opcodes.ACONST_NULL)
visitInsn(Opcodes.ATHROW)
visitMaxs(0, 0)
visitEnd()
}
// Only instructions and locals follow after `visitCode`.
mv = null
}
}
}
@@ -121,18 +136,6 @@ class JvmAbiOutputExtension(
return delegate
return abiMetadataProcessor(delegate)
}
override fun visitEnd() {
fieldNodes.sortBy { "${it.name}${it.desc}" }
methodNodes.sortBy { "${it.name}${it.desc}" }
for (field in fieldNodes) {
field.accept(writer)
}
for (method in methodNodes) {
method.accept(writer)
}
super.visitEnd()
}
}, 0)
SimpleOutputBinaryFile(outputFile.sourceFiles, outputFile.relativePath, writer.toByteArray())
}
@@ -143,117 +146,3 @@ class JvmAbiOutputExtension(
}
}
}
private class StrippedMethodNode(
val access: Int,
val name: String,
val desc: String,
val signature: String?,
val exceptions: Array<out String>?,
) : MethodVisitor(Opcodes.API_VERSION) {
private class MethodAnnotation(
val visible: Boolean,
val node: AnnotationNode,
)
private class MethodTypeAnnotation(
val typeRef: Int,
val typePath: TypePath?,
val visible: Boolean,
val node: AnnotationNode,
)
private class MethodParameterAnnotation(
val parameter: Int,
val visible: Boolean,
val node: AnnotationNode,
)
private val parameters = mutableListOf<ParameterNode>()
private val annotations = mutableListOf<MethodAnnotation>()
private val typeAnnotations = mutableListOf<MethodTypeAnnotation>()
private var visibleAnnotableParameterCount = 0
private var invisibleAnnotableParameterCount = 0
private val parameterAnnotations = mutableListOf<MethodParameterAnnotation>()
private val attributes = mutableListOf<Attribute>()
override fun visitParameter(name: String?, access: Int) {
parameters.add(ParameterNode(name, access))
}
override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor =
AnnotationNode(Opcodes.API_VERSION, descriptor).also {
annotations.add(MethodAnnotation(visible, it))
}
override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, descriptor: String, visible: Boolean): AnnotationVisitor =
AnnotationNode(Opcodes.API_VERSION, descriptor).also {
typeAnnotations.add(MethodTypeAnnotation(typeRef, typePath, visible, it))
}
override fun visitAttribute(attribute: Attribute) {
attributes.add(attribute)
}
override fun visitAnnotableParameterCount(parameterCount: Int, visible: Boolean) {
if (visible) {
visibleAnnotableParameterCount = parameterCount
} else {
invisibleAnnotableParameterCount = parameterCount
}
}
override fun visitParameterAnnotation(parameter: Int, descriptor: String, visible: Boolean): AnnotationVisitor =
AnnotationNode(Opcodes.API_VERSION, descriptor).also {
parameterAnnotations.add(MethodParameterAnnotation(parameter, visible, it))
}
fun accept(classVisitor: ClassVisitor) {
val visitor = classVisitor.visitMethod(access, name, desc, signature, exceptions)
for (parameter in parameters) {
visitor.visitParameter(parameter.name, parameter.access)
}
for (annotation in annotations) {
visitor.visitAnnotation(annotation.node.desc, annotation.visible)?.let {
annotation.node.accept(it)
}
}
for (typeAnnotation in typeAnnotations) {
visitor.visitTypeAnnotation(
typeAnnotation.typeRef,
typeAnnotation.typePath,
typeAnnotation.node.desc,
typeAnnotation.visible,
)?.let {
typeAnnotation.node.accept(it)
}
}
visitor.visitAnnotableParameterCount(visibleAnnotableParameterCount, true)
visitor.visitAnnotableParameterCount(invisibleAnnotableParameterCount, false)
for (parameterAnnotation in parameterAnnotations) {
visitor.visitParameterAnnotation(
parameterAnnotation.parameter,
parameterAnnotation.node.desc,
parameterAnnotation.visible
)?.let {
parameterAnnotation.node.accept(it)
}
}
for (attribute in attributes) {
visitor.visitAttribute(attribute)
}
if (access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) == 0) {
visitor.visitCode()
visitor.visitInsn(Opcodes.ACONST_NULL)
visitor.visitInsn(Opcodes.ATHROW)
visitor.visitMaxs(0, parameters.size)
}
visitor.visitEnd()
}
}
@@ -55,11 +55,6 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest {
runTest("plugins/jvm-abi-gen/testData/compare/constant/");
}
@TestMetadata("declarationOrder")
public void testDeclarationOrder() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrder/");
}
@TestMetadata("declarationOrderInline")
public void testDeclarationOrderInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInline/");
@@ -70,11 +65,6 @@ public class CompareJvmAbiTestGenerated extends AbstractCompareJvmAbiTest {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInlineCall/");
}
@TestMetadata("declarationOrderInlineNonInline")
public void testDeclarationOrderInlineNonInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInlineNonInline/");
}
@TestMetadata("declarationOrderPrivateInline")
public void testDeclarationOrderPrivateInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderPrivateInline/");
@@ -55,11 +55,6 @@ public class IrCompareJvmAbiTestGenerated extends AbstractIrCompareJvmAbiTest {
runTest("plugins/jvm-abi-gen/testData/compare/constant/");
}
@TestMetadata("declarationOrder")
public void testDeclarationOrder() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrder/");
}
@TestMetadata("declarationOrderInline")
public void testDeclarationOrderInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInline/");
@@ -70,11 +65,6 @@ public class IrCompareJvmAbiTestGenerated extends AbstractIrCompareJvmAbiTest {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInlineCall/");
}
@TestMetadata("declarationOrderInlineNonInline")
public void testDeclarationOrderInlineNonInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInlineNonInline/");
}
@TestMetadata("declarationOrderPrivateInline")
public void testDeclarationOrderPrivateInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderPrivateInline/");
@@ -55,11 +55,6 @@ public class LegacyCompareJvmAbiTestGenerated extends AbstractLegacyCompareJvmAb
runTest("plugins/jvm-abi-gen/testData/compare/constant/");
}
@TestMetadata("declarationOrder")
public void testDeclarationOrder() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrder/");
}
@TestMetadata("declarationOrderInline")
public void testDeclarationOrderInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInline/");
@@ -70,11 +65,6 @@ public class LegacyCompareJvmAbiTestGenerated extends AbstractLegacyCompareJvmAb
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInlineCall/");
}
@TestMetadata("declarationOrderInlineNonInline")
public void testDeclarationOrderInlineNonInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderInlineNonInline/");
}
@TestMetadata("declarationOrderPrivateInline")
public void testDeclarationOrderPrivateInline() throws Exception {
runTest("plugins/jvm-abi-gen/testData/compare/declarationOrderPrivateInline/");
@@ -1,9 +0,0 @@
package test
class A {
fun f() = 1
val x = 2
}
val y = 3
fun g() = 4
@@ -1 +0,0 @@
// IGNORE_BACKEND_LEGACY: JVM
@@ -1,9 +0,0 @@
package test
fun g() = 4
val y = 3
class A {
val x = 2
fun f() = 1
}
@@ -1,6 +0,0 @@
package test
inline fun f() = 1
fun g() = 2
fun h() = 3
@@ -1 +0,0 @@
// IGNORE_BACKEND_LEGACY: JVM
@@ -1,6 +0,0 @@
package test
inline fun f() = 1
fun h() = 3
fun g() = 2