Deserialization/class reading: pass chosen JvmMetadataVersion whenever possible

This commit is contained in:
Mikhail Glukhikh
2022-12-02 16:38:59 +01:00
parent 52ab565cc6
commit 0c4a0360ac
67 changed files with 399 additions and 163 deletions
@@ -11,6 +11,7 @@ import com.intellij.psi.ClassFileViewProvider
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.load.kotlin.findKotlinClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
@@ -20,7 +21,10 @@ object ClsClassFinder {
val partsFinder = DirectoryBasedClassFinder(file.parent!!, packageFqName)
return partNames.mapNotNull {
partsFinder.findKotlinClass(ClassId(packageFqName, Name.identifier(it.substringAfterLast('/'))))
partsFinder.findKotlinClass(
ClassId(packageFqName, Name.identifier(it.substringAfterLast('/'))),
JvmMetadataVersion.INSTANCE
)
}
}
@@ -45,7 +45,7 @@ class ClsKotlinBinaryClassCache {
getKotlinBinaryFromCache(file)?.let {
return it.isKotlinBinary
}
return kotlinJvmBinaryClass(file, fileContent) != null
return kotlinJvmBinaryClass(file, fileContent, JvmMetadataVersion.INSTANCE) != null
}
fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? {
@@ -54,16 +54,19 @@ class ClsKotlinBinaryClassCache {
return null
}
return kotlinJvmBinaryClass(file, fileContent)
return kotlinJvmBinaryClass(file, fileContent, cached?.headerData?.metadataVersion ?: JvmMetadataVersion.INSTANCE)
}
private fun kotlinJvmBinaryClass(
file: VirtualFile,
fileContent: ByteArray?
fileContent: ByteArray?,
jvmMetadataVersion: JvmMetadataVersion
): KotlinJvmBinaryClass? {
if (ModelBranch.getFileBranch(file) != null) return null
val classFileContent = try {
KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file, fileContent)
KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(
file, jvmMetadataVersion, fileContent = fileContent
)
} catch (e: Exception) {
if (e is ControlFlowException) throw e
return null
@@ -94,7 +97,7 @@ class ClsKotlinBinaryClassCache {
}
}
val kotlinBinaryClass = kotlinJvmBinaryClass(file, fileContent) ?: return null
val kotlinBinaryClass = kotlinJvmBinaryClass(file, fileContent, JvmMetadataVersion.INSTANCE) ?: return null
return createHeaderInfo(kotlinBinaryClass)
}
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.classId
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder.Result.KotlinClass
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import java.io.InputStream
@@ -18,15 +19,16 @@ class DirectoryBasedClassFinder(
val packageDirectory: VirtualFile,
val directoryPackageFqName: FqName
) : KotlinClassFinder {
override fun findKotlinClassOrContent(javaClass: JavaClass): KotlinClassFinder.Result? = findKotlinClassOrContent(javaClass.classId!!)
override fun findKotlinClassOrContent(javaClass: JavaClass, jvmMetadataVersion: JvmMetadataVersion): KotlinClassFinder.Result? =
findKotlinClassOrContent(javaClass.classId!!, jvmMetadataVersion)
override fun findKotlinClassOrContent(classId: ClassId): KotlinClassFinder.Result? {
override fun findKotlinClassOrContent(classId: ClassId, jvmMetadataVersion: JvmMetadataVersion): KotlinClassFinder.Result? {
if (classId.packageFqName != directoryPackageFqName) {
return null
}
val targetName = classId.relativeClassName.pathSegments().joinToString("$", postfix = ".class")
val virtualFile = packageDirectory.findChild(targetName)
if (virtualFile != null && isKotlinWithCompatibleAbiVersion(virtualFile)) {
if (virtualFile != null && isKotlinWithCompatibleAbiVersion(virtualFile, jvmMetadataVersion)) {
return ClsKotlinBinaryClassCache.getInstance().getKotlinBinaryClass(virtualFile)?.let(::KotlinClass)
}
return null
@@ -45,12 +47,12 @@ class DirectoryBasedClassFinder(
/**
* Checks if this file is a compiled Kotlin class file ABI-compatible with the current plugin
*/
private fun isKotlinWithCompatibleAbiVersion(file: VirtualFile): Boolean {
private fun isKotlinWithCompatibleAbiVersion(file: VirtualFile, jvmMetadataVersion: JvmMetadataVersion): Boolean {
val clsKotlinBinaryClassCache = ClsKotlinBinaryClassCache.getInstance()
if (!clsKotlinBinaryClassCache.isKotlinJvmCompiledFile(file)) return false
val kotlinClass = clsKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file)
return kotlinClass != null && kotlinClass.metadataVersion.isCompatible()
return kotlinClass != null && kotlinClass.metadataVersion.isCompatible(jvmMetadataVersion)
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.decompiler.stub.file
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import org.jetbrains.kotlin.load.kotlin.findKotlinClass
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.ClassData
@@ -15,10 +16,11 @@ import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder
class DirectoryBasedDataFinder(
val classFinder: DirectoryBasedClassFinder,
val log: Logger
val log: Logger,
private val jvmMetadataVersion: JvmMetadataVersion
) : ClassDataFinder {
override fun findClassData(classId: ClassId): ClassData? {
val binaryClass = classFinder.findKotlinClass(classId) ?: return null
val binaryClass = classFinder.findKotlinClass(classId, jvmMetadataVersion) ?: return null
val classHeader = binaryClass.classHeader
val data = classHeader.data
if (data == null) {
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
@@ -32,7 +33,6 @@ import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.KClassValue
import org.jetbrains.kotlin.serialization.deserialization.getClassId
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager
open class KotlinClsStubBuilder : ClsStubBuilder() {
override fun getStubVersion() = ClassFileStubBuilder.STUB_VERSION + KotlinStubVersions.CLASSFILE_STUB_VERSION
@@ -60,11 +60,11 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
val classId = kotlinClass.classId
val packageFqName = header.packageName?.let { FqName(it) } ?: classId.packageFqName
if (!header.metadataVersion.isCompatible()) {
if (!header.metadataVersion.isCompatibleWithCurrentCompilerVersion()) {
return createIncompatibleAbiVersionFileStub()
}
val components = createStubBuilderComponents(file, packageFqName, fileContent)
val components = createStubBuilderComponents(file, packageFqName, fileContent, header.metadataVersion)
if (header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
val partFiles = ClsClassFinder.findMultifileClassParts(file, classId, header.multifilePartNames)
return createMultifileClassStub(header, partFiles, classId.asSingleFqName(), components)
@@ -105,10 +105,15 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
}
}
private fun createStubBuilderComponents(file: VirtualFile, packageFqName: FqName, fileContent: ByteArray): ClsStubBuilderComponents {
private fun createStubBuilderComponents(
file: VirtualFile,
packageFqName: FqName,
fileContent: ByteArray,
jvmMetadataVersion: JvmMetadataVersion
): ClsStubBuilderComponents {
val classFinder = DirectoryBasedClassFinder(file.parent!!, packageFqName)
val classDataFinder = DirectoryBasedDataFinder(classFinder, LOG)
val annotationLoader = AnnotationLoaderForClassFileStubBuilder(classFinder, file, fileContent)
val classDataFinder = DirectoryBasedDataFinder(classFinder, LOG, jvmMetadataVersion)
val annotationLoader = AnnotationLoaderForClassFileStubBuilder(classFinder, file, fileContent, jvmMetadataVersion)
return ClsStubBuilderComponents(classDataFinder, annotationLoader, file)
}
@@ -127,7 +132,8 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
private class AnnotationLoaderForClassFileStubBuilder(
kotlinClassFinder: KotlinClassFinder,
private val cachedFile: VirtualFile,
private val cachedFileContent: ByteArray
private val cachedFileContent: ByteArray,
override val jvmMetadataVersion: JvmMetadataVersion
) : AbstractBinaryClassAnnotationLoader<ClassId, AnnotationLoaderForClassFileStubBuilder.AnnotationsClassIdContainer>(kotlinClassFinder) {
private val storage =
@@ -16,10 +16,8 @@ import org.jetbrains.kotlin.contracts.ContractDeserializerImpl
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.kotlin.BinaryClassAnnotationAndConstantLoaderImpl
import org.jetbrains.kotlin.load.kotlin.JavaFlexibleTypeDeserializer
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.load.kotlin.findKotlinClass
import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
@@ -33,12 +31,13 @@ fun DeserializerForClassfileDecompiler(classFile: VirtualFile): DeserializerForC
ClsKotlinBinaryClassCache.getInstance().getKotlinBinaryClassHeaderData(classFile)
?: error("Decompiled data factory shouldn't be called on an unsupported file: $classFile")
val packageFqName = kotlinClassHeaderInfo.classId.packageFqName
return DeserializerForClassfileDecompiler(classFile.parent!!, packageFqName)
return DeserializerForClassfileDecompiler(classFile.parent!!, packageFqName, kotlinClassHeaderInfo.metadataVersion)
}
class DeserializerForClassfileDecompiler(
packageDirectory: VirtualFile,
directoryPackageFqName: FqName
directoryPackageFqName: FqName,
private val jvmMetadataVersion: JvmMetadataVersion
) : DeserializerForDecompilerBase(directoryPackageFqName) {
override val builtIns: KotlinBuiltIns get() = DefaultBuiltIns.Instance
@@ -47,10 +46,11 @@ class DeserializerForClassfileDecompiler(
override val deserializationComponents: DeserializationComponents
init {
val classDataFinder = DirectoryBasedDataFinder(classFinder, LOG)
val classDataFinder = DirectoryBasedDataFinder(classFinder, LOG, jvmMetadataVersion)
val notFoundClasses = NotFoundClasses(storageManager, moduleDescriptor)
val annotationAndConstantLoader =
BinaryClassAnnotationAndConstantLoaderImpl(moduleDescriptor, notFoundClasses, storageManager, classFinder)
val annotationAndConstantLoader = createBinaryClassAnnotationAndConstantLoader(
moduleDescriptor, notFoundClasses, storageManager, classFinder, jvmMetadataVersion
)
val configuration = object : DeserializationConfiguration {
override val readDeserializedContracts: Boolean = true
@@ -72,7 +72,7 @@ class DeserializerForClassfileDecompiler(
assert(packageFqName == directoryPackageFqName) {
"Was called for $facadeFqName; only members of $directoryPackageFqName package are expected."
}
val binaryClassForPackageClass = classFinder.findKotlinClass(ClassId.topLevel(facadeFqName))
val binaryClassForPackageClass = classFinder.findKotlinClass(ClassId.topLevel(facadeFqName), jvmMetadataVersion)
val header = binaryClassForPackageClass?.classHeader
val annotationData = header?.data
val strings = header?.strings
@@ -27,7 +27,7 @@ fun buildDecompiledTextForClassFile(
val classId = classHeader.classId
if (!classHeader.metadataVersion.isCompatible()) {
if (!classHeader.metadataVersion.isCompatibleWithCurrentCompilerVersion()) {
return createIncompatibleAbiVersionDecompiledText(JvmMetadataVersion.INSTANCE, classHeader.metadataVersion)
}