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:
Nikolay Krasko
2016-01-11 18:10:53 +03:00
committed by Nikolay Krasko
parent 5431c6937f
commit adbf098166
12 changed files with 190 additions and 56 deletions
@@ -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))
} }
@@ -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('/'))))
} }
@@ -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
} }
@@ -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)
} }
@@ -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
@@ -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]
} }
} }
@@ -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)
} }