Fix: Don't index and decompile incompatible .knm files from K/N KLIBs
Issue #KT-34159
This commit is contained in:
@@ -41,12 +41,12 @@ fun createLoggingErrorReporter(log: Logger) = LoggingErrorReporter(log)
|
|||||||
internal object CachingIdeKonanLibraryMetadataLoader : PackageAccessHandler {
|
internal object CachingIdeKonanLibraryMetadataLoader : PackageAccessHandler {
|
||||||
override fun loadModuleHeader(library: KotlinLibrary): KlibMetadataProtoBuf.Header {
|
override fun loadModuleHeader(library: KotlinLibrary): KlibMetadataProtoBuf.Header {
|
||||||
val virtualFile = getVirtualFile(library, library.moduleHeaderFile)
|
val virtualFile = getVirtualFile(library, library.moduleHeaderFile)
|
||||||
return cache.getCachedModuleHeader(virtualFile)
|
return cache.getCachedModuleHeader(virtualFile)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadPackageFragment(library: KotlinLibrary, packageFqName: String, partName: String): ProtoBuf.PackageFragment {
|
override fun loadPackageFragment(library: KotlinLibrary, packageFqName: String, partName: String): ProtoBuf.PackageFragment {
|
||||||
val virtualFile = getVirtualFile(library, library.packageFragmentFile(packageFqName, partName))
|
val virtualFile = getVirtualFile(library, library.packageFragmentFile(packageFqName, partName))
|
||||||
return cache.getCachedPackageFragment(virtualFile)
|
return cache.getCachedPackageFragment(virtualFile)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getVirtualFile(library: KotlinLibrary, file: KFile): VirtualFile =
|
private fun getVirtualFile(library: KotlinLibrary, file: KFile): VirtualFile =
|
||||||
|
|||||||
+61
-22
@@ -9,12 +9,13 @@ import com.intellij.openapi.application.ApplicationManager
|
|||||||
import com.intellij.openapi.components.BaseComponent
|
import com.intellij.openapi.components.BaseComponent
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.util.containers.ContainerUtil.createConcurrentWeakValueMap
|
import com.intellij.util.containers.ContainerUtil.createConcurrentWeakValueMap
|
||||||
import org.jetbrains.kotlin.library.KLIB_METADATA_FILE_EXTENSION
|
import org.jetbrains.kotlin.library.*
|
||||||
import org.jetbrains.kotlin.library.KLIB_MODULE_METADATA_FILE_NAME
|
|
||||||
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
|
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
|
||||||
import org.jetbrains.kotlin.library.metadata.parseModuleHeader
|
import org.jetbrains.kotlin.library.metadata.parseModuleHeader
|
||||||
import org.jetbrains.kotlin.library.metadata.parsePackageFragment
|
import org.jetbrains.kotlin.library.metadata.parsePackageFragment
|
||||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class KotlinNativeLoadingMetadataCache : BaseComponent {
|
class KotlinNativeLoadingMetadataCache : BaseComponent {
|
||||||
|
|
||||||
@@ -33,34 +34,72 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
|
|||||||
constructor(virtualFile: VirtualFile) : this(virtualFile.url, virtualFile.modificationStamp)
|
constructor(virtualFile: VirtualFile) : this(virtualFile.url, virtualFile.modificationStamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val packageFragmentCache = createConcurrentWeakValueMap<CacheKey, ProtoBuf.PackageFragment>()
|
// ConcurrentWeakValueHashMap does not allow null values.
|
||||||
private val moduleHeaderCache = createConcurrentWeakValueMap<CacheKey, KlibMetadataProtoBuf.Header>()
|
private class CacheValue<T : Any>(val value: T?)
|
||||||
|
|
||||||
fun getCachedPackageFragment(virtualFile: VirtualFile): ProtoBuf.PackageFragment =
|
private val packageFragmentCache = createConcurrentWeakValueMap<CacheKey, CacheValue<ProtoBuf.PackageFragment>>()
|
||||||
packageFragmentCache.computeIfAbsent(CacheKey(virtualFile.ensurePackageMetadataFile)) {
|
private val moduleHeaderCache = createConcurrentWeakValueMap<CacheKey, CacheValue<KlibMetadataProtoBuf.Header>>()
|
||||||
virtualFile.createPackageFragmentCacheEntry
|
private val libraryVersioningCache = createConcurrentWeakValueMap<CacheKey, CacheValue<KonanLibraryVersioning>>()
|
||||||
|
|
||||||
|
fun getCachedPackageFragment(packageFragmentFile: VirtualFile): ProtoBuf.PackageFragment? {
|
||||||
|
check(packageFragmentFile.extension == KLIB_METADATA_FILE_EXTENSION) {
|
||||||
|
"Not a package metadata file: $packageFragmentFile"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCachedModuleHeader(virtualFile: VirtualFile): KlibMetadataProtoBuf.Header =
|
return packageFragmentCache.computeIfAbsent(CacheKey(packageFragmentFile)) {
|
||||||
moduleHeaderCache.computeIfAbsent(CacheKey(virtualFile.ensureModuleHeaderFile)) {
|
CacheValue(computePackageFragment(packageFragmentFile))
|
||||||
virtualFile.createModuleHeaderCacheEntry
|
}.value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCachedModuleHeader(moduleHeaderFile: VirtualFile): KlibMetadataProtoBuf.Header? {
|
||||||
|
check(moduleHeaderFile.name == KLIB_MODULE_METADATA_FILE_NAME) {
|
||||||
|
"Not a module header file: $moduleHeaderFile"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val VirtualFile.createPackageFragmentCacheEntry
|
return moduleHeaderCache.computeIfAbsent(CacheKey(moduleHeaderFile)) {
|
||||||
get() = parsePackageFragment(contentsToByteArray(false))
|
CacheValue(computeModuleHeader(moduleHeaderFile))
|
||||||
|
}.value
|
||||||
|
}
|
||||||
|
|
||||||
private val VirtualFile.createModuleHeaderCacheEntry
|
private fun isAbiCompatible(libraryRoot: VirtualFile): Boolean {
|
||||||
get() = parseModuleHeader(contentsToByteArray(false))
|
val manifestFile = libraryRoot.findChild(KLIB_MANIFEST_FILE_NAME) ?: return false
|
||||||
|
|
||||||
private val VirtualFile.ensurePackageMetadataFile
|
val versioning = libraryVersioningCache.computeIfAbsent(CacheKey(manifestFile)) {
|
||||||
get() = if (isPackageMetadataFile) this else error("Not a package metadata file: $this")
|
CacheValue(computeLibraryVersioning(manifestFile))
|
||||||
|
}.value
|
||||||
|
|
||||||
private val VirtualFile.isPackageMetadataFile
|
return versioning?.abiVersion == KotlinAbiVersion.CURRENT
|
||||||
get() = extension == KLIB_METADATA_FILE_EXTENSION
|
}
|
||||||
|
|
||||||
private val VirtualFile.ensureModuleHeaderFile
|
private fun computePackageFragment(packageFragmentFile: VirtualFile): ProtoBuf.PackageFragment? {
|
||||||
get() = if (isModuleHeaderFile) this else error("Not a module header file: $this")
|
if (!isAbiCompatible(packageFragmentFile.parent.parent.parent))
|
||||||
|
return null
|
||||||
|
|
||||||
private val VirtualFile.isModuleHeaderFile
|
return try {
|
||||||
get() = name == KLIB_MODULE_METADATA_FILE_NAME
|
parsePackageFragment(packageFragmentFile.contentsToByteArray(false))
|
||||||
|
} catch (_: IOException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeModuleHeader(moduleHeaderFile: VirtualFile): KlibMetadataProtoBuf.Header? {
|
||||||
|
if (!isAbiCompatible(moduleHeaderFile.parent.parent))
|
||||||
|
return null
|
||||||
|
|
||||||
|
return try {
|
||||||
|
parseModuleHeader(moduleHeaderFile.contentsToByteArray(false))
|
||||||
|
} catch (_: IOException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeLibraryVersioning(manifestFile: VirtualFile): KonanLibraryVersioning? = try {
|
||||||
|
Properties().apply { manifestFile.inputStream.use { load(it) } }.readKonanLibraryVersioning()
|
||||||
|
} catch (_: IOException) {
|
||||||
|
// ignore and cache null value
|
||||||
|
null
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
// ignore and cache null value
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -22,8 +22,8 @@ class KotlinNativeMetadataDecompiler : KotlinNativeMetadataDecompilerBase<Kotlin
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
override fun doReadFile(file: VirtualFile): FileWithMetadata? {
|
override fun doReadFile(file: VirtualFile): FileWithMetadata? {
|
||||||
val proto = KotlinNativeLoadingMetadataCache.getInstance().getCachedPackageFragment(file)
|
val fragment = KotlinNativeLoadingMetadataCache.getInstance().getCachedPackageFragment(file) ?: return null
|
||||||
return FileWithMetadata.Compatible(proto, KlibMetadataSerializerProtocol) //todo: check version compatibility
|
return FileWithMetadata.Compatible(fragment, KlibMetadataSerializerProtocol) //todo: check version compatibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -28,6 +28,9 @@ object KotlinNativeMetaFileIndex : KotlinFileIndexBase<KotlinNativeMetaFileIndex
|
|||||||
/*todo: check version?!*/
|
/*todo: check version?!*/
|
||||||
private val INDEXER = indexer { fileContent ->
|
private val INDEXER = indexer { fileContent ->
|
||||||
val fragment = KotlinNativeLoadingMetadataCache.getInstance().getCachedPackageFragment(fileContent.file)
|
val fragment = KotlinNativeLoadingMetadataCache.getInstance().getCachedPackageFragment(fileContent.file)
|
||||||
FqName(fragment.getExtension(KlibMetadataProtoBuf.fqName))
|
if (fragment != null)
|
||||||
|
FqName(fragment.getExtension(KlibMetadataProtoBuf.fqName))
|
||||||
|
else
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user