diff --git a/build-common/src/org/jetbrains/kotlin/incremental/JavaClassesSerializerExtension.kt b/build-common/src/org/jetbrains/kotlin/incremental/JavaClassesSerializerExtension.kt index c53a36fe299..02e5dc127d9 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/JavaClassesSerializerExtension.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/JavaClassesSerializerExtension.kt @@ -19,7 +19,9 @@ package org.jetbrains.kotlin.incremental import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.metadata.java.JavaClassProtoBuf +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.scopes.MemberScope @@ -29,6 +31,9 @@ import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerial // It uses BuiltInSerializerProtocol for annotations serialization class JavaClassesSerializerExtension : KotlinSerializerExtensionBase(BuiltInSerializerProtocol) { + override val metadataVersion: BinaryVersion + get() = JvmMetadataVersion.INVALID_VERSION + override fun serializeClass( descriptor: ClassDescriptor, proto: ProtoBuf.Class.Builder, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt index 6f785f71b56..61d2c1ba336 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/serialization/JvmSerializerExtension.kt @@ -16,9 +16,11 @@ import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl import org.jetbrains.kotlin.load.kotlin.NON_EXISTENT_CLASS_NAME import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf import org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmFlags +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable import org.jetbrains.kotlin.name.FqName @@ -37,7 +39,6 @@ import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method class JvmSerializerExtension(private val bindings: JvmSerializationBindings, state: GenerationState) : SerializerExtension() { - private val codegenBinding = state.bindingContext private val typeMapper = state.typeMapper override val stringTable = JvmCodegenStringTable(typeMapper) @@ -46,9 +47,10 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta private val classBuilderMode = state.classBuilderMode private val isReleaseCoroutines = state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) - override fun shouldUseTypeTable(): Boolean { - return useTypeTable - } + override val metadataVersion: BinaryVersion + get() = JvmMetadataVersion.INSTANCE + + override fun shouldUseTypeTable(): Boolean = useTypeTable override fun serializeClass( descriptor: ClassDescriptor, diff --git a/compiler/builtins-serializer/src/org/jetbrains/kotlin/serialization/builtins/BuiltInsSerializerExtension.kt b/compiler/builtins-serializer/src/org/jetbrains/kotlin/serialization/builtins/BuiltInsSerializerExtension.kt index 67efaf9f94b..8c42b31b807 100644 --- a/compiler/builtins-serializer/src/org/jetbrains/kotlin/serialization/builtins/BuiltInsSerializerExtension.kt +++ b/compiler/builtins-serializer/src/org/jetbrains/kotlin/serialization/builtins/BuiltInsSerializerExtension.kt @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.serialization.builtins import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.serialization.KotlinSerializerExtensionBase import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol import org.jetbrains.kotlin.types.KotlinType @@ -29,6 +31,9 @@ class BuiltInsSerializerExtension : KotlinSerializerExtensionBase(BuiltInSeriali "CharRange" to "kotlin/ranges/CharRange" ) + override val metadataVersion: BinaryVersion + get() = BuiltInsBinaryVersion.INSTANCE + override fun shouldUseTypeTable(): Boolean = true override fun serializeErrorType(type: KotlinType, builder: ProtoBuf.Type.Builder) { diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializerExtension.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializerExtension.kt index 7f991595029..66a13aff01d 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializerExtension.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializerExtension.kt @@ -16,9 +16,14 @@ package org.jetbrains.kotlin.cli.metadata +import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.serialization.KotlinSerializerExtensionBase import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol class MetadataSerializerExtension : KotlinSerializerExtensionBase(BuiltInSerializerProtocol) { + override val metadataVersion: BinaryVersion + get() = BuiltInsBinaryVersion.INSTANCE + override fun shouldUseTypeTable(): Boolean = true } diff --git a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt index 4161a21c0d7..d78be4bf7b4 100644 --- a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt +++ b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.deserialization.Flags import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement +import org.jetbrains.kotlin.metadata.deserialization.isVersionRequirementTableWrittenCorrectly import org.jetbrains.kotlin.metadata.serialization.Interner import org.jetbrains.kotlin.metadata.serialization.MutableTypeTable import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable @@ -728,7 +729,8 @@ class DescriptorSerializer private constructor( Interner(parent.typeParameters), extension, MutableTypeTable(), - if (container is ClassDescriptor) parent.versionRequirementTable else MutableVersionRequirementTable(), + if (container is ClassDescriptor && !isVersionRequirementTableWrittenCorrectly(extension.metadataVersion)) + parent.versionRequirementTable else MutableVersionRequirementTable(), serializeTypeTableToFunction = false ) for (typeParameter in descriptor.declaredTypeParameters) { diff --git a/compiler/serialization/src/org/jetbrains/kotlin/serialization/SerializerExtension.kt b/compiler/serialization/src/org/jetbrains/kotlin/serialization/SerializerExtension.kt index 5461bdf4cbd..73b6ee821a8 100644 --- a/compiler/serialization/src/org/jetbrains/kotlin/serialization/SerializerExtension.kt +++ b/compiler/serialization/src/org/jetbrains/kotlin/serialization/SerializerExtension.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.serialization import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.FlexibleType @@ -15,6 +16,8 @@ import org.jetbrains.kotlin.types.KotlinType abstract class SerializerExtension { abstract val stringTable: DescriptorAwareStringTable + abstract val metadataVersion: BinaryVersion + val annotationSerializer by lazy { AnnotationSerializer(stringTable) } open fun shouldUseTypeTable(): Boolean = false diff --git a/compiler/serialization/src/org/jetbrains/kotlin/serialization/SerializerExtensionBase.kt b/compiler/serialization/src/org/jetbrains/kotlin/serialization/SerializerExtensionBase.kt index 24e9e7cc3be..86303b51d51 100644 --- a/compiler/serialization/src/org/jetbrains/kotlin/serialization/SerializerExtensionBase.kt +++ b/compiler/serialization/src/org/jetbrains/kotlin/serialization/SerializerExtensionBase.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.resolve.constants.NullValue import org.jetbrains.kotlin.resolve.descriptorUtil.nonSourceAnnotations import org.jetbrains.kotlin.types.KotlinType -open class KotlinSerializerExtensionBase(private val protocol: SerializerExtensionProtocol) : SerializerExtension() { +abstract class KotlinSerializerExtensionBase(private val protocol: SerializerExtensionProtocol) : SerializerExtension() { override val stringTable = StringTableImpl() override fun serializeClass( diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/context.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/context.kt index 85ad3de990f..b75722b2983 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/context.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/context.kt @@ -24,10 +24,7 @@ import org.jetbrains.kotlin.descriptors.deserialization.ClassDescriptorFactory import org.jetbrains.kotlin.descriptors.deserialization.PlatformDependentDeclarationFilter import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.metadata.ProtoBuf -import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion -import org.jetbrains.kotlin.metadata.deserialization.NameResolver -import org.jetbrains.kotlin.metadata.deserialization.TypeTable -import org.jetbrains.kotlin.metadata.deserialization.VersionRequirementTable +import org.jetbrains.kotlin.metadata.deserialization.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite import org.jetbrains.kotlin.resolve.constants.ConstantValue @@ -96,9 +93,12 @@ class DeserializationContext( typeParameterProtos: List, nameResolver: NameResolver = this.nameResolver, typeTable: TypeTable = this.typeTable, + versionRequirementTable: VersionRequirementTable = this.versionRequirementTable, metadataVersion: BinaryVersion = this.metadataVersion ): DeserializationContext = DeserializationContext( - components, nameResolver, descriptor, typeTable, versionRequirementTable, metadataVersion, this.containerSource, + components, nameResolver, descriptor, typeTable, + if (isVersionRequirementTableWrittenCorrectly(metadataVersion)) versionRequirementTable else this.versionRequirementTable, + metadataVersion, this.containerSource, parentTypeDeserializer = this.typeDeserializer, typeParameters = typeParameterProtos ) } diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt index 6de9b1105ef..b07bc8ea559 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt @@ -45,7 +45,10 @@ class DeserializedClassDescriptor( private val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(classProto.flags)) private val kind = ProtoEnumFlags.classKind(Flags.CLASS_KIND.get(classProto.flags)) - val c = outerContext.childContext(this, classProto.typeParameterList, nameResolver, TypeTable(classProto.typeTable), metadataVersion) + val c = outerContext.childContext( + this, classProto.typeParameterList, nameResolver, TypeTable(classProto.typeTable), + VersionRequirementTable.create(classProto.versionRequirementTable), metadataVersion + ) private val staticScope = if (kind == ClassKind.ENUM_CLASS) StaticScopeForKotlinEnum(c.storageManager, this) else MemberScope.Empty private val typeConstructor = DeserializedClassTypeConstructor() diff --git a/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/versionSpecificBehavior.kt b/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/versionSpecificBehavior.kt new file mode 100644 index 00000000000..fd0b9862185 --- /dev/null +++ b/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/versionSpecificBehavior.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.metadata.deserialization + +// The purpose of utilities in this file is to support different behavior in deserialization according to the given binary file's version. +// +// For example, if we find a bug in serialization/deserialization and would like to fix it _remaining compatible_ with two latest versions +// of Kotlin, we can use methods of this class to fix deserialization of the "future" binaries, and later (in the next major version) +// fix the bug in serialization when the binary version advances to the value supported in the first bug fix. + +/** + * Before Kotlin 1.4, version requirements for nested classes were deserialized incorrectly: the version requirement table was loaded from + * the outermost class and passed to the nested classes and their members, even though indices of their version requirements were pointing + * to the other table stored in the nested class (which was not read by deserialization). See KT-25120 for more information + */ +fun isVersionRequirementTableWrittenCorrectly(version: BinaryVersion): Boolean = + isKotlin1Dot4OrLater(version) + +private fun isKotlin1Dot4OrLater(version: BinaryVersion): Boolean { + // All metadata versions (JVM, JS, common) will be advanced to 1.4.0 in Kotlin 1.4 + return version.major == 1 && version.minor >= 4 +} diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializerExtension.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializerExtension.kt index 9c866d73a44..57c9803f1cf 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializerExtension.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializerExtension.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.metadata.js.JsProtoBuf import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable import org.jetbrains.kotlin.psi.KtFile @@ -27,11 +28,15 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.source.PsiSourceFile import org.jetbrains.kotlin.serialization.KotlinSerializerExtensionBase import org.jetbrains.kotlin.types.FlexibleType +import org.jetbrains.kotlin.utils.JsMetadataVersion class KotlinJavascriptSerializerExtension( private val fileRegistry: KotlinFileRegistry, private val languageVersionSettings: LanguageVersionSettings ) : KotlinSerializerExtensionBase(JsSerializerProtocol) { + override val metadataVersion: BinaryVersion + get() = JsMetadataVersion.INSTANCE + override val stringTable = JavaScriptStringTable() override fun serializeFlexibleType(flexibleType: FlexibleType, lowerProto: ProtoBuf.Type.Builder, upperProto: ProtoBuf.Type.Builder) {