diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java index 8e22a03355f..20e5632f3e4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java @@ -29,9 +29,12 @@ import org.jetbrains.kotlin.backend.common.output.OutputFileCollection; import org.jetbrains.kotlin.codegen.extensions.ClassFileFactoryFinalizerExtension; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.config.JvmAnalysisFlags; +import org.jetbrains.kotlin.config.LanguageVersion; import org.jetbrains.kotlin.load.kotlin.ModuleMappingUtilKt; import org.jetbrains.kotlin.metadata.ProtoBuf; +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion; import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf; +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion; import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping; import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMappingKt; import org.jetbrains.kotlin.metadata.jvm.deserialization.PackageParts; @@ -144,7 +147,7 @@ public class ClassFileFactory implements OutputFileCollection { if (state.getLanguageVersionSettings().getFlag(JvmAnalysisFlags.getStrictMetadataVersionSemantics())) { flags |= ModuleMapping.STRICT_METADATA_VERSION_SEMANTICS_FLAG; } - return ModuleMappingKt.serializeToByteArray(moduleProto, state.getConfig().getMetadataVersion(), flags); + return ModuleMappingKt.serializeToByteArray(moduleProto, getMetadataVersionToUseForModuleMapping(), flags); } @Override @@ -154,6 +157,31 @@ public class ClassFileFactory implements OutputFileCollection { }); } + @NotNull + private BinaryVersion getMetadataVersionToUseForModuleMapping() { + BinaryVersion version = state.getConfig().getMetadataVersion(); + if (version.getMajor() == LanguageVersion.KOTLIN_2_0.getMajor() && + version.getMinor() == LanguageVersion.KOTLIN_2_0.getMinor()) { + // If language version is >= 2.0, we're using metadata version 1.9.*. This is needed because before Kotlin 1.8.20-RC, there was + // a bug in determining whether module metadata is written in the pre-1.4 format, or in the 1.4+ format with an extra integer + // for module-wide flags (see https://github.com/jetbrains/kotlin/commit/25c600c556a5). + // + // Normally it should not be possible to suffer from it because we have only one version forward compatibility on JVM. However, + // with `-Xskip-metadata-version-check`, which is used in Gradle, pre-1.8.20-RC Kotlin compilers were trying to read the 2.0 + // module metadata in the wrong format and failed with an exception: KT-62531. + // + // Since module metadata is not supposed to have any changes in 2.0, we're using the metadata version 1.9 as a workaround. This + // way, it's still written in the 1.4+ format, and old compilers will correctly understand that it's written in the 1.4+ format. + // + // Patch version does not affect anything, so we can use any number, for example 9999 to make it more recognizable that it's + // not a real Kotlin version, and rather a substitute for the 2.0 metadata version. + // + // This workaround can be removed once we no longer support language version 2.0. + return new JvmMetadataVersion(1, 9, 9999); + } + return version; + } + @NotNull @Override public List asList() { diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt index 20c7163dd01..32f99044f65 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt @@ -387,8 +387,12 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo val moduleFile = File(tmpdir.absolutePath, "META-INF/main.kotlin_module").readBytes() val versionNumber = ModuleMapping.readVersionNumber(DataInputStream(ByteArrayInputStream(moduleFile)))!! val moduleVersion = JvmMetadataVersion(*versionNumber) - assertEquals("Actual version: $moduleVersion", expectedMajor, moduleVersion.major) - assertEquals("Actual version: $moduleVersion", expectedMinor, moduleVersion.minor) + if (languageVersion == LanguageVersion.KOTLIN_2_0) { + assertEquals("Actual version: $moduleVersion", JvmMetadataVersion(1, 9, 9999), moduleVersion) + } else { + assertEquals("Actual version: $moduleVersion", expectedMajor, moduleVersion.major) + assertEquals("Actual version: $moduleVersion", expectedMinor, moduleVersion.minor) + } } }