jvm-abi-gen: keep InnerClass attributes for all referenced types
^KT-55233 Fixed
This commit is contained in:
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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/");
|
||||
|
||||
Generated
+5
@@ -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/");
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user