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