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.codegen.extensions.ClassFileFactoryFinalizerExtension
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
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.Method
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.Remapper
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class JvmAbiOutputExtension(
|
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) :
|
private class AbiOutputFiles(val abiClassInfos: Map<String, AbiClassInfo>, val outputFiles: OutputFileCollection) :
|
||||||
OutputFileCollection {
|
OutputFileCollection {
|
||||||
override fun get(relativePath: String): OutputFile? {
|
override fun get(relativePath: String): OutputFile? {
|
||||||
@@ -68,8 +72,14 @@ class JvmAbiOutputExtension(
|
|||||||
|
|
||||||
else -> /* abiInfo is AbiClassInfo.Stripped */ {
|
else -> /* abiInfo is AbiClassInfo.Stripped */ {
|
||||||
val methodInfo = (abiInfo as AbiClassInfo.Stripped).methodInfo
|
val methodInfo = (abiInfo as AbiClassInfo.Stripped).methodInfo
|
||||||
|
val innerClassInfos = mutableMapOf<String, InnerClassInfo>()
|
||||||
|
val innerClassesToKeep = abiClassInfos.keys.toMutableSet()
|
||||||
val writer = ClassWriter(0)
|
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.
|
// Strip private fields.
|
||||||
override fun visitField(
|
override fun visitField(
|
||||||
access: Int,
|
access: Int,
|
||||||
@@ -123,10 +133,10 @@ class JvmAbiOutputExtension(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove inner classes which are not present in the abi jar.
|
// Remove inner classes which are not present in the abi jar.
|
||||||
override fun visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) {
|
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
|
||||||
if (name in abiClassInfos.keys) {
|
// `visitInnerClass` is called before `visitField`/`visitMethod`, so we don't know
|
||||||
super.visitInnerClass(name, outerName, innerName, access)
|
// which types are referenced by kept methods yet.
|
||||||
}
|
innerClassInfos[name] = InnerClassInfo(name, outerName, innerName, access)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip private declarations from the Kotlin Metadata annotation.
|
// Strip private declarations from the Kotlin Metadata annotation.
|
||||||
@@ -136,7 +146,18 @@ class JvmAbiOutputExtension(
|
|||||||
return delegate
|
return delegate
|
||||||
return abiMetadataProcessor(delegate)
|
return abiMetadataProcessor(delegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitEnd() {}
|
||||||
}, 0)
|
}, 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())
|
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/");
|
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")
|
@TestMetadata("kt50005")
|
||||||
public void testKt50005() throws Exception {
|
public void testKt50005() throws Exception {
|
||||||
runTest("plugins/jvm-abi-gen/testData/content/kt50005/");
|
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/");
|
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")
|
@TestMetadata("kt50005")
|
||||||
public void testKt50005() throws Exception {
|
public void testKt50005() throws Exception {
|
||||||
runTest("plugins/jvm-abi-gen/testData/content/kt50005/");
|
runTest("plugins/jvm-abi-gen/testData/content/kt50005/");
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ public final class test/Class$NestedInnerClass$NestedNestedInnerClass {
|
|||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
final class test/Class$NestedInnerClass {
|
final class test/Class$NestedInnerClass {
|
||||||
// source: 'classes.kt'
|
// source: 'classes.kt'
|
||||||
private final inner class test/Class$NestedInnerClass
|
|
||||||
public final inner class test/Class$NestedInnerClass$NestedNestedInnerClass
|
public final inner class test/Class$NestedInnerClass$NestedNestedInnerClass
|
||||||
|
private final inner class test/Class$NestedInnerClass
|
||||||
public method <init>(): void
|
public method <init>(): void
|
||||||
}
|
}
|
||||||
@kotlin.Metadata
|
@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