Move IDEKotlinBinaryClassCache from intellij
This commit is contained in:
+68
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.file.builder
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
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.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
object ClsClassFinder {
|
||||||
|
fun findMultifileClassParts(file: VirtualFile, classId: ClassId, partNames: List<String>): List<KotlinJvmBinaryClass> {
|
||||||
|
val packageFqName = classId.packageFqName
|
||||||
|
val partsFinder = DirectoryBasedClassFinder(file.parent!!, packageFqName)
|
||||||
|
|
||||||
|
return partNames.mapNotNull {
|
||||||
|
partsFinder.findKotlinClass(ClassId(packageFqName, Name.identifier(it.substringAfterLast('/'))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this file is a compiled "internal" Kotlin class, i.e. a Kotlin class (not necessarily ABI-compatible with the current plugin)
|
||||||
|
* which should NOT be decompiled (and, as a result, shown under the library in the Project view, be searchable via Find class, etc.)
|
||||||
|
*/
|
||||||
|
fun isKotlinInternalCompiledFile(file: VirtualFile, fileContent: ByteArray? = null): Boolean {
|
||||||
|
// Don't crash on invalid files (EA-97751)
|
||||||
|
if (!file.isValid || fileContent?.size == 0 || !file.exists()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val clsKotlinBinaryClassCache = ClsKotlinBinaryClassCache.getInstance()
|
||||||
|
|
||||||
|
if (!clsKotlinBinaryClassCache.isKotlinJvmCompiledFile(file, fileContent)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val innerClass =
|
||||||
|
try {
|
||||||
|
if (fileContent == null) {
|
||||||
|
ClassFileViewProvider.isInnerClass(file)
|
||||||
|
} else {
|
||||||
|
ClassFileViewProvider.isInnerClass(file, fileContent)
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Logger
|
||||||
|
.getInstance("org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsClassFinder.isKotlinInternalCompiledFile")
|
||||||
|
.debug(file.path, exception)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (innerClass) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val header = clsKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file, fileContent) ?: return false
|
||||||
|
if (header.classId.isLocal) return true
|
||||||
|
|
||||||
|
return header.kind == KotlinClassHeader.Kind.SYNTHETIC_CLASS ||
|
||||||
|
header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|
||||||
|
}
|
||||||
|
}
|
||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.file.builder
|
||||||
|
|
||||||
|
import com.intellij.ide.highlighter.JavaClassFileType
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileWithId
|
||||||
|
import com.intellij.reference.SoftReference
|
||||||
|
import org.jetbrains.kotlin.deserialization.common.jvm.impl.KotlinBinaryClassCache
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
|
||||||
|
class ClsKotlinBinaryClassCache {
|
||||||
|
class KotlinBinaryClassHeaderData(
|
||||||
|
val classId: ClassId,
|
||||||
|
val kind: KotlinClassHeader.Kind,
|
||||||
|
val metadataVersion: JvmMetadataVersion,
|
||||||
|
val partNamesIfMultifileFacade: List<String>,
|
||||||
|
val packageName: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class KotlinBinaryData(val isKotlinBinary: Boolean, val timestamp: Long, val headerData: KotlinBinaryClassHeaderData?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this file is a compiled Kotlin class file (not necessarily ABI-compatible with the current plugin)
|
||||||
|
*/
|
||||||
|
fun isKotlinJvmCompiledFile(file: VirtualFile, fileContent: ByteArray? = null): Boolean {
|
||||||
|
if (file.extension != JavaClassFileType.INSTANCE!!.defaultExtension) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
getKotlinBinaryFromCache(file)?.let {
|
||||||
|
return it.isKotlinBinary
|
||||||
|
}
|
||||||
|
return kotlinJvmBinaryClass(file, fileContent) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? {
|
||||||
|
val cached = getKotlinBinaryFromCache(file)
|
||||||
|
if (cached != null && !cached.isKotlinBinary) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return kotlinJvmBinaryClass(file, fileContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun kotlinJvmBinaryClass(
|
||||||
|
file: VirtualFile,
|
||||||
|
fileContent: ByteArray?
|
||||||
|
): KotlinJvmBinaryClass? {
|
||||||
|
val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClassOrClassFileContent(file, fileContent)?.toKotlinJvmBinaryClass()
|
||||||
|
|
||||||
|
val isKotlinBinaryClass = kotlinBinaryClass != null
|
||||||
|
if (file is VirtualFileWithId) {
|
||||||
|
attributeService.writeBooleanAttribute(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE, file, isKotlinBinaryClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isKotlinBinaryClass) {
|
||||||
|
val headerInfo = createHeaderInfo(kotlinBinaryClass!!)
|
||||||
|
file.putUserData(KOTLIN_BINARY_DATA_KEY, SoftReference(KotlinBinaryData(isKotlinBinaryClass, file.timeStamp, headerInfo)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return kotlinBinaryClass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKotlinBinaryClassHeaderData(file: VirtualFile, fileContent: ByteArray? = null): KotlinBinaryClassHeaderData? {
|
||||||
|
getKotlinBinaryFromCache(file)?.let { cached ->
|
||||||
|
if (!cached.isKotlinBinary) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (cached.headerData != null) {
|
||||||
|
return cached.headerData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinBinaryClass = kotlinJvmBinaryClass(file, fileContent) ?: return null
|
||||||
|
return createHeaderInfo(kotlinBinaryClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val attributeService = ApplicationManager.getApplication().getService(FileAttributeService::class.java)
|
||||||
|
|
||||||
|
private fun createHeaderInfo(kotlinBinaryClass: KotlinJvmBinaryClass): KotlinBinaryClassHeaderData {
|
||||||
|
val classId = kotlinBinaryClass.classId
|
||||||
|
|
||||||
|
return kotlinBinaryClass.classHeader.toLightHeader(classId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KotlinClassHeader.toLightHeader(classId: ClassId) =
|
||||||
|
KotlinBinaryClassHeaderData(
|
||||||
|
classId, kind, metadataVersion, multifilePartNames, packageName
|
||||||
|
)
|
||||||
|
|
||||||
|
private val KOTLIN_IS_COMPILED_FILE_ATTRIBUTE: String = "kotlin-is-binary-compiled".apply {
|
||||||
|
attributeService.register(this, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val KOTLIN_BINARY_DATA_KEY = Key.create<SoftReference<KotlinBinaryData>>(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE)
|
||||||
|
|
||||||
|
private fun getKotlinBinaryFromCache(file: VirtualFile): KotlinBinaryData? {
|
||||||
|
val userData = file.getUserData(KOTLIN_BINARY_DATA_KEY)?.get()
|
||||||
|
if (userData != null && userData.timestamp == file.timeStamp) {
|
||||||
|
return userData
|
||||||
|
}
|
||||||
|
|
||||||
|
val isKotlinBinaryAttribute = if (file is VirtualFileWithId) {
|
||||||
|
attributeService.readBooleanAttribute(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE, file)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isKotlinBinaryAttribute != null) {
|
||||||
|
val isKotlinBinary = isKotlinBinaryAttribute.value
|
||||||
|
val kotlinBinaryData = KotlinBinaryData(isKotlinBinary, file.timeStamp, null)
|
||||||
|
if (isKotlinBinary) {
|
||||||
|
file.putUserData(KOTLIN_BINARY_DATA_KEY, SoftReference(kotlinBinaryData))
|
||||||
|
}
|
||||||
|
|
||||||
|
return kotlinBinaryData
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getInstance(): ClsKotlinBinaryClassCache =
|
||||||
|
ApplicationManager.getApplication().getService(ClsKotlinBinaryClassCache::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-4
@@ -9,6 +9,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
|||||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||||
import org.jetbrains.kotlin.load.java.structure.classId
|
import org.jetbrains.kotlin.load.java.structure.classId
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder.Result.KotlinClass
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@@ -26,7 +27,7 @@ class DirectoryBasedClassFinder(
|
|||||||
val targetName = classId.relativeClassName.pathSegments().joinToString("$", postfix = ".class")
|
val targetName = classId.relativeClassName.pathSegments().joinToString("$", postfix = ".class")
|
||||||
val virtualFile = packageDirectory.findChild(targetName)
|
val virtualFile = packageDirectory.findChild(targetName)
|
||||||
if (virtualFile != null && isKotlinWithCompatibleAbiVersion(virtualFile)) {
|
if (virtualFile != null && isKotlinWithCompatibleAbiVersion(virtualFile)) {
|
||||||
return IDEKotlinBinaryClassCache.getInstance().getKotlinBinaryClass(virtualFile)?.let(::KotlinClass)
|
return ClsKotlinBinaryClassCache.getInstance().getKotlinBinaryClass(virtualFile)?.let(::KotlinClass)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -45,10 +46,10 @@ class DirectoryBasedClassFinder(
|
|||||||
* Checks if this file is a compiled Kotlin class file ABI-compatible with the current plugin
|
* 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): Boolean {
|
||||||
val ideKotlinBinaryClassCache = IDEKotlinBinaryClassCache.getInstance()
|
val clsKotlinBinaryClassCache = ClsKotlinBinaryClassCache.getInstance()
|
||||||
if (!ideKotlinBinaryClassCache.isKotlinJvmCompiledFile(file)) return false
|
if (!clsKotlinBinaryClassCache.isKotlinJvmCompiledFile(file)) return false
|
||||||
|
|
||||||
val kotlinClass = ideKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file)
|
val kotlinClass = clsKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file)
|
||||||
return kotlinClass != null && kotlinClass.metadataVersion.isCompatible()
|
return kotlinClass != null && kotlinClass.metadataVersion.isCompatible()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.stubs.file.builder
|
||||||
|
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import java.io.DataInput
|
||||||
|
import java.io.DataOutput
|
||||||
|
|
||||||
|
data class CachedAttributeData<out T>(val value: T, val timeStamp: Long)
|
||||||
|
|
||||||
|
interface FileAttributeService {
|
||||||
|
fun register(id: String, version: Int, fixedSize: Boolean = true) {}
|
||||||
|
|
||||||
|
fun <T : Enum<T>> writeEnumAttribute(id: String, file: VirtualFile, value: T): CachedAttributeData<T> =
|
||||||
|
CachedAttributeData(value, timeStamp = file.timeStamp)
|
||||||
|
|
||||||
|
fun <T : Enum<T>> readEnumAttribute(id: String, file: VirtualFile, klass: Class<T>): CachedAttributeData<T>? = null
|
||||||
|
|
||||||
|
fun writeBooleanAttribute(id: String, file: VirtualFile, value: Boolean): CachedAttributeData<Boolean> =
|
||||||
|
CachedAttributeData(value, timeStamp = file.timeStamp)
|
||||||
|
|
||||||
|
fun readBooleanAttribute(id: String, file: VirtualFile): CachedAttributeData<Boolean>? = null
|
||||||
|
|
||||||
|
fun <T> write(file: VirtualFile, id: String, value: T, writeValueFun: (DataOutput, T) -> Unit): CachedAttributeData<T>
|
||||||
|
|
||||||
|
fun <T> read(file: VirtualFile, id: String, readValueFun: (DataInput) -> T): CachedAttributeData<T>?
|
||||||
|
}
|
||||||
+4
-3
@@ -13,6 +13,7 @@ import com.intellij.psi.stubs.PsiFileStub
|
|||||||
import com.intellij.util.indexing.FileContent
|
import com.intellij.util.indexing.FileContent
|
||||||
import org.jetbrains.kotlin.SpecialJvmAnnotations
|
import org.jetbrains.kotlin.SpecialJvmAnnotations
|
||||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||||
|
import org.jetbrains.kotlin.deserialization.common.jvm.impl.VirtualFileKotlinClass
|
||||||
import org.jetbrains.kotlin.load.kotlin.*
|
import org.jetbrains.kotlin.load.kotlin.*
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||||
@@ -37,7 +38,7 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
|
|||||||
override fun buildFileStub(content: FileContent): PsiFileStub<*>? {
|
override fun buildFileStub(content: FileContent): PsiFileStub<*>? {
|
||||||
val virtualFile = content.file
|
val virtualFile = content.file
|
||||||
|
|
||||||
if (isKotlinInternalCompiledFile(virtualFile, content.content)) {
|
if (ClsClassFinder.isKotlinInternalCompiledFile(virtualFile, content.content)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun doBuildFileStub(file: VirtualFile, fileContent: ByteArray): PsiFileStub<KtFile>? {
|
private fun doBuildFileStub(file: VirtualFile, fileContent: ByteArray): PsiFileStub<KtFile>? {
|
||||||
val kotlinClass: KotlinJvmBinaryClass = IDEKotlinBinaryClassCache.getInstance().getKotlinBinaryClass(file, fileContent)
|
val kotlinClass = ClsKotlinBinaryClassCache.getInstance().getKotlinBinaryClass(file, fileContent)
|
||||||
?: error("Can't find binary class for Kotlin file: $file")
|
?: error("Can't find binary class for Kotlin file: $file")
|
||||||
val header = kotlinClass.classHeader
|
val header = kotlinClass.classHeader
|
||||||
val classId = kotlinClass.classId
|
val classId = kotlinClass.classId
|
||||||
@@ -63,7 +64,7 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
|
|||||||
|
|
||||||
val components = createStubBuilderComponents(file, packageFqName, fileContent)
|
val components = createStubBuilderComponents(file, packageFqName, fileContent)
|
||||||
if (header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
|
if (header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
|
||||||
val partFiles = findMultifileClassParts(file, classId, header.multifilePartNames)
|
val partFiles = ClsClassFinder.findMultifileClassParts(file, classId, header.multifilePartNames)
|
||||||
return createMultifileClassStub(header, partFiles, classId.asSingleFqName(), components)
|
return createMultifileClassStub(header, partFiles, classId.asSingleFqName(), components)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user