From a52017a1308d8fcca9dccf005c7934955bf0a2ce Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 2 Jun 2022 01:14:42 +0200 Subject: [PATCH] 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. --- .../codegen/AbstractLightAnalysisModeTest.kt | 60 ++++++++----------- .../BytecodeListingTextCollectingVisitor.kt | 13 ++-- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLightAnalysisModeTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLightAnalysisModeTest.kt index 71da202710b..b77577c8338 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLightAnalysisModeTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLightAnalysisModeTest.kt @@ -7,16 +7,17 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles 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.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.extensions.AnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension import org.jetbrains.kotlin.test.util.KtTestUtil.getAnnotationsJar import org.jetbrains.org.objectweb.asm.Opcodes.* +import org.jetbrains.org.objectweb.asm.tree.ClassNode import java.io.File abstract class AbstractLightAnalysisModeTest : CodegenTestCase() { @@ -66,40 +67,27 @@ abstract class AbstractLightAnalysisModeTest : CodegenTestCase() { private fun compileWithFullAnalysis(files: List): String { compile(files) - classFileFactory.getClassFiles() - - 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 - } - }) + return BytecodeListingTextCollectingVisitor.getText(classFileFactory, ListAnalysisFilter()) } - private open class ListAnalysisFilter : BytecodeListingTextCollectingVisitor.Filter { - override fun shouldWriteClass(access: Int, name: String) = when { - name.endsWith(MAPPINGS_CLASS_NAME_POSTFIX) && (access and ACC_SYNTHETIC != 0) && (access and ACC_FINAL != 0) -> false - name.contains("\$\$inlined") && (access and ACC_FINAL != 0) -> false - name.contains("\$sam\$") -> false - else -> true + private class ListAnalysisFilter : BytecodeListingTextCollectingVisitor.Filter { + @Suppress("UNCHECKED_CAST") + override fun shouldWriteClass(node: ClassNode): Boolean { + val metadata = node.visibleAnnotations.singleOrNull { it.desc == "Lkotlin/Metadata;" } + ?: error("No kotlin.Metadata generated for class ${node.name}") + 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).toTypedArray() + val d2 = (args["d2"] as List).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 { diff --git a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/codegen/BytecodeListingTextCollectingVisitor.kt b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/codegen/BytecodeListingTextCollectingVisitor.kt index 6c5e39e10a2..6197f4c12d8 100644 --- a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/codegen/BytecodeListingTextCollectingVisitor.kt +++ b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/codegen/BytecodeListingTextCollectingVisitor.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.test.KtAssert import org.jetbrains.org.objectweb.asm.* +import org.jetbrains.org.objectweb.asm.tree.ClassNode class BytecodeListingTextCollectingVisitor( val filter: Filter, @@ -26,10 +27,12 @@ class BytecodeListingTextCollectingVisitor( .sortedBy { it.relativePath } .mapNotNull { val cr = ClassReader(it.asByteArray()) + val node = ClassNode(Opcodes.API_VERSION) + cr.accept(node, ClassReader.SKIP_CODE) 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") private val CLASS_OR_FIELD_OR_METHOD = setOf(ModifierTarget.CLASS, ModifierTarget.FIELD, ModifierTarget.METHOD) @@ -61,20 +64,20 @@ class BytecodeListingTextCollectingVisitor( } interface Filter { - fun shouldWriteClass(access: Int, name: String): Boolean + fun shouldWriteClass(node: ClassNode): Boolean fun shouldWriteMethod(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 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 shouldWriteField(access: Int, name: String, desc: String) = true override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?) = true } 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 shouldWriteField(access: Int, name: String, desc: String): Boolean = true override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean = true