Fix: Use more precise KLIB compatibility check (by metadata version)

Issue #KT-34811
This commit is contained in:
Dmitriy Dolovov
2019-12-12 14:49:38 +03:00
parent 34e1ab4e7a
commit deb4025987
5 changed files with 55 additions and 39 deletions
@@ -16,6 +16,8 @@ import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
import org.jetbrains.kotlin.analyzer.getCapability
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.backend.common.serialization.metadata.metadataVersion
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.functions.functionInterfacePackageFragmentProvider
@@ -27,10 +29,10 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.Companion.safeAbiVersion
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.Companion.isCompatible
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.Companion.safeMetadataVersion
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.MetadataInfo.Compatible
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.MetadataInfo.Incompatible
import org.jetbrains.kotlin.ide.konan.analyzer.NativeResolverForModuleFactory
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.project.lazyClosure
@@ -57,7 +59,7 @@ fun KotlinLibrary.createPackageFragmentProvider(
moduleDescriptor: ModuleDescriptor
): PackageFragmentProvider? {
if (!safeAbiVersion.isCompatible) return null
if (safeMetadataVersion?.isCompatible() != true) return null
val libraryProto = CachingIdeKonanLibraryMetadataLoader.loadModuleHeader(this)
@@ -187,14 +189,33 @@ private fun createKotlinNativeBuiltIns(moduleInfo: ModuleInfo, projectContext: P
private fun ModuleInfo.findNativeStdlib(): NativeLibraryInfo? =
dependencies().lazyClosure { it.dependencies() }
.filterIsInstance<NativeLibraryInfo>()
.firstOrNull { it.isStdlib && it.safeAbiVersion.isCompatible }
.firstOrNull { it.isStdlib && it.metadataInfo.isCompatible }
class NativeLibraryInfo(project: Project, library: Library, val libraryRoot: String) : LibraryInfo(project, library) {
sealed class MetadataInfo {
abstract val isCompatible: Boolean
object Compatible : MetadataInfo() {
override val isCompatible get() = true
}
class Incompatible(val isOlder: Boolean) : MetadataInfo() {
override val isCompatible get() = false
}
}
private val nativeLibrary = createKotlinLibrary(File(libraryRoot))
val isStdlib get() = libraryRoot.endsWith(KONAN_STDLIB_NAME)
val safeAbiVersion get() = nativeLibrary.safeAbiVersion
val metadataInfo by lazy {
val metadataVersion = nativeLibrary.safeMetadataVersion
when {
metadataVersion == null -> Incompatible(true) // too old KLIB format, even doesn't have metadata version
!metadataVersion.isCompatible() -> Incompatible(!metadataVersion.isAtLeast(KlibMetadataVersion.INSTANCE))
else -> Compatible
}
}
override fun getLibraryRoots() = listOf(libraryRoot)
@@ -215,8 +236,7 @@ class NativeLibraryInfo(project: Project, library: Library, val libraryRoot: Str
companion object {
val NATIVE_LIBRARY_CAPABILITY = ModuleDescriptor.Capability<KotlinLibrary>("KotlinNativeLibrary")
internal val KotlinLibrary.safeAbiVersion get() = this.readSafe(null) { versions.abiVersion }
internal val KotlinAbiVersion?.isCompatible get() = this == KotlinAbiVersion.CURRENT
internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion }
private fun <T> KotlinLibrary.readSafe(defaultValue: T, action: KotlinLibrary.() -> T) = try {
action()
@@ -9,11 +9,13 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.BaseComponent
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.containers.ContainerUtil.createConcurrentWeakValueMap
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
import org.jetbrains.kotlin.library.metadata.parseModuleHeader
import org.jetbrains.kotlin.library.metadata.parsePackageFragment
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import java.io.IOException
import java.util.*
@@ -39,7 +41,7 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
private val packageFragmentCache = createConcurrentWeakValueMap<CacheKey, CacheValue<ProtoBuf.PackageFragment>>()
private val moduleHeaderCache = createConcurrentWeakValueMap<CacheKey, CacheValue<KlibMetadataProtoBuf.Header>>()
private val libraryVersioningCache = createConcurrentWeakValueMap<CacheKey, CacheValue<KotlinLibraryVersioning>>()
private val libraryMetadataVersionCache = createConcurrentWeakValueMap<CacheKey, CacheValue<KlibMetadataVersion>>()
fun getCachedPackageFragment(packageFragmentFile: VirtualFile): ProtoBuf.PackageFragment? {
check(packageFragmentFile.extension == KLIB_METADATA_FILE_EXTENSION) {
@@ -61,18 +63,18 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
}.value
}
private fun isAbiCompatible(libraryRoot: VirtualFile): Boolean {
private fun isMetadataCompatible(libraryRoot: VirtualFile): Boolean {
val manifestFile = libraryRoot.findChild(KLIB_MANIFEST_FILE_NAME) ?: return false
val versioning = libraryVersioningCache.computeIfAbsent(CacheKey(manifestFile)) {
CacheValue(computeLibraryVersioning(manifestFile))
}.value
val metadataVersion = libraryMetadataVersionCache.computeIfAbsent(CacheKey(manifestFile)) {
CacheValue(computeLibraryMetadataVersion(manifestFile))
}.value ?: return false
return versioning?.abiVersion == KotlinAbiVersion.CURRENT
return metadataVersion.isCompatible()
}
private fun computePackageFragment(packageFragmentFile: VirtualFile): ProtoBuf.PackageFragment? {
if (!isAbiCompatible(packageFragmentFile.parent.parent.parent))
if (!isMetadataCompatible(packageFragmentFile.parent.parent.parent))
return null
return try {
@@ -83,7 +85,7 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
}
private fun computeModuleHeader(moduleHeaderFile: VirtualFile): KlibMetadataProtoBuf.Header? {
if (!isAbiCompatible(moduleHeaderFile.parent.parent))
if (!isMetadataCompatible(moduleHeaderFile.parent.parent))
return null
return try {
@@ -93,8 +95,9 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
}
}
private fun computeLibraryVersioning(manifestFile: VirtualFile): KotlinLibraryVersioning? = try {
Properties().apply { manifestFile.inputStream.use { load(it) } }.readKonanLibraryVersioning()
private fun computeLibraryMetadataVersion(manifestFile: VirtualFile): KlibMetadataVersion? = try {
val versioning = Properties().apply { manifestFile.inputStream.use { load(it) } }.readKonanLibraryVersioning()
versioning.metadataVersion?.let(BinaryVersion.Companion::parseVersionArray)?.let(::KlibMetadataVersion)
} catch (_: IOException) {
// ignore and cache null value
null