From 1d5d6b5b723976140b6a9f355492cd223308ca0b Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 6 Sep 2018 21:49:24 +0300 Subject: [PATCH] Support strict metadata version semantics Preface: Kotlin 1.3 will be able to read metadata of .class files produced by Kotlin 1.4 (see KT-25972). Also, to simplify implementation and to improve diagnostic messages, we're going to advance JVM metadata version to 1.4.0 in Kotlin 1.4, and would like to keep it in sync with the compiler version thereafter. This presents a problem: in an unlikely event that before releasing 1.4, we find out that the metadata-reading implementation in 1.3 was incorrect, we'd like to be able to fix the bug in that implementation and _forbid_ 1.3 from reading metadata of 1.4. But prior to this commit the only way to do this was to advance the metadata version, in this case to 1.5, and that breaks the metadata/compiler version equivalence we'd like to keep. The solution is to add another boolean flag to the class file, called "strict metadata version semantics", which signifies that if this class file has metadata version 1.X, then it can only be read by the compilers of versions 1.X and greater. This flag effectively disables the smooth migration scenario proposed in KT-25972 (as does increasing metadata version by 2), and will be used only in hopeless situations as in the case described above. --- .../kotlin/codegen/ClassFileFactory.java | 6 +++- .../kotlin/codegen/writeAnnotationUtil.kt | 4 +++ .../arguments/K2JVMCompilerArguments.kt | 7 ++++ compiler/testData/cli/jvm/extraHelp.out | 2 ++ .../library/a.kt | 7 ++++ .../output.txt | 25 +++++++++++++++ .../source.kt | 7 ++++ .../library/a.kt | 7 ++++ .../output.txt | 1 + .../source.kt | 7 ++++ .../CompileKotlinAgainstCustomBinariesTest.kt | 12 +++++++ .../jetbrains/kotlin/config/AnalysisFlag.kt | 3 ++ .../kotlin/load/java/JvmAnnotationNames.java | 1 + ...eadKotlinClassHeaderAnnotationVisitor.java | 17 +++++----- .../jvm/deserialization/JvmMetadataVersion.kt | 19 +++++++---- .../jvm/deserialization/ModuleMapping.kt | 16 +++++++--- .../src/kotlin/reflect/jvm/reflectLambda.kt | 4 ++- .../versions/KotlinJsMetadataVersionIndex.kt | 2 +- .../versions/KotlinJvmMetadataVersionIndex.kt | 32 +++++++++++++------ .../KotlinMetadataVersionIndexBase.kt | 12 +++++-- .../metadata/jvm/KotlinClassMetadata.kt | 6 +++- .../stdlib/jvm/runtime/kotlin/Metadata.kt | 8 +++-- 22 files changed, 167 insertions(+), 38 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/library/a.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/library/a.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/source.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java index 21f90a8c50a..49065851085 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java @@ -123,7 +123,11 @@ public class ClassFileFactory implements OutputFileCollection { generators.put(outputFilePath, new OutAndSourceFileList(CollectionsKt.toList(sourceFiles)) { @Override public byte[] asBytes(ClassBuilderFactory factory) { - return ModuleMappingKt.serializeToByteArray(moduleProto, state.getMetadataVersion(), 0); + int flags = 0; + if (state.getLanguageVersionSettings().getFlag(AnalysisFlag.getStrictMetadataVersionSemantics())) { + flags |= ModuleMapping.STRICT_METADATA_VERSION_SEMANTICS_FLAG; + } + return ModuleMappingKt.serializeToByteArray(moduleProto, state.getMetadataVersion(), flags); } @Override diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/writeAnnotationUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/writeAnnotationUtil.kt index ba60c36c86e..2e50e2a34a2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/writeAnnotationUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/writeAnnotationUtil.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmBytecodeBinaryVersion @@ -37,6 +38,9 @@ fun writeKotlinMetadata( if (state.languageVersionSettings.isPreRelease()) { flags = flags or JvmAnnotationNames.METADATA_PRE_RELEASE_FLAG } + if (state.languageVersionSettings.getFlag(AnalysisFlag.strictMetadataVersionSemantics)) { + flags = flags or JvmAnnotationNames.METADATA_STRICT_VERSION_SEMANTICS_FLAG + } if (flags != 0) { av.visit(JvmAnnotationNames.METADATA_EXTRA_INT_FIELD_NAME, flags) } diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index 4ecb49dbba5..62a5bf73cd4 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -244,6 +244,12 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xdisable-standard-script", description = "Disable standard kotlin script support") var disableStandardScript: Boolean by FreezableVar(false) + @Argument( + value = "-Xgenerate-strict-metadata-version", + description = "Generate metadata with strict version semantics (see kdoc on Metadata.extraInt)" + ) + var strictMetadataVersionSemantics: Boolean by FreezableVar(false) + @Argument( value = "-Xfriend-paths", valueDescription = "", @@ -253,6 +259,7 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { override fun configureAnalysisFlags(collector: MessageCollector): MutableMap, Any> { val result = super.configureAnalysisFlags(collector) + result.put(AnalysisFlag.strictMetadataVersionSemantics, strictMetadataVersionSemantics) result[AnalysisFlag.jsr305] = Jsr305Parser(collector).parse( jsr305, supportCompatqualCheckerFrameworkAnnotations diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index 9b15823842e..971ccf31d31 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -52,6 +52,8 @@ where advanced options include: -Xskip-runtime-version-check Allow Kotlin runtime libraries of incompatible versions in the classpath -Xstrict-java-nullability-assertions Generate nullability assertions for non-null Java expressions + -Xgenerate-strict-metadata-version + Generate metadata with strict version semantics (see kdoc on Metadata.extraInt) -Xsupport-compatqual-checker-framework-annotations=enable|disable Specify behavior for Checker Framework compatqual annotations (NullableDecl/NonNullDecl). Default value is 'enable' diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/library/a.kt new file mode 100644 index 00000000000..86a1f3e311a --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/library/a.kt @@ -0,0 +1,7 @@ +package a + +class C + +fun f() {} + +val v = Unit diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/output.txt new file mode 100644 index 00000000000..a8c7b87f739 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/output.txt @@ -0,0 +1,25 @@ +error: incompatible classes were found in dependencies. Remove them from the classpath or use '-Xskip-metadata-version-check' to suppress errors +$TMP_DIR$/library.jar!/META-INF/main.kotlin_module: error: module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.4.0, expected version is $ABI_VERSION$. +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:3:13: error: class 'a.C' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.4.0, expected version is $ABI_VERSION$. +The class is loaded from $TMP_DIR$/library.jar!/a/C.class +fun test(c: C) { + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:4:5: error: unresolved reference: f + f() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:5:5: error: unresolved reference: v + v + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:6:5: error: class 'a.C' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.4.0, expected version is $ABI_VERSION$. +The class is loaded from $TMP_DIR$/library.jar!/a/C.class + c.let { C() } + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:6:7: error: class 'a.C' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.4.0, expected version is $ABI_VERSION$. +The class is loaded from $TMP_DIR$/library.jar!/a/C.class + c.let { C() } + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:6:13: error: class 'a.C' was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.4.0, expected version is $ABI_VERSION$. +The class is loaded from $TMP_DIR$/library.jar!/a/C.class + c.let { C() } + ^ +COMPILATION_ERROR diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt new file mode 100644 index 00000000000..80cd851db3c --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt @@ -0,0 +1,7 @@ +import a.* + +fun test(c: C) { + f() + v + c.let { C() } +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/library/a.kt new file mode 100644 index 00000000000..86a1f3e311a --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/library/a.kt @@ -0,0 +1,7 @@ +package a + +class C + +fun f() {} + +val v = Unit diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/source.kt new file mode 100644 index 00000000000..80cd851db3c --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsSameVersion/source.kt @@ -0,0 +1,7 @@ +import a.* + +fun test(c: C) { + f() + v + c.let { C() } +} diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt index e66090e40a9..72bbf88bd76 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt @@ -332,6 +332,18 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration ) } + fun testStrictMetadataVersionSemanticsSameVersion() { + val library = compileLibrary("library", additionalOptions = listOf("-Xgenerate-strict-metadata-version")) + compileKotlin("source.kt", tmpdir, listOf(library)) + } + + fun testStrictMetadataVersionSemanticsOldVersion() { + val library = compileLibrary( + "library", additionalOptions = listOf("-Xgenerate-strict-metadata-version", "-Xmetadata-version=1.4.0") + ) + compileKotlin("source.kt", tmpdir, listOf(library)) + } + /*test source mapping generation when source info is absent*/ fun testInlineFunWithoutDebugInfo() { compileKotlin("sourceInline.kt", tmpdir) diff --git a/compiler/util/src/org/jetbrains/kotlin/config/AnalysisFlag.kt b/compiler/util/src/org/jetbrains/kotlin/config/AnalysisFlag.kt index 06950fb0ed7..9f460799ce3 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/AnalysisFlag.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/AnalysisFlag.kt @@ -45,6 +45,9 @@ class AnalysisFlag internal constructor( @JvmStatic val skipMetadataVersionCheck by Flag.Boolean + @JvmStatic + val strictMetadataVersionSemantics by Flag.Boolean + @JvmStatic val multiPlatformDoNotCheckActual by Flag.Boolean diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java index 8edf985840f..5b366c9df35 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java @@ -38,6 +38,7 @@ public final class JvmAnnotationNames { public static final int METADATA_MULTIFILE_PARTS_INHERIT_FLAG = 1 << 0; public static final int METADATA_PRE_RELEASE_FLAG = 1 << 1; public static final int METADATA_SCRIPT_FLAG = 1 << 2; + public static final int METADATA_STRICT_VERSION_SEMANTICS_FLAG = 1 << 3; public static final Name DEFAULT_ANNOTATION_MEMBER_NAME = Name.identifier("value"); diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/header/ReadKotlinClassHeaderAnnotationVisitor.java b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/header/ReadKotlinClassHeaderAnnotationVisitor.java index c9f1a2e2755..eebc7a97adc 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/header/ReadKotlinClassHeaderAnnotationVisitor.java +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/header/ReadKotlinClassHeaderAnnotationVisitor.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.kotlin.header; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.SourceElement; +import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmBytecodeBinaryVersion; import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion; import org.jetbrains.kotlin.name.ClassId; @@ -48,7 +49,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor HEADER_KINDS.put(ClassId.topLevel(new FqName("kotlin.jvm.internal.KotlinSyntheticClass")), SYNTHETIC_CLASS); } - private JvmMetadataVersion metadataVersion = null; + private int[] metadataVersionArray = null; private JvmBytecodeBinaryVersion bytecodeVersion = null; private String extraString = null; private int extraInt = 0; @@ -60,15 +61,15 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor @Nullable public KotlinClassHeader createHeader() { - if (headerKind == null) { + if (headerKind == null || metadataVersionArray == null) { return null; } + JvmMetadataVersion metadataVersion = + new JvmMetadataVersion(metadataVersionArray, (extraInt & JvmAnnotationNames.METADATA_STRICT_VERSION_SEMANTICS_FLAG) != 0); + if (!metadataVersion.isCompatible()) { incompatibleData = data; - } - - if (metadataVersion == null || !metadataVersion.isCompatible()) { data = null; } else if (shouldHaveData() && data == null) { @@ -79,7 +80,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor return new KotlinClassHeader( headerKind, - metadataVersion != null ? metadataVersion : JvmMetadataVersion.INVALID_VERSION, + metadataVersion, bytecodeVersion != null ? bytecodeVersion : JvmBytecodeBinaryVersion.INVALID_VERSION, data, incompatibleData, @@ -137,7 +138,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor } else if (METADATA_VERSION_FIELD_NAME.equals(string)) { if (value instanceof int[]) { - metadataVersion = new JvmMetadataVersion((int[]) value); + metadataVersionArray = (int[]) value; } } else if (BYTECODE_VERSION_FIELD_NAME.equals(string)) { @@ -224,7 +225,7 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor String string = name.asString(); if ("version".equals(string)) { if (value instanceof int[]) { - metadataVersion = new JvmMetadataVersion((int[]) value); + metadataVersionArray = (int[]) value; // If there's no bytecode binary version in the class file, we assume it to be equal to the metadata version if (bytecodeVersion == null) { diff --git a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/JvmMetadataVersion.kt b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/JvmMetadataVersion.kt index f80ff9f7a4e..d66b1203504 100644 --- a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/JvmMetadataVersion.kt +++ b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/JvmMetadataVersion.kt @@ -11,12 +11,19 @@ import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion * The version of the metadata serialized by the compiler and deserialized by the compiler and reflection. * This version includes the version of the core protobuf messages (metadata.proto) as well as JVM extensions (jvm_metadata.proto). */ -class JvmMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) { - // In Kotlin 1.4, JVM metadata version is going to be advanced to 1.4.0. - // Kotlin 1.3 is able to read metadata of versions up to Kotlin 1.4. - // NOTE: 1.0 is a pre-Kotlin-1.0 metadata version, with which the current compiler is incompatible - override fun isCompatible() = - major == 1 && minor in 1..4 +class JvmMetadataVersion(versionArray: IntArray, val isStrictSemantics: Boolean) : BinaryVersion(*versionArray) { + constructor(vararg numbers: Int) : this(numbers, isStrictSemantics = false) + + override fun isCompatible(): 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 { + // In Kotlin 1.4, JVM metadata version is going to be advanced to 1.4.0. + // Kotlin 1.3 is able to read metadata of versions up to Kotlin 1.4 (unless the version has strict semantics). + major == 1 && minor <= 4 + } companion object { @JvmField diff --git a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/ModuleMapping.kt b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/ModuleMapping.kt index 10cead42bd5..eacaa7b8222 100644 --- a/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/ModuleMapping.kt +++ b/core/metadata.jvm/src/org/jetbrains/kotlin/metadata/jvm/deserialization/ModuleMapping.kt @@ -31,6 +31,8 @@ class ModuleMapping private constructor( @JvmField val CORRUPTED: ModuleMapping = ModuleMapping(emptyMap(), BinaryModuleData(emptyList()), "CORRUPTED") + const val STRICT_METADATA_VERSION_SEMANTICS_FLAG = 1 shl 0 + fun loadModuleMapping( bytes: ByteArray?, debugName: String, @@ -50,15 +52,19 @@ class ModuleMapping private constructor( return CORRUPTED } - val version = JvmMetadataVersion(*versionNumber) - if (!skipMetadataVersionCheck && !version.isCompatible()) { - reportIncompatibleVersionError(version) + val preVersion = JvmMetadataVersion(*versionNumber) + if (!skipMetadataVersionCheck && !preVersion.isCompatible()) { + reportIncompatibleVersionError(preVersion) return EMPTY } // Since Kotlin 1.4, we write integer flags between the version and the proto - if (isKotlin1Dot4OrLater(version)) { - stream.readInt() + 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()) { + reportIncompatibleVersionError(version) + return EMPTY } val moduleProto = JvmModuleProtoBuf.Module.parseFrom(stream) ?: return EMPTY diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/reflectLambda.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/reflectLambda.kt index e2ffaa742a1..06519167ed5 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/reflectLambda.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/reflectLambda.kt @@ -18,6 +18,7 @@ package kotlin.reflect.jvm +import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.metadata.deserialization.TypeTable import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil @@ -36,7 +37,8 @@ fun Function.reflect(): KFunction? { val annotation = javaClass.getAnnotation(Metadata::class.java) ?: return null val data = annotation.d1.takeUnless(Array::isEmpty) ?: return null val (nameResolver, proto) = JvmProtoBufUtil.readFunctionDataFrom(data, annotation.d2) - val metadataVersion = JvmMetadataVersion(*annotation.mv) + val metadataVersion = + JvmMetadataVersion(annotation.mv, (annotation.xi and JvmAnnotationNames.METADATA_STRICT_VERSION_SEMANTICS_FLAG) != 0) val descriptor = deserializeToDescriptor( javaClass, proto, nameResolver, TypeTable(proto.typeTable), metadataVersion, MemberDeserializer::loadFunction diff --git a/idea/src/org/jetbrains/kotlin/idea/versions/KotlinJsMetadataVersionIndex.kt b/idea/src/org/jetbrains/kotlin/idea/versions/KotlinJsMetadataVersionIndex.kt index 278e3b50e7a..717e43f6fa6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/versions/KotlinJsMetadataVersionIndex.kt +++ b/idea/src/org/jetbrains/kotlin/idea/versions/KotlinJsMetadataVersionIndex.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils import java.util.* object KotlinJsMetadataVersionIndex : KotlinMetadataVersionIndexBase( - KotlinJsMetadataVersionIndex::class.java, ::JsMetadataVersion + KotlinJsMetadataVersionIndex::class.java, { version, _ -> JsMetadataVersion(*version) } ) { override fun getIndexer() = INDEXER diff --git a/idea/src/org/jetbrains/kotlin/idea/versions/KotlinJvmMetadataVersionIndex.kt b/idea/src/org/jetbrains/kotlin/idea/versions/KotlinJvmMetadataVersionIndex.kt index 36fb3d6f040..cd71556f328 100644 --- a/idea/src/org/jetbrains/kotlin/idea/versions/KotlinJvmMetadataVersionIndex.kt +++ b/idea/src/org/jetbrains/kotlin/idea/versions/KotlinJvmMetadataVersionIndex.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.fileTypes.StdFileTypes import com.intellij.util.indexing.DataIndexer import com.intellij.util.indexing.FileBasedIndex import com.intellij.util.indexing.FileContent +import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.JvmAnnotationNames.* import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion @@ -29,7 +30,8 @@ import org.jetbrains.org.objectweb.asm.ClassVisitor import org.jetbrains.org.objectweb.asm.Opcodes object KotlinJvmMetadataVersionIndex : KotlinMetadataVersionIndexBase( - KotlinJvmMetadataVersionIndex::class.java, ::JvmMetadataVersion + KotlinJvmMetadataVersionIndex::class.java, + { version, isStrictSemantics -> JvmMetadataVersion(version, isStrictSemantics = isStrictSemantics!!) } ) { override fun getIndexer() = INDEXER @@ -37,16 +39,21 @@ object KotlinJvmMetadataVersionIndex : KotlinMetadataVersionIndexBase { inputData: FileContent -> - var version: JvmMetadataVersion? = null + var versionArray: IntArray? = null + var isStrictSemantics = false var annotationPresent = false var kind: KotlinClassHeader.Kind? = null @@ -61,11 +68,14 @@ object KotlinJvmMetadataVersionIndex : KotlinMetadataVersionIndexBase if (value is IntArray) { - version = createBinaryVersion(value) + versionArray = value } KIND_FIELD_NAME -> if (value is Int) { kind = KotlinClassHeader.Kind.getById(value) } + METADATA_EXTRA_INT_FIELD_NAME -> if (value is Int) { + isStrictSemantics = (value and JvmAnnotationNames.METADATA_STRICT_VERSION_SEMANTICS_FLAG) != 0 + } } } } @@ -73,15 +83,17 @@ object KotlinJvmMetadataVersionIndex : KotlinMetadataVersionIndexBase( private val classOfIndex: Class, - protected val createBinaryVersion: (IntArray) -> V + protected val createBinaryVersion: (IntArray, Boolean?) -> V ) : ScalarIndexExtension() { override fun getName(): ID = ID.create(classOfIndex.canonicalName) @@ -43,7 +43,9 @@ abstract class KotlinMetadataVersionIndexBase( override fun read(input: DataInput): V { val size = DataInputOutputUtil.readINT(input) - return createBinaryVersion((0..size - 1).map { DataInputOutputUtil.readINT(input) }.toIntArray()) + val versionArray = (0..size - 1).map { DataInputOutputUtil.readINT(input) }.toIntArray() + val extraBoolean = if (isExtraBooleanNeeded()) DataInputOutputUtil.readINT(input) == 1 else null + return createBinaryVersion(versionArray, extraBoolean) } override fun save(output: DataOutput, value: V) { @@ -52,11 +54,17 @@ abstract class KotlinMetadataVersionIndexBase( for (number in array) { DataInputOutputUtil.writeINT(output, number) } + if (isExtraBooleanNeeded()) { + DataInputOutputUtil.writeINT(output, if (getExtraBoolean(value)) 1 else 0) + } } } override fun dependsOnFileContent() = true + protected open fun isExtraBooleanNeeded(): Boolean = false + protected open fun getExtraBoolean(version: V): Boolean = throw UnsupportedOperationException() + protected val LOG: Logger = Logger.getInstance(classOfIndex) protected inline fun tryBlock(inputData: FileContent, body: () -> Unit) { diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt index 7829cd03aed..3b7a3e12d2d 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt @@ -313,7 +313,11 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { @JvmStatic fun read(header: KotlinClassHeader): KotlinClassMetadata? { // We only support metadata of version 1.1.* (this is Kotlin from 1.0 until today) - if (!JvmMetadataVersion(*header.metadataVersion).isCompatible()) return null + if (!JvmMetadataVersion( + header.metadataVersion, + (header.extraInt and (1 shl 3)/* see JvmAnnotationNames.METADATA_STRICT_VERSION_SEMANTICS_FLAG */) != 0 + ).isCompatible() + ) return null return try { when (header.kind) { diff --git a/libraries/stdlib/jvm/runtime/kotlin/Metadata.kt b/libraries/stdlib/jvm/runtime/kotlin/Metadata.kt index 2543e12e8d2..50562e184d8 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/Metadata.kt +++ b/libraries/stdlib/jvm/runtime/kotlin/Metadata.kt @@ -56,9 +56,11 @@ public annotation class Metadata( /** * An extra int. Bits of this number represent the following flags: * - * 0 - this is a multi-file class facade or part, compiled with `-Xmultifile-parts-inherit`. - * 1 - this class file is compiled by a pre-release version of Kotlin and is not visible to release versions. - * 2 - this class file is a compiled Kotlin script source file (.kts). + * * 0 - this is a multi-file class facade or part, compiled with `-Xmultifile-parts-inherit`. + * * 1 - this class file is compiled by a pre-release version of Kotlin and is not visible to release versions. + * * 2 - this class file is a compiled Kotlin script source file (.kts). + * * 3 - the metadata of this class file is not supposed to be read by the compiler, whose major.minor version is less than + * the major.minor version of this metadata ([mv]). */ @SinceKotlin("1.1") val xi: Int = 0