From 9fd144563156ce08ab5d7f0c9665770458041856 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 12 Oct 2023 10:42:20 +0200 Subject: [PATCH] JVM: use metadata version 1.9 for .kotlin_module if LV=2.0 See the comment in the code for more info. No proper test added because the compiler test infrastructure lacks the ability to run old compiler versions, however I've verified manually that the change fixes the issue. #KT-62531 Fixed --- .../kotlin/codegen/ClassFileFactory.java | 30 ++++++++++++++++++- ...tCompileKotlinAgainstCustomBinariesTest.kt | 8 +++-- 2 files changed, 35 insertions(+), 3 deletions(-) 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) + } } }