Store information is file was compiled with Kotlin in attributes (KT-10312)
- Add boolean attribute for all binary files in the project - Stop using JarUserDataManager for full file indexing - Store full header of files compiled with Kotlin in file UserData
This commit is contained in:
committed by
Nikolay Krasko
parent
5431c6937f
commit
adbf098166
@@ -18,13 +18,18 @@ package org.jetbrains.kotlin.idea.caches
|
|||||||
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
|
||||||
data class CachedAttributeData<T: Enum<T>>(val value: T?, val timeStamp: Long)
|
data class CachedAttributeData<T>(val value: T, val timeStamp: Long)
|
||||||
|
|
||||||
interface FileAttributeService {
|
interface FileAttributeService {
|
||||||
fun register(id: String, version: Int) {}
|
fun register(id: String, version: Int) {}
|
||||||
|
|
||||||
fun <T: Enum<T>> writeAttribute(id: String, file: VirtualFile, value: T): CachedAttributeData<T> =
|
fun <T: Enum<T>> writeEnumAttribute(id: String, file: VirtualFile, value: T): CachedAttributeData<T> =
|
||||||
CachedAttributeData(value, timeStamp = file.timeStamp)
|
CachedAttributeData(value, timeStamp = file.timeStamp)
|
||||||
|
|
||||||
fun <T: Enum<T>> readAttribute(id: String, file: VirtualFile, klass: Class<T>): CachedAttributeData<T>? = null
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.caches
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.ServiceManager
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
|
||||||
|
object IDEKotlinBinaryClassCache {
|
||||||
|
data class KotlinBinaryHeaderData(val classHeader: KotlinClassHeader, val classId: ClassId)
|
||||||
|
data class KotlinBinaryData(val isKotlinBinary: Boolean, val timestamp: Long, val headerData: KotlinBinaryHeaderData?)
|
||||||
|
|
||||||
|
fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? {
|
||||||
|
val cached = getKotlinBinaryFromCache(file)
|
||||||
|
if (cached != null && !cached.isKotlinBinary) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClass(file, fileContent)
|
||||||
|
|
||||||
|
val isKotlinBinaryClass = kotlinBinaryClass != null
|
||||||
|
attributeService.writeBooleanAttribute(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE, file, isKotlinBinaryClass)
|
||||||
|
|
||||||
|
if (isKotlinBinaryClass) {
|
||||||
|
val headerInfo = createHeaderInfo(kotlinBinaryClass)
|
||||||
|
file.putUserData(KOTLIN_BINARY_DATA_KEY, KotlinBinaryData(isKotlinBinaryClass, file.timeStamp, headerInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return kotlinBinaryClass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKotlinBinaryClassHeaderData(file: VirtualFile, fileContent: ByteArray? = null): KotlinBinaryHeaderData? {
|
||||||
|
val cached = getKotlinBinaryFromCache(file)
|
||||||
|
if (cached != null) {
|
||||||
|
if (!cached.isKotlinBinary) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (cached.headerData != null) {
|
||||||
|
return cached.headerData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinBinaryClass = getKotlinBinaryClass(file, fileContent)
|
||||||
|
return createHeaderInfo(kotlinBinaryClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val attributeService = ServiceManager.getService(FileAttributeService::class.java)
|
||||||
|
|
||||||
|
private fun createHeaderInfo(kotlinBinaryClass: KotlinJvmBinaryClass?): KotlinBinaryHeaderData? {
|
||||||
|
val header = kotlinBinaryClass?.classHeader
|
||||||
|
val classId = kotlinBinaryClass?.classId
|
||||||
|
|
||||||
|
return if (header != null && classId != null) KotlinBinaryHeaderData(header, classId) else null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val KOTLIN_IS_COMPILED_FILE_ATTRIBUTE: String = "kotlin-is-binary-compiled".apply {
|
||||||
|
ServiceManager.getService(FileAttributeService::class.java).register(this, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val KOTLIN_BINARY_DATA_KEY = Key.create<KotlinBinaryData>(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE)
|
||||||
|
|
||||||
|
private fun getKotlinBinaryFromCache(file: VirtualFile): KotlinBinaryData? {
|
||||||
|
val userData = file.getUserData(KOTLIN_BINARY_DATA_KEY)
|
||||||
|
if (userData != null && userData.timestamp == file.timeStamp) {
|
||||||
|
return userData
|
||||||
|
}
|
||||||
|
|
||||||
|
val isKotlinBinaryAttribute = attributeService.readBooleanAttribute(KOTLIN_IS_COMPILED_FILE_ATTRIBUTE, file)
|
||||||
|
|
||||||
|
if (isKotlinBinaryAttribute != null) {
|
||||||
|
val isKotlinBinary = isKotlinBinaryAttribute.value
|
||||||
|
val kotlinBinaryData = KotlinBinaryData(isKotlinBinary, file.timeStamp, null)
|
||||||
|
if (isKotlinBinary) {
|
||||||
|
file.putUserData(KOTLIN_BINARY_DATA_KEY, kotlinBinaryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return kotlinBinaryData
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,8 +52,8 @@ object JarUserDataManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stored == null && fileAttributeService != null) {
|
if (stored == null && fileAttributeService != null) {
|
||||||
val savedData = fileAttributeService.readAttribute(counter.key.toString(), localJarFile, State::class.java)
|
val savedData = fileAttributeService.readEnumAttribute(counter.key.toString(), localJarFile, State::class.java)
|
||||||
if (savedData != null && savedData.value != null) {
|
if (savedData != null) {
|
||||||
val hasFileWithProperty = savedData.value == State.HAS_FILE
|
val hasFileWithProperty = savedData.value == State.HAS_FILE
|
||||||
|
|
||||||
storeUserData(counter, localJarFile, hasFileWithProperty, savedData.timeStamp)
|
storeUserData(counter, localJarFile, hasFileWithProperty, savedData.timeStamp)
|
||||||
@@ -91,7 +91,7 @@ object JarUserDataManager {
|
|||||||
|
|
||||||
val state = if (hasFileWithProperty) State.HAS_FILE else State.NO_FILE
|
val state = if (hasFileWithProperty) State.HAS_FILE else State.NO_FILE
|
||||||
|
|
||||||
val savedData = fileAttributeService?.writeAttribute(counter.key.toString(), localJarFile, state)
|
val savedData = fileAttributeService?.writeEnumAttribute(counter.key.toString(), localJarFile, state)
|
||||||
|
|
||||||
storeUserData(counter, localJarFile, hasFileWithProperty, (savedData?.timeStamp ?: localJarFile.timeStamp))
|
storeUserData(counter, localJarFile, hasFileWithProperty, (savedData?.timeStamp ?: localJarFile.timeStamp))
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-20
@@ -17,15 +17,25 @@
|
|||||||
package org.jetbrains.kotlin.idea.decompiler.classFile
|
package org.jetbrains.kotlin.idea.decompiler.classFile
|
||||||
|
|
||||||
import com.intellij.ide.highlighter.JavaClassFileType
|
import com.intellij.ide.highlighter.JavaClassFileType
|
||||||
|
import com.intellij.openapi.components.ServiceManager
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.ClassFileViewProvider
|
import com.intellij.psi.ClassFileViewProvider
|
||||||
import org.jetbrains.kotlin.idea.caches.JarUserDataManager
|
import org.jetbrains.kotlin.idea.caches.FileAttributeService
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
import org.jetbrains.kotlin.idea.caches.IDEKotlinBinaryClassCache
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
data class IsKotlinBinary(val isKotlinBinary: Boolean, val timestamp: Long)
|
||||||
|
|
||||||
|
val KOTLIN_COMPILED_FILE_ATTRIBUTE: String = "kotlin-compiled-file".apply {
|
||||||
|
ServiceManager.getService(FileAttributeService::class.java).register(this, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val KEY = Key.create<IsKotlinBinary>(KOTLIN_COMPILED_FILE_ATTRIBUTE)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this file is a compiled Kotlin class file (not necessarily ABI-compatible with the current plugin)
|
* Checks if this file is a compiled Kotlin class file (not necessarily ABI-compatible with the current plugin)
|
||||||
*/
|
*/
|
||||||
@@ -34,12 +44,7 @@ fun isKotlinJvmCompiledFile(file: VirtualFile): Boolean {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasCompiledKotlinInJar.isInNoKotlinJar(file)) {
|
return IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file) != null
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file)?.classHeader
|
|
||||||
return header != null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +53,7 @@ fun isKotlinJvmCompiledFile(file: VirtualFile): Boolean {
|
|||||||
fun isKotlinWithCompatibleAbiVersion(file: VirtualFile): Boolean {
|
fun isKotlinWithCompatibleAbiVersion(file: VirtualFile): Boolean {
|
||||||
if (!isKotlinJvmCompiledFile(file)) return false
|
if (!isKotlinJvmCompiledFile(file)) return false
|
||||||
|
|
||||||
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file)?.classHeader
|
val header = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file)?.classHeader
|
||||||
return header != null && header.isCompatibleAbiVersion
|
return header != null && header.isCompatibleAbiVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,24 +69,17 @@ fun isKotlinInternalCompiledFile(file: VirtualFile): Boolean {
|
|||||||
if (ClassFileViewProvider.isInnerClass(file)) {
|
if (ClassFileViewProvider.isInnerClass(file)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
val header = KotlinBinaryClassCache.getKotlinBinaryClass(file)?.classHeader ?: return false
|
val header = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file)?.classHeader ?: return false
|
||||||
|
|
||||||
return header.kind == KotlinClassHeader.Kind.SYNTHETIC_CLASS ||
|
return header.kind == KotlinClassHeader.Kind.SYNTHETIC_CLASS ||
|
||||||
header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART ||
|
header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART ||
|
||||||
header.isLocalClass || header.syntheticClassKind == "PACKAGE_PART"
|
header.isLocalClass || header.syntheticClassKind == "PACKAGE_PART"
|
||||||
}
|
}
|
||||||
|
|
||||||
object HasCompiledKotlinInJar : JarUserDataManager.JarBooleanPropertyCounter(HasCompiledKotlinInJar::class.simpleName!!) {
|
fun findMultifileClassParts(file: VirtualFile, classId: ClassId, kotlinClassHeader: KotlinClassHeader): List<KotlinJvmBinaryClass> {
|
||||||
override fun hasProperty(file: VirtualFile) = isKotlinJvmCompiledFile(file)
|
val packageFqName = classId.packageFqName
|
||||||
|
|
||||||
fun isInNoKotlinJar(file: VirtualFile): Boolean =
|
|
||||||
JarUserDataManager.hasFileWithProperty(HasCompiledKotlinInJar, file) == false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findMultifileClassParts(file: VirtualFile, multifileClass: KotlinJvmBinaryClass): List<KotlinJvmBinaryClass> {
|
|
||||||
val packageFqName = multifileClass.classId.packageFqName
|
|
||||||
val partsFinder = DirectoryBasedClassFinder(file.parent!!, packageFqName)
|
val partsFinder = DirectoryBasedClassFinder(file.parent!!, packageFqName)
|
||||||
val partNames = multifileClass.classHeader.filePartClassNames ?: return emptyList()
|
val partNames = kotlinClassHeader.filePartClassNames ?: return emptyList()
|
||||||
return partNames.mapNotNull {
|
return partNames.mapNotNull {
|
||||||
partsFinder.findKotlinClass(ClassId(packageFqName, Name.identifier(it.substringAfterLast('/'))))
|
partsFinder.findKotlinClass(ClassId(packageFqName, Name.identifier(it.substringAfterLast('/'))))
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.decompiler.classFile
|
|||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.IDEKotlinBinaryClassCache
|
||||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DeserializerForDecompilerBase
|
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DeserializerForDecompilerBase
|
||||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
|
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
|
||||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassResolver
|
import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassResolver
|
||||||
@@ -37,9 +38,9 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
|
|||||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
||||||
|
|
||||||
fun DeserializerForClassfileDecompiler(classFile: VirtualFile): DeserializerForClassfileDecompiler {
|
fun DeserializerForClassfileDecompiler(classFile: VirtualFile): DeserializerForClassfileDecompiler {
|
||||||
val kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(classFile)
|
val kotlinClassHeaderInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(classFile)
|
||||||
assert(kotlinClass != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile }
|
assert(kotlinClassHeaderInfo != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile }
|
||||||
val packageFqName = kotlinClass!!.classId.packageFqName
|
val packageFqName = kotlinClassHeaderInfo!!.classId.packageFqName
|
||||||
return DeserializerForClassfileDecompiler(classFile.parent!!, packageFqName)
|
return DeserializerForClassfileDecompiler(classFile.parent!!, packageFqName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +104,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 KotlinBinaryClassCache.getKotlinBinaryClass(virtualFile)
|
return IDEKotlinBinaryClassCache.getKotlinBinaryClass(virtualFile)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -21,6 +21,7 @@ import com.intellij.openapi.roots.FileIndexFacade
|
|||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
import com.intellij.psi.compiled.ClassFileDecompilers
|
import com.intellij.psi.compiled.ClassFileDecompilers
|
||||||
|
import org.jetbrains.kotlin.idea.caches.IDEKotlinBinaryClassCache
|
||||||
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
|
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
|
||||||
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
||||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
||||||
@@ -28,7 +29,6 @@ import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolverForDecompiler
|
|||||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
|
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
|
||||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
|
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
|
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
|
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind
|
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind
|
||||||
@@ -79,10 +79,10 @@ fun buildDecompiledTextForClassFile(
|
|||||||
classFile: VirtualFile,
|
classFile: VirtualFile,
|
||||||
resolver: ResolverForDecompiler = DeserializerForClassfileDecompiler(classFile)
|
resolver: ResolverForDecompiler = DeserializerForClassfileDecompiler(classFile)
|
||||||
): DecompiledText {
|
): DecompiledText {
|
||||||
val kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(classFile)
|
val kotlinClassHeaderInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(classFile)
|
||||||
assert(kotlinClass != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile }
|
assert(kotlinClassHeaderInfo != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile }
|
||||||
val classId = kotlinClass!!.classId
|
val classId = kotlinClassHeaderInfo!!.classId
|
||||||
val classHeader = kotlinClass.classHeader
|
val classHeader = kotlinClassHeaderInfo.classHeader
|
||||||
val packageFqName = classId.packageFqName
|
val packageFqName = classId.packageFqName
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
@@ -98,7 +98,7 @@ fun buildDecompiledTextForClassFile(
|
|||||||
classHeader.isCompatibleClassKind() ->
|
classHeader.isCompatibleClassKind() ->
|
||||||
buildDecompiledText(packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)), decompilerRendererForClassFiles)
|
buildDecompiledText(packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)), decompilerRendererForClassFiles)
|
||||||
classHeader.isCompatibleMultifileClassKind() -> {
|
classHeader.isCompatibleMultifileClassKind() -> {
|
||||||
val partClasses = findMultifileClassParts(classFile, kotlinClass)
|
val partClasses = findMultifileClassParts(classFile, classId, classHeader)
|
||||||
val partMembers = partClasses.flatMap { partClass -> resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName()) }
|
val partMembers = partClasses.flatMap { partClass -> resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName()) }
|
||||||
buildDecompiledText(packageFqName, partMembers, decompilerRendererForClassFiles)
|
buildDecompiledText(packageFqName, partMembers, decompilerRendererForClassFiles)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -24,10 +24,10 @@ import com.intellij.psi.stubs.PsiFileStub
|
|||||||
import com.intellij.util.indexing.FileContent
|
import com.intellij.util.indexing.FileContent
|
||||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||||
|
import org.jetbrains.kotlin.idea.caches.IDEKotlinBinaryClassCache
|
||||||
import org.jetbrains.kotlin.idea.decompiler.stubBuilder.*
|
import org.jetbrains.kotlin.idea.decompiler.stubBuilder.*
|
||||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
|
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
|
||||||
import org.jetbrains.kotlin.load.kotlin.AbstractBinaryClassAnnotationAndConstantLoader
|
import org.jetbrains.kotlin.load.kotlin.AbstractBinaryClassAnnotationAndConstantLoader
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
|
import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind
|
||||||
@@ -58,9 +58,9 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun doBuildFileStub(file: VirtualFile): PsiFileStub<KtFile>? {
|
fun doBuildFileStub(file: VirtualFile): PsiFileStub<KtFile>? {
|
||||||
val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClass(file)!!
|
val kotlinClassHeaderInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file)!!
|
||||||
val header = kotlinBinaryClass.classHeader
|
val header = kotlinClassHeaderInfo.classHeader
|
||||||
val classId = kotlinBinaryClass.classId
|
val classId = kotlinClassHeaderInfo.classId
|
||||||
val packageFqName = classId.packageFqName
|
val packageFqName = classId.packageFqName
|
||||||
if (!header.isCompatibleAbiVersion) {
|
if (!header.isCompatibleAbiVersion) {
|
||||||
return createIncompatibleAbiVersionFileStub()
|
return createIncompatibleAbiVersionFileStub()
|
||||||
@@ -68,8 +68,8 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
|
|||||||
|
|
||||||
val components = createStubBuilderComponents(file, packageFqName)
|
val components = createStubBuilderComponents(file, packageFqName)
|
||||||
if (header.isCompatibleMultifileClassKind()) {
|
if (header.isCompatibleMultifileClassKind()) {
|
||||||
val partFiles = findMultifileClassParts(file, kotlinBinaryClass)
|
val partFiles = findMultifileClassParts(file, classId, header)
|
||||||
return createMultifileClassStub(kotlinBinaryClass, partFiles, classId.asSingleFqName(), components)
|
return createMultifileClassStub(header, partFiles, classId.asSingleFqName(), components)
|
||||||
}
|
}
|
||||||
|
|
||||||
val annotationData = header.annotationData
|
val annotationData = header.annotationData
|
||||||
|
|||||||
+3
-2
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
|||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
|
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
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 org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -74,13 +75,13 @@ fun createFileFacadeStub(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createMultifileClassStub(
|
fun createMultifileClassStub(
|
||||||
multifileClass: KotlinJvmBinaryClass,
|
kotlinClassHeader: KotlinClassHeader,
|
||||||
partFiles: List<KotlinJvmBinaryClass>,
|
partFiles: List<KotlinJvmBinaryClass>,
|
||||||
facadeFqName: FqName,
|
facadeFqName: FqName,
|
||||||
components: ClsStubBuilderComponents
|
components: ClsStubBuilderComponents
|
||||||
): KotlinFileStubImpl {
|
): KotlinFileStubImpl {
|
||||||
val packageFqName = facadeFqName.parent()
|
val packageFqName = facadeFqName.parent()
|
||||||
val partNames = multifileClass.classHeader.filePartClassNames?.asList()?.map { it.substringAfterLast('/') }
|
val partNames = kotlinClassHeader.filePartClassNames?.asList()?.map { it.substringAfterLast('/') }
|
||||||
val fileStub = KotlinFileStubForIde.forMultifileClassStub(facadeFqName, partNames, packageFqName.isRoot)
|
val fileStub = KotlinFileStubForIde.forMultifileClassStub(facadeFqName, partNames, packageFqName.isRoot)
|
||||||
setupFileStub(fileStub, packageFqName)
|
setupFileStub(fileStub, packageFqName)
|
||||||
for (partFile in partFiles) {
|
for (partFile in partFiles) {
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ import com.intellij.ide.highlighter.JavaClassFileType
|
|||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.util.indexing.*
|
import com.intellij.util.indexing.*
|
||||||
import com.intellij.util.io.KeyDescriptor
|
import com.intellij.util.io.KeyDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.IDEKotlinBinaryClassCache
|
||||||
import org.jetbrains.kotlin.idea.decompiler.js.JsMetaFileUtils
|
import org.jetbrains.kotlin.idea.decompiler.js.JsMetaFileUtils
|
||||||
import org.jetbrains.kotlin.idea.decompiler.js.KotlinJavaScriptMetaFileType
|
import org.jetbrains.kotlin.idea.decompiler.js.KotlinJavaScriptMetaFileType
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import java.io.DataInput
|
import java.io.DataInput
|
||||||
import java.io.DataOutput
|
import java.io.DataOutput
|
||||||
@@ -78,8 +78,8 @@ object KotlinClassFileIndex : KotlinFileIndexBase<KotlinClassFileIndex>(KotlinCl
|
|||||||
private val VERSION = 2
|
private val VERSION = 2
|
||||||
|
|
||||||
private val INDEXER = indexer() { fileContent ->
|
private val INDEXER = indexer() { fileContent ->
|
||||||
val kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(fileContent.file, fileContent.content)
|
val kotlinClassHeaderInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(fileContent.file, fileContent.content)
|
||||||
if (kotlinClass != null && kotlinClass.classHeader.isCompatibleAbiVersion) kotlinClass.classId.asSingleFqName() else null
|
if (kotlinClassHeaderInfo != null && kotlinClassHeaderInfo.classHeader.isCompatibleAbiVersion) kotlinClassHeaderInfo.classId.asSingleFqName() else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
|
|||||||
import com.intellij.openapi.updateSettings.impl.UpdateChecker;
|
import com.intellij.openapi.updateSettings.impl.UpdateChecker;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.idea.caches.IDEKotlinBinaryClassCache;
|
||||||
import org.jetbrains.kotlin.idea.caches.JarUserDataManager;
|
import org.jetbrains.kotlin.idea.caches.JarUserDataManager;
|
||||||
import org.jetbrains.kotlin.idea.debugger.filter.DebuggerFiltersUtilKt;
|
import org.jetbrains.kotlin.idea.debugger.filter.DebuggerFiltersUtilKt;
|
||||||
import org.jetbrains.kotlin.idea.decompiler.classFile.HasCompiledKotlinInJar;
|
|
||||||
import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil;
|
import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil;
|
||||||
import org.jetbrains.kotlin.utils.PathUtil;
|
import org.jetbrains.kotlin.utils.PathUtil;
|
||||||
|
|
||||||
@@ -53,7 +53,6 @@ public class PluginStartupComponent implements ApplicationComponent {
|
|||||||
registerPathVariable();
|
registerPathVariable();
|
||||||
|
|
||||||
JarUserDataManager.INSTANCE.register(KotlinJavaScriptLibraryDetectionUtil.HasKotlinJSMetadataInJar.INSTANCE);
|
JarUserDataManager.INSTANCE.register(KotlinJavaScriptLibraryDetectionUtil.HasKotlinJSMetadataInJar.INSTANCE);
|
||||||
JarUserDataManager.INSTANCE.register(HasCompiledKotlinInJar.INSTANCE);
|
|
||||||
|
|
||||||
DebuggerFiltersUtilKt.addKotlinStdlibDebugFilterIfNeeded();
|
DebuggerFiltersUtilKt.addKotlinStdlibDebugFilterIfNeeded();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import com.intellij.openapi.vfs.VirtualFile
|
|||||||
import com.intellij.openapi.vfs.newvfs.FileAttribute
|
import com.intellij.openapi.vfs.newvfs.FileAttribute
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import com.intellij.util.io.DataInputOutputUtil
|
import com.intellij.util.io.DataInputOutputUtil
|
||||||
|
import java.io.DataInput
|
||||||
|
import java.io.DataOutput
|
||||||
|
|
||||||
class FileAttributeServiceImpl : FileAttributeService {
|
class FileAttributeServiceImpl : FileAttributeService {
|
||||||
val attributes: MutableMap<String, FileAttribute> = ContainerUtil.newConcurrentMap()
|
val attributes: MutableMap<String, FileAttribute> = ContainerUtil.newConcurrentMap()
|
||||||
@@ -28,29 +30,54 @@ class FileAttributeServiceImpl : FileAttributeService {
|
|||||||
attributes[id] = FileAttribute(id, version, true)
|
attributes[id] = FileAttribute(id, version, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T: Enum<T>> writeAttribute(id: String, file: VirtualFile, value: T): CachedAttributeData<T> {
|
override fun <T: Enum<T>> writeEnumAttribute(id: String, file: VirtualFile, value: T): CachedAttributeData<T> {
|
||||||
|
return write(file, id, value) { output, v ->
|
||||||
|
DataInputOutputUtil.writeINT(output, v.ordinal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T: Enum<T>> readEnumAttribute(id: String, file: VirtualFile, klass: Class<T>): CachedAttributeData<T>? {
|
||||||
|
return read(file, id) { input ->
|
||||||
|
deserializeEnumValue(DataInputOutputUtil.readINT(input), klass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun writeBooleanAttribute(id: String, file: VirtualFile, value: Boolean): CachedAttributeData<Boolean> {
|
||||||
|
return write(file, id, value) { output, v ->
|
||||||
|
DataInputOutputUtil.writeINT(output, if (v) 1 else 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readBooleanAttribute(id: String, file: VirtualFile): CachedAttributeData<Boolean>? {
|
||||||
|
return read(file, id) { input ->
|
||||||
|
DataInputOutputUtil.readINT(input) > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T> write(file: VirtualFile, id: String, value: T, writeValueFun: (DataOutput, T) -> Unit): CachedAttributeData<T> {
|
||||||
val attribute = attributes[id] ?: throw IllegalArgumentException("Attribute with $id wasn't registered")
|
val attribute = attributes[id] ?: throw IllegalArgumentException("Attribute with $id wasn't registered")
|
||||||
|
|
||||||
val data = CachedAttributeData(value, timeStamp = file.timeStamp)
|
val data = CachedAttributeData(value, timeStamp = file.timeStamp)
|
||||||
|
|
||||||
attribute.writeAttribute(file).use {
|
attribute.writeAttribute(file).use {
|
||||||
DataInputOutputUtil.writeTIME(it, data.timeStamp)
|
DataInputOutputUtil.writeTIME(it, data.timeStamp)
|
||||||
DataInputOutputUtil.writeINT(it, data.value?.ordinal ?: -1)
|
writeValueFun(it, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T: Enum<T>> readAttribute(id: String, file: VirtualFile, klass: Class<T>): CachedAttributeData<T>? {
|
private inline fun <T> read(file: VirtualFile, id: String, readValueFun: (DataInput) -> T): CachedAttributeData<T>? {
|
||||||
val attribute = attributes[id] ?: throw IllegalArgumentException("Attribute with $id wasn't registered")
|
val attribute = attributes[id] ?: throw IllegalArgumentException("Attribute with $id wasn't registered")
|
||||||
|
|
||||||
val stream = attribute.readAttribute(file) ?: return null
|
val stream = attribute.readAttribute(file) ?: return null
|
||||||
return stream.use {
|
return stream.use {
|
||||||
val timeStamp = DataInputOutputUtil.readTIME(it)
|
val timeStamp = DataInputOutputUtil.readTIME(it)
|
||||||
val intValue = DataInputOutputUtil.readINT(it)
|
val value = readValueFun(it)
|
||||||
|
|
||||||
if (file.timeStamp == timeStamp) {
|
if (file.timeStamp == timeStamp) {
|
||||||
CachedAttributeData(deserializeEnumValue(intValue, klass), timeStamp)
|
CachedAttributeData(value, timeStamp)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
null
|
null
|
||||||
@@ -60,7 +87,10 @@ class FileAttributeServiceImpl : FileAttributeService {
|
|||||||
|
|
||||||
private fun <T: Enum<T>> deserializeEnumValue(i: Int, klass: Class<T>): T {
|
private fun <T: Enum<T>> deserializeEnumValue(i: Int, klass: Class<T>): T {
|
||||||
val method = klass.getMethod("values")
|
val method = klass.getMethod("values")
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
val values = method.invoke(null) as Array<T>
|
val values = method.invoke(null) as Array<T>
|
||||||
|
|
||||||
return values[i]
|
return values[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -18,15 +18,15 @@ package org.jetbrains.kotlin.idea.decompiler
|
|||||||
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
|
import org.jetbrains.kotlin.idea.caches.IDEKotlinBinaryClassCache
|
||||||
import org.jetbrains.kotlin.idea.decompiler.navigation.NavigateToDecompiledLibraryTest
|
import org.jetbrains.kotlin.idea.decompiler.navigation.NavigateToDecompiledLibraryTest
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
|
|
||||||
abstract class AbstractInternalCompiledClassesTest : KotlinLightCodeInsightFixtureTestCase() {
|
abstract class AbstractInternalCompiledClassesTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
private fun isFileWithHeader(predicate: (KotlinClassHeader) -> Boolean) : VirtualFile.() -> Boolean = {
|
private fun isFileWithHeader(predicate: (KotlinClassHeader) -> Boolean) : VirtualFile.() -> Boolean = {
|
||||||
val header = KotlinBinaryClassCache.getKotlinBinaryClass(this)?.classHeader
|
val header = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(this)?.classHeader
|
||||||
header != null && predicate(header)
|
header != null && predicate(header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user