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:
+24
-36
@@ -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 {
|
||||||
|
|||||||
+8
-5
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user