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
This commit is contained in:
Alexander Udalov
2023-10-12 10:42:20 +02:00
committed by Space Team
parent d5540ff035
commit 9fd1445631
2 changed files with 35 additions and 3 deletions
@@ -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<OutputFile> asList() {
@@ -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)
}
}
}