Rewrite light analysis mode test class filter

Instead of filtering local/synthetic classes based on ClassDescriptor
instances, do it by interpreting kotlin.Metadata. This is needed to
enable these tests for JVM IR, where descriptors are not available in
this way.
This commit is contained in:
Alexander Udalov
2022-06-02 01:14:42 +02:00
parent 448cba07bb
commit a52017a130
2 changed files with 32 additions and 41 deletions
@@ -7,16 +7,17 @@ package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.`when`.WhenByEnumsMapping.MAPPINGS_CLASS_NAME_POSTFIX
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
import org.jetbrains.kotlin.test.util.KtTestUtil.getAnnotationsJar import org.jetbrains.kotlin.test.util.KtTestUtil.getAnnotationsJar
import org.jetbrains.org.objectweb.asm.Opcodes.* import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import java.io.File import java.io.File
abstract class AbstractLightAnalysisModeTest : CodegenTestCase() { abstract class AbstractLightAnalysisModeTest : CodegenTestCase() {
@@ -66,40 +67,27 @@ abstract class AbstractLightAnalysisModeTest : CodegenTestCase() {
private fun compileWithFullAnalysis(files: List<TestFile>): String { private fun compileWithFullAnalysis(files: List<TestFile>): String {
compile(files) compile(files)
classFileFactory.getClassFiles() return BytecodeListingTextCollectingVisitor.getText(classFileFactory, ListAnalysisFilter())
val classInternalNames = classFileFactory.generationState.bindingContext
.getSliceContents(CodegenBinding.ASM_TYPE).map { it.value.internalName to it.key }.toMap()
return BytecodeListingTextCollectingVisitor.getText(classFileFactory, object : ListAnalysisFilter() {
override fun shouldWriteClass(access: Int, name: String): Boolean {
val classDescriptor = classInternalNames[name]
if (classDescriptor != null && shouldFilterClass(classDescriptor)) {
return false
}
return super.shouldWriteClass(access, name)
}
override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean {
val classDescriptor = classInternalNames[name]
if (classDescriptor != null && shouldFilterClass(classDescriptor)) {
return false
}
return super.shouldWriteInnerClass(name, outerName, innerName)
}
private fun shouldFilterClass(descriptor: ClassDescriptor): Boolean {
return descriptor.visibility == DescriptorVisibilities.LOCAL || descriptor is SyntheticClassDescriptorForLambda
}
})
} }
private open class ListAnalysisFilter : BytecodeListingTextCollectingVisitor.Filter { private class ListAnalysisFilter : BytecodeListingTextCollectingVisitor.Filter {
override fun shouldWriteClass(access: Int, name: String) = when { @Suppress("UNCHECKED_CAST")
name.endsWith(MAPPINGS_CLASS_NAME_POSTFIX) && (access and ACC_SYNTHETIC != 0) && (access and ACC_FINAL != 0) -> false override fun shouldWriteClass(node: ClassNode): Boolean {
name.contains("\$\$inlined") && (access and ACC_FINAL != 0) -> false val metadata = node.visibleAnnotations.singleOrNull { it.desc == "Lkotlin/Metadata;" }
name.contains("\$sam\$") -> false ?: error("No kotlin.Metadata generated for class ${node.name}")
else -> true val args = metadata.values.chunked(2).associate { (x, y) -> x to y }
val kind = args["k"] as Int
return when (Kind.getById(kind)) {
Kind.UNKNOWN -> error(node.name)
Kind.CLASS -> {
val d1 = (args["d1"] as List<String>).toTypedArray()
val d2 = (args["d2"] as List<String>).toTypedArray()
val (_, proto) = JvmProtoBufUtil.readClassDataFrom(d1, d2)
Flags.VISIBILITY.get(proto.flags) != ProtoBuf.Visibility.LOCAL
}
Kind.FILE_FACADE, Kind.MULTIFILE_CLASS, Kind.MULTIFILE_CLASS_PART -> true
Kind.SYNTHETIC_CLASS -> false
}
} }
override fun shouldWriteMethod(access: Int, name: String, desc: String) = when { override fun shouldWriteMethod(access: Int, name: String, desc: String) = when {
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.test.KtAssert import org.jetbrains.kotlin.test.KtAssert
import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.tree.ClassNode
class BytecodeListingTextCollectingVisitor( class BytecodeListingTextCollectingVisitor(
val filter: Filter, val filter: Filter,
@@ -26,10 +27,12 @@ class BytecodeListingTextCollectingVisitor(
.sortedBy { it.relativePath } .sortedBy { it.relativePath }
.mapNotNull { .mapNotNull {
val cr = ClassReader(it.asByteArray()) val cr = ClassReader(it.asByteArray())
val node = ClassNode(Opcodes.API_VERSION)
cr.accept(node, ClassReader.SKIP_CODE)
val visitor = BytecodeListingTextCollectingVisitor(filter, withSignatures, withAnnotations = withAnnotations) val visitor = BytecodeListingTextCollectingVisitor(filter, withSignatures, withAnnotations = withAnnotations)
cr.accept(visitor, ClassReader.SKIP_CODE) node.accept(visitor)
if (!filter.shouldWriteClass(cr.access, cr.className)) null else visitor.text if (!filter.shouldWriteClass(node)) null else visitor.text
}.joinToString("\n\n", postfix = "\n") }.joinToString("\n\n", postfix = "\n")
private val CLASS_OR_FIELD_OR_METHOD = setOf(ModifierTarget.CLASS, ModifierTarget.FIELD, ModifierTarget.METHOD) private val CLASS_OR_FIELD_OR_METHOD = setOf(ModifierTarget.CLASS, ModifierTarget.FIELD, ModifierTarget.METHOD)
@@ -61,20 +64,20 @@ class BytecodeListingTextCollectingVisitor(
} }
interface Filter { interface Filter {
fun shouldWriteClass(access: Int, name: String): Boolean fun shouldWriteClass(node: ClassNode): Boolean
fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean
fun shouldWriteField(access: Int, name: String, desc: String): Boolean fun shouldWriteField(access: Int, name: String, desc: String): Boolean
fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean
object EMPTY : Filter { object EMPTY : Filter {
override fun shouldWriteClass(access: Int, name: String) = true override fun shouldWriteClass(node: ClassNode) = true
override fun shouldWriteMethod(access: Int, name: String, desc: String) = true override fun shouldWriteMethod(access: Int, name: String, desc: String) = true
override fun shouldWriteField(access: Int, name: String, desc: String) = true override fun shouldWriteField(access: Int, name: String, desc: String) = true
override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?) = true override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?) = true
} }
object ForCodegenTests : Filter { object ForCodegenTests : Filter {
override fun shouldWriteClass(access: Int, name: String): Boolean = !name.startsWith("helpers/") override fun shouldWriteClass(node: ClassNode): Boolean = !node.name.startsWith("helpers/")
override fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean = true override fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean = true
override fun shouldWriteField(access: Int, name: String, desc: String): Boolean = true override fun shouldWriteField(access: Int, name: String, desc: String): Boolean = true
override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean = true override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean = true