Deserialization/class reading: pass chosen JvmMetadataVersion whenever possible

This commit is contained in:
Mikhail Glukhikh
2022-12-02 16:38:59 +01:00
parent 52ab565cc6
commit 0c4a0360ac
67 changed files with 399 additions and 163 deletions
@@ -14,24 +14,43 @@ import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
class JvmMetadataVersion(versionArray: IntArray, val isStrictSemantics: Boolean) : BinaryVersion(*versionArray) {
constructor(vararg numbers: Int) : this(numbers, isStrictSemantics = false)
override fun isCompatibleWithCurrentCompilerVersion(): Boolean =
override fun isCompatibleWithCurrentCompilerVersion(): Boolean {
return isCompatibleInternal(INSTANCE_NEXT)
}
fun isCompatible(metadataVersionFromLanguageVersion: JvmMetadataVersion): Boolean {
// * Compiler of deployVersion X (INSTANCE) with LV Y (metadataVersionFromLanguageVersion)
// * can read metadata with version <= max(X+1, Y)
val forwardCompatibility = if (isStrictSemantics) INSTANCE else INSTANCE_NEXT
val limitVersion = maxOf(forwardCompatibility, metadataVersionFromLanguageVersion)
return isCompatibleInternal(limitVersion)
}
private fun isCompatibleInternal(limitVersion: JvmMetadataVersion): Boolean {
// NOTE: 1.0 is a pre-Kotlin-1.0 metadata version, with which the current compiler is incompatible
(major != 1 || minor != 0) &&
if (isStrictSemantics) {
isCompatibleTo(INSTANCE)
} else {
// Kotlin 1.N is able to read metadata of versions up to Kotlin 1.{N+1} (unless the version has strict semantics).
// Kotlin 1.9 is able to read Kotlin 2.0
// Kotlin K.* is able to read Kotlin M.* if K > M
major == INSTANCE.major && minor <= INSTANCE.minor + 1 ||
major == 2 && INSTANCE.major == 1 && minor == 9 ||
major < INSTANCE.major
}
if (major == 1 && minor == 0) return false
// The same for 0.*
if (major == 0) return false
// Otherwise we just compare with the given limitVersion
return this <= limitVersion
}
fun next(): JvmMetadataVersion {
if (minor < 9 || major > 1) return JvmMetadataVersion(major, minor + 1, 0)
return JvmMetadataVersion(2, 0, 0)
}
fun prev(): JvmMetadataVersion {
if (minor > 0) return JvmMetadataVersion(major, minor - 1, 0)
return JvmMetadataVersion(major - 1, 9, 0)
}
companion object {
@JvmField
val INSTANCE = JvmMetadataVersion(1, 8, 0)
private val INSTANCE_NEXT = INSTANCE.next()
@JvmField
val INVALID_VERSION = JvmMetadataVersion()
}
@@ -53,6 +53,7 @@ class ModuleMapping private constructor(
debugName: String,
skipMetadataVersionCheck: Boolean,
isJvmPackageNameSupported: Boolean,
metadataVersionFromLanguageVersion: JvmMetadataVersion,
reportIncompatibleVersionError: (JvmMetadataVersion) -> Unit,
): ModuleMapping {
if (bytes == null) {
@@ -63,7 +64,7 @@ class ModuleMapping private constructor(
val versionNumber = readVersionNumber(stream) ?: return CORRUPTED
val preVersion = JvmMetadataVersion(*versionNumber)
if (!skipMetadataVersionCheck && !preVersion.isCompatible()) {
if (!skipMetadataVersionCheck && !preVersion.isCompatible(metadataVersionFromLanguageVersion)) {
reportIncompatibleVersionError(preVersion)
return EMPTY
}
@@ -72,7 +73,7 @@ class ModuleMapping private constructor(
val flags = if (isKotlin1Dot4OrLater(preVersion)) stream.readInt() else 0
val version = JvmMetadataVersion(versionNumber, (flags and STRICT_METADATA_VERSION_SEMANTICS_FLAG) != 0)
if (!skipMetadataVersionCheck && !version.isCompatible()) {
if (!skipMetadataVersionCheck && !version.isCompatible(metadataVersionFromLanguageVersion)) {
reportIncompatibleVersionError(version)
return EMPTY
}