jvm-abi-gen: keep InnerClass attributes for all referenced types

^KT-55233 Fixed
This commit is contained in:
pyos
2022-12-02 12:38:24 +01:00
committed by Alexander Udalov
parent 93ec8dd63d
commit 26a7ac6d92
9 changed files with 111 additions and 6 deletions
@@ -15,7 +15,9 @@ import org.jetbrains.kotlin.codegen.ClassFileFactory
import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
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 java.io.File
class JvmAbiOutputExtension(
@@ -43,6 +45,8 @@ class JvmAbiOutputExtension(
}
}
private class InnerClassInfo(val name: String, val outerName: String?, val innerName: String?, val access: Int)
private class AbiOutputFiles(val abiClassInfos: Map<String, AbiClassInfo>, val outputFiles: OutputFileCollection) :
OutputFileCollection {
override fun get(relativePath: String): OutputFile? {
@@ -68,8 +72,14 @@ class JvmAbiOutputExtension(
else -> /* abiInfo is AbiClassInfo.Stripped */ {
val methodInfo = (abiInfo as AbiClassInfo.Stripped).methodInfo
val innerClassInfos = mutableMapOf<String, InnerClassInfo>()
val innerClassesToKeep = abiClassInfos.keys.toMutableSet()
val writer = ClassWriter(0)
ClassReader(outputFile.asByteArray()).accept(object : ClassVisitor(Opcodes.API_VERSION, writer) {
val remapper = ClassRemapper(writer, object : Remapper() {
override fun map(internalName: String): String =
internalName.also { innerClassesToKeep.add(it) }
})
ClassReader(outputFile.asByteArray()).accept(object : ClassVisitor(Opcodes.API_VERSION, remapper) {
// Strip private fields.
override fun visitField(
access: Int,
@@ -123,10 +133,10 @@ class JvmAbiOutputExtension(
}
// Remove inner classes which are not present in the abi jar.
override fun visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) {
if (name in abiClassInfos.keys) {
super.visitInnerClass(name, outerName, innerName, access)
}
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
// `visitInnerClass` is called before `visitField`/`visitMethod`, so we don't know
// which types are referenced by kept methods yet.
innerClassInfos[name] = InnerClassInfo(name, outerName, innerName, access)
}
// Strip private declarations from the Kotlin Metadata annotation.
@@ -136,7 +146,18 @@ class JvmAbiOutputExtension(
return delegate
return abiMetadataProcessor(delegate)
}
override fun visitEnd() {}
}, 0)
for (name in innerClassesToKeep.toList()) {
val info = innerClassInfos[name] ?: continue
generateSequence(info) { it.outerName?.takeIf(innerClassesToKeep::add)?.let(innerClassInfos::get) }
.forEach { writer.visitInnerClass(it.name, it.outerName, it.innerName, it.access) }
}
writer.visitEnd()
SimpleOutputBinaryFile(outputFile.sourceFiles, outputFile.relativePath, writer.toByteArray())
}
}
@@ -55,6 +55,11 @@ public class JvmAbiContentTestGenerated extends AbstractJvmAbiContentTest {
runTest("plugins/jvm-abi-gen/testData/content/class/");
}
@TestMetadata("innerClasses")
public void testInnerClasses() throws Exception {
runTest("plugins/jvm-abi-gen/testData/content/innerClasses/");
}
@TestMetadata("kt50005")
public void testKt50005() throws Exception {
runTest("plugins/jvm-abi-gen/testData/content/kt50005/");
@@ -55,6 +55,11 @@ public class LegacyJvmAbiContentTestGenerated extends AbstractLegacyJvmAbiConten
runTest("plugins/jvm-abi-gen/testData/content/class/");
}
@TestMetadata("innerClasses")
public void testInnerClasses() throws Exception {
runTest("plugins/jvm-abi-gen/testData/content/innerClasses/");
}
@TestMetadata("kt50005")
public void testKt50005() throws Exception {
runTest("plugins/jvm-abi-gen/testData/content/kt50005/");
+1 -1
View File
@@ -28,8 +28,8 @@ public final class test/Class$NestedInnerClass$NestedNestedInnerClass {
@kotlin.Metadata
final class test/Class$NestedInnerClass {
// source: 'classes.kt'
private final inner class test/Class$NestedInnerClass
public final inner class test/Class$NestedInnerClass$NestedNestedInnerClass
private final inner class test/Class$NestedInnerClass
public method <init>(): void
}
@kotlin.Metadata
@@ -0,0 +1,3 @@
public @interface Anno {
Class<?> value();
}
@@ -0,0 +1,6 @@
public class Outer {
public static class Middle {
public static class Inner {
}
}
}
@@ -0,0 +1 @@
// IGNORE_BACKEND_LEGACY: JVM_IR
@@ -0,0 +1,14 @@
@Anno(Outer.Middle.Inner::class)
class InAnnotation {}
class InPublicMethod {
fun foo(x: Outer.Middle.Inner) {}
}
class InPrivateMethod {
private fun foo(x: Outer.Middle.Inner) {}
}
class InInlineMethod {
inline fun foo(): Class<*> = Outer.Middle.Inner::class.java
}
@@ -0,0 +1,50 @@
public annotation class Anno {
// source: 'Anno.java'
public abstract method value(): java.lang.Class
}
@Anno(value=Outer$Middle$Inner::class)
@kotlin.Metadata
public final class InAnnotation {
// source: 'innerClasses.kt'
public inner class Outer$Middle$Inner
public inner class Outer$Middle
public method <init>(): void
}
@kotlin.Metadata
public final class InInlineMethod {
// source: 'innerClasses.kt'
public inner class Outer$Middle$Inner
public inner class Outer$Middle
public method <init>(): void
public final @org.jetbrains.annotations.NotNull method foo(): java.lang.Class
}
@kotlin.Metadata
public final class InPrivateMethod {
// source: 'innerClasses.kt'
public method <init>(): void
}
@kotlin.Metadata
public final class InPublicMethod {
// source: 'innerClasses.kt'
public inner class Outer$Middle$Inner
public inner class Outer$Middle
public method <init>(): void
public final method foo(@org.jetbrains.annotations.NotNull p0: Outer$Middle$Inner): void
}
public class Outer$Middle$Inner {
// source: 'Outer.java'
public inner class Outer$Middle
public inner class Outer$Middle$Inner
public method <init>(): void
}
public class Outer$Middle {
// source: 'Outer.java'
public inner class Outer$Middle
public inner class Outer$Middle$Inner
public method <init>(): void
}
public class Outer {
// source: 'Outer.java'
public inner class Outer$Middle
public method <init>(): void
}