From 3d9093583fa1f8baaf8767a31bcbb5ea1ddfa211 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Mon, 20 Jul 2020 18:21:34 +0700 Subject: [PATCH] Metadata: 'non-stable parameter names' flag for callables ^KT-34602 --- .../fir/serialization/FirElementSerializer.kt | 6 +- .../calls/FirArgumentsToParametersMapper.kt | 4 +- .../serialization/encodings/BinaryFlags.kt | 4 +- .../serialization/DescriptorSerializer.kt | 13 +- .../nonStableParameterNames/test.kt | 15 +++ .../org/jetbrains/kotlin/test/KlibTestUtil.kt | 120 ++++++++++++++++++ ...onStableParameterNamesSerializationTest.kt | 105 +++++++++++++++ .../metadata/KlibMetadataMetadataVersion.kt | 2 +- .../deserialization/MemberDeserializer.kt | 5 + .../DeserializedMemberDescriptor.kt | 6 +- .../jvm/deserialization/JvmMetadataVersion.kt | 2 +- core/metadata/src/metadata.proto | 2 + .../metadata/deserialization/Flags.java | 10 +- .../metadata/test/MetadataSmokeTest.kt | 36 +++++- .../src/kotlinx/metadata/Flag.kt | 12 ++ .../org/jetbrains/kotlin/kotlinp/printers.kt | 7 +- 16 files changed, 334 insertions(+), 15 deletions(-) create mode 100644 compiler/testData/serialization/nonStableParameterNames/test.kt create mode 100644 compiler/tests-common/tests/org/jetbrains/kotlin/test/KlibTestUtil.kt create mode 100644 compiler/tests/org/jetbrains/kotlin/serialization/NonStableParameterNamesSerializationTest.kt diff --git a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt index 0d6ce29f401..9f902605b9f 100644 --- a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt +++ b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt @@ -310,7 +310,8 @@ class FirElementSerializer private constructor( simpleFunction?.isTailRec == true, simpleFunction?.isExternal == true, simpleFunction?.isSuspend == true, - simpleFunction?.isExpect == true + simpleFunction?.isExpect == true, + true // TODO: supply 'hasStableParameterNames' flag for metadata ) if (flags != builder.flags) { builder.flags = flags @@ -439,7 +440,8 @@ class FirElementSerializer private constructor( val flags = Flags.getConstructorFlags( constructor.nonSourceAnnotations(session).isNotEmpty(), ProtoEnumFlags.visibility(normalizeVisibility(constructor)), - !constructor.isPrimary + !constructor.isPrimary, + true // TODO: supply 'hasStableParameterNames' flag for metadata ) if (flags != builder.flags) { builder.flags = flags diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirArgumentsToParametersMapper.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirArgumentsToParametersMapper.kt index d702c13c6ea..0545796b530 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirArgumentsToParametersMapper.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirArgumentsToParametersMapper.kt @@ -264,7 +264,9 @@ private class FirCallArgumentsProcessor(private val function: FirFunction<*>) { private val FirExpression.argumentName: Name? get() = (this as? FirNamedArgumentExpression)?.name - // TODO: handle java functions + // TODO: handle functions with non-stable parameter names, see also + // org.jetbrains.kotlin.fir.serialization.FirElementSerializer.functionProto + // org.jetbrains.kotlin.fir.serialization.FirElementSerializer.constructorProto private val FirFunction<*>.hasStableParameterNames: Boolean get() = true } diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryFlags.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryFlags.kt index 00fba881c20..685afb42210 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryFlags.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/encodings/BinaryFlags.kt @@ -74,10 +74,10 @@ inline class FunctionFlags(val flags: Long) { val modality = ProtoEnumFlags.modality(modality) val kind = if (isFakeOverride) ProtoBuf.MemberKind.FAKE_OVERRIDE else ProtoBuf.MemberKind.DECLARATION - val flags = IrFlags.getFunctionFlags( hasAnnotation, visibility, modality, kind, - isOperator, isInfix, isInline, isTailrec, isExternal, isSuspend, isExpect + isOperator, isInfix, isInline, isTailrec, isExternal, isSuspend, isExpect, + true // hasStableParameterNames does not make sense for Ir, just pass the default value ) return flags.toLong() diff --git a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt index 9abfe4e1645..5a886cccaf2 100644 --- a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt +++ b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt @@ -291,6 +291,14 @@ class DescriptorSerializer private constructor( else descriptor.visibility + private fun shouldSerializeHasStableParameterNames(descriptor: CallableMemberDescriptor): Boolean { + return when { + descriptor.hasStableParameterNames() -> true + descriptor.kind == CallableMemberDescriptor.Kind.DELEGATION -> true // remove this line to fix KT-4758 + else -> false + } + } + fun functionProto(descriptor: FunctionDescriptor): ProtoBuf.Function.Builder? { if (!extension.shouldSerializeFunction(descriptor)) return null @@ -304,7 +312,7 @@ class DescriptorSerializer private constructor( ProtoEnumFlags.modality(descriptor.modality), ProtoEnumFlags.memberKind(descriptor.kind), descriptor.isOperator, descriptor.isInfix, descriptor.isInline, descriptor.isTailrec, descriptor.isExternal, - descriptor.isSuspend, descriptor.isExpect + descriptor.isSuspend, descriptor.isExpect, shouldSerializeHasStableParameterNames(descriptor) ) if (flags != builder.flags) { builder.flags = flags @@ -367,7 +375,8 @@ class DescriptorSerializer private constructor( val local = createChildSerializer(descriptor) val flags = Flags.getConstructorFlags( - hasAnnotations(descriptor), ProtoEnumFlags.visibility(normalizeVisibility(descriptor)), !descriptor.isPrimary + hasAnnotations(descriptor), ProtoEnumFlags.visibility(normalizeVisibility(descriptor)), !descriptor.isPrimary, + shouldSerializeHasStableParameterNames(descriptor) ) if (flags != builder.flags) { builder.flags = flags diff --git a/compiler/testData/serialization/nonStableParameterNames/test.kt b/compiler/testData/serialization/nonStableParameterNames/test.kt new file mode 100644 index 00000000000..432c0e62540 --- /dev/null +++ b/compiler/testData/serialization/nonStableParameterNames/test.kt @@ -0,0 +1,15 @@ +package library + +fun foo(a: Int, b: String, c: Boolean) = Unit + +class Foo(a: Int, b: String, c: Boolean) { + constructor(a: Int, b: String, c: Boolean, d: Double) : this(a, b, c) + + fun foo(a: Int, b: String, c: Boolean) = Unit + + class Bar(a: Int, b: String, c: Boolean) { + constructor(a: Int, b: String, c: Boolean, d: Double) : this(a, b, c) + + fun bar(a: Int, b: String, c: Boolean) = Unit + } +} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KlibTestUtil.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KlibTestUtil.kt new file mode 100644 index 00000000000..91ff8f45190 --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KlibTestUtil.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.test + +import com.intellij.openapi.util.Disposer +import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion +import org.jetbrains.kotlin.builtins.DefaultBuiltIns +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.konan.util.KlibMetadataFactories +import org.jetbrains.kotlin.library.* +import org.jetbrains.kotlin.library.impl.BuiltInsPlatform +import org.jetbrains.kotlin.library.impl.KotlinLibraryWriterImpl +import org.jetbrains.kotlin.library.metadata.NativeTypeTransformer +import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.CommonPlatforms +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.util.DummyLogger +import java.io.File +import org.jetbrains.kotlin.konan.file.File as KFile + +object KlibTestUtil { + fun compileCommonSourcesToKlib(sourceFiles: Collection, libraryName: String, klibFile: File) { + require(!Name.guessByFirstCharacter(libraryName).isSpecial) { "Invalid library name: $libraryName" } + + val configuration = KotlinTestUtils.newConfiguration() + configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) + configuration.put(CommonConfigurationKeys.MODULE_NAME, libraryName) + configuration.addKotlinSourceRoots(sourceFiles.map { it.absolutePath }) + + val rootDisposable = Disposer.newDisposable() + val module = try { + val environment = KotlinCoreEnvironment.createForTests( + parentDisposable = rootDisposable, + initialConfiguration = configuration, + extensionConfigs = EnvironmentConfigFiles.METADATA_CONFIG_FILES + ) + + CommonResolverForModuleFactory.analyzeFiles( + files = environment.getSourceFiles(), + moduleName = Name.special("<$libraryName>"), + dependOnBuiltIns = true, + languageVersionSettings = environment.configuration.languageVersionSettings, + targetPlatform = CommonPlatforms.defaultCommonPlatform + ) { content -> + environment.createPackagePartProvider(content.moduleContentScope) + }.moduleDescriptor + } finally { + Disposer.dispose(rootDisposable) + } + + serializeCommonModuleToKlib(module, libraryName, klibFile) + } + + fun serializeCommonModuleToKlib(module: ModuleDescriptor, libraryName: String, klibFile: File) { + require(klibFile.extension == KLIB_FILE_EXTENSION) { "KLIB file must have $KLIB_FILE_EXTENSION extension" } + + val serializer = KlibMetadataMonolithicSerializer( + languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT, + metadataVersion = KlibMetadataVersion.INSTANCE, + skipExpects = false + ) + + val serializedMetadata = serializer.serializeModule(module) + + val library = KotlinLibraryWriterImpl( + libDir = KFile(klibFile.path.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT)), + moduleName = libraryName, + versions = KotlinLibraryVersioning( + libraryVersion = null, + compilerVersion = null, + abiVersion = null, + metadataVersion = KlibMetadataVersion.INSTANCE.toString(), + irVersion = null + ), + builtInsPlatform = BuiltInsPlatform.COMMON, + nativeTargets = emptyList(), + nopack = false, + shortName = libraryName + ) + + library.addMetadata(serializedMetadata) + library.commit() + } + + fun deserializeKlibToCommonModule(klibFile: File): ModuleDescriptorImpl { + val library = resolveSingleFileKlib( + libraryFile = KFile(klibFile.path), + logger = DummyLogger, + strategy = ToolingSingleFileKlibResolveStrategy + ) + + val metadataFactories = KlibMetadataFactories({ DefaultBuiltIns.Instance }, NullFlexibleTypeDeserializer, NativeTypeTransformer()) + + val module = metadataFactories.DefaultDeserializedDescriptorFactory.createDescriptor( + library = library, + languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT, + storageManager = LockBasedStorageManager.NO_LOCKS, + builtIns = DefaultBuiltIns.Instance, + packageAccessHandler = null + ) + module.setDependencies(listOf(DefaultBuiltIns.Instance.builtInsModule, module)) + + return module + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/NonStableParameterNamesSerializationTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/NonStableParameterNamesSerializationTest.kt new file mode 100644 index 00000000000..a7ef57ff556 --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/serialization/NonStableParameterNamesSerializationTest.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.serialization + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion.CLASSIFIERS_MASK +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion.FUNCTIONS_MASK +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.test.KlibTestUtil +import org.jetbrains.kotlin.test.TestCaseWithTmpdir +import org.jetbrains.kotlin.utils.addToStdlib.assertedCast +import org.jetbrains.kotlin.utils.alwaysTrue +import java.io.File +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class NonStableParameterNamesSerializationTest : TestCaseWithTmpdir() { + fun testNonStableParameterNames() { + val originalKlibName = File(SOURCE_FILE).nameWithoutExtension + val originalKlibFile = File(tmpdir, "$originalKlibName.klib") + KlibTestUtil.compileCommonSourcesToKlib(listOf(File(SOURCE_FILE)), originalKlibName, originalKlibFile) + + val originalModule = KlibTestUtil.deserializeKlibToCommonModule(originalKlibFile) + val originalCallables = collectCallablesForPatch(originalModule) + + assertTrue { originalCallables.isNotEmpty() } + assertTrue { originalCallables.all { it.hasStableParameterNames() } } + + originalCallables.forEach { it.setHasStableParameterNames(false) } + + val patchedKlibName = "${originalKlibFile.nameWithoutExtension}-patched" + val patchedKlibFile = originalKlibFile.resolveSibling("$patchedKlibName.klib") + KlibTestUtil.serializeCommonModuleToKlib(originalModule, patchedKlibName, patchedKlibFile) + + val patchedModule = KlibTestUtil.deserializeKlibToCommonModule(patchedKlibFile) + val patchedCallables = collectCallablesForPatch(patchedModule) + + assertEquals(expected = originalCallables.size, actual = patchedCallables.size) + assertEquals(expected = originalCallables.map { it.signature }.toSet(), actual = patchedCallables.map { it.signature }.toSet()) + assertTrue { patchedCallables.all { !it.hasStableParameterNames() } } + } + + companion object { + private const val SOURCE_FILE = "compiler/testData/serialization/nonStableParameterNames/test.kt" + + private fun collectCallablesForPatch(module: ModuleDescriptorImpl): List { + fun DeclarationDescriptor.castToFunctionImpl(): FunctionDescriptorImpl = + assertedCast { "Not an instance of ${FunctionDescriptorImpl::class.java}: ${this::class.java}, $this" } + + val result = mutableListOf() + + fun recurse(memberScope: MemberScope) { + memberScope + .getContributedDescriptors(DescriptorKindFilter(FUNCTIONS_MASK or CLASSIFIERS_MASK)) + .forEach { descriptor -> + when (descriptor) { + is SimpleFunctionDescriptor -> { + if (descriptor.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) + result += descriptor.castToFunctionImpl() + } + is ClassDescriptor -> { + descriptor.constructors.mapTo(result) { it.castToFunctionImpl() } + recurse(descriptor.unsubstitutedMemberScope) + } + } + } + } + + val packageFragmentProvider = module.packageFragmentProviderForModuleContentWithoutDependencies + + fun recurse(packageFqName: FqName) { + packageFragmentProvider + .getPackageFragments(packageFqName) + .forEach { packageFragment -> + recurse(packageFragment.getMemberScope()) + } + + packageFragmentProvider + .getSubPackagesOf(packageFqName, alwaysTrue()) + .toSet() + .forEach { recurse(it) } + } + + recurse(FqName.ROOT) + + return result + } + + private val FunctionDescriptorImpl.signature: String + get() = buildString { + append(fqNameSafe) + append('(') + valueParameters.joinTo(this, ",") { it.type.constructor.declarationDescriptor?.fqNameSafe?.asString().orEmpty() } + append(')') + } + } +} diff --git a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibMetadataMetadataVersion.kt b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibMetadataMetadataVersion.kt index e15182d07d3..b492ac8f88a 100644 --- a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibMetadataMetadataVersion.kt +++ b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibMetadataMetadataVersion.kt @@ -15,7 +15,7 @@ class KlibMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) { companion object { @JvmField - val INSTANCE = KlibMetadataVersion(1, 4, 0) + val INSTANCE = KlibMetadataVersion(1, 4, 1) @JvmField val INVALID_VERSION = KlibMetadataVersion() diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt index 54dfae91e25..2a3c442dbe3 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt @@ -272,6 +272,7 @@ class MemberDeserializer(private val c: DeserializationContext) { ProtoEnumFlags.memberKind(Flags.MEMBER_KIND.get(flags)), proto, c.nameResolver, c.typeTable, versionRequirementTable, c.containerSource ) + val local = c.childContext(function, proto.typeParameterList) function.initializeWithCoroutinesExperimentalityStatus( @@ -294,6 +295,7 @@ class MemberDeserializer(private val c: DeserializationContext) { function.isTailrec = Flags.IS_TAILREC.get(flags) function.isSuspend = Flags.IS_SUSPEND.get(flags) function.isExpect = Flags.IS_EXPECT_FUNCTION.get(flags) + function.setHasStableParameterNames(!Flags.IS_FUNCTION_WITH_NON_STABLE_PARAMETER_NAMES.get(flags)) val mapValueForContract = c.components.contractDeserializer.deserializeContractFromFunction(proto, function, c.typeTable, local.typeDeserializer) @@ -337,6 +339,7 @@ class MemberDeserializer(private val c: DeserializationContext) { isPrimary, CallableMemberDescriptor.Kind.DECLARATION, proto, c.nameResolver, c.typeTable, c.versionRequirementTable, c.containerSource ) + val local = c.childContext(descriptor, listOf()) descriptor.initialize( local.memberDeserializer.valueParameters(proto.valueParameterList, proto, AnnotatedCallableKind.FUNCTION), @@ -344,6 +347,8 @@ class MemberDeserializer(private val c: DeserializationContext) { ) descriptor.returnType = classDescriptor.defaultType + descriptor.setHasStableParameterNames(!Flags.IS_CONSTRUCTOR_WITH_NON_STABLE_PARAMETER_NAMES.get(proto.flags)) + val doesClassContainIncompatibility = (c.containingDeclaration as? DeserializedClassDescriptor) ?.c?.typeDeserializer?.experimentalSuspendFunctionTypeEncountered == true diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt index 83001481cd2..71054f5e59b 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt @@ -120,6 +120,7 @@ class DeserializedSimpleFunctionDescriptor( newOwner, original as SimpleFunctionDescriptor?, annotations, newName ?: name, kind, proto, nameResolver, typeTable, versionRequirementTable, containerSource, source ).also { + it.setHasStableParameterNames(hasStableParameterNames()) it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode } } @@ -209,7 +210,10 @@ class DeserializedClassConstructorDescriptor( return DeserializedClassConstructorDescriptor( newOwner as ClassDescriptor, original as ConstructorDescriptor?, annotations, isPrimary, kind, proto, nameResolver, typeTable, versionRequirementTable, containerSource, source - ).also { it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode } + ).also { + it.setHasStableParameterNames(hasStableParameterNames()) + it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode + } } override fun isExternal(): Boolean = false 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 d5fae35c55b..56630dc464d 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 @@ -26,7 +26,7 @@ class JvmMetadataVersion(versionArray: IntArray, val isStrictSemantics: Boolean) companion object { @JvmField - val INSTANCE = JvmMetadataVersion(1, 4, 0) + val INSTANCE = JvmMetadataVersion(1, 4, 1) @JvmField val INVALID_VERSION = JvmMetadataVersion() diff --git a/core/metadata/src/metadata.proto b/core/metadata/src/metadata.proto index 38e6418c3e0..23a79879b8d 100644 --- a/core/metadata/src/metadata.proto +++ b/core/metadata/src/metadata.proto @@ -270,6 +270,7 @@ message Constructor { hasAnnotations Visibility isSecondary + hasNonStableParameterNames */ optional int32 flags = 1 [default = 6 /* public constructor, no annotations */]; @@ -294,6 +295,7 @@ message Function { isExternal isSuspend isExpect + hasNonStableParameterNames */ optional int32 flags = 9 [default = 6 /* public final function, no annotations */]; optional int32 old_flags = 1 [default = 6]; diff --git a/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/Flags.java b/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/Flags.java index 1fa3980376a..f533771e472 100644 --- a/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/Flags.java +++ b/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/Flags.java @@ -35,6 +35,7 @@ public class Flags { // Constructors public static final BooleanFlagField IS_SECONDARY = FlagField.booleanAfter(VISIBILITY); + public static final BooleanFlagField IS_CONSTRUCTOR_WITH_NON_STABLE_PARAMETER_NAMES = FlagField.booleanAfter(IS_SECONDARY); // Callables @@ -49,6 +50,7 @@ public class Flags { public static final BooleanFlagField IS_EXTERNAL_FUNCTION = FlagField.booleanAfter(IS_TAILREC); public static final BooleanFlagField IS_SUSPEND = FlagField.booleanAfter(IS_EXTERNAL_FUNCTION); public static final BooleanFlagField IS_EXPECT_FUNCTION = FlagField.booleanAfter(IS_SUSPEND); + public static final BooleanFlagField IS_FUNCTION_WITH_NON_STABLE_PARAMETER_NAMES = FlagField.booleanAfter(IS_EXPECT_FUNCTION); // Properties @@ -115,11 +117,13 @@ public class Flags { public static int getConstructorFlags( boolean hasAnnotations, @NotNull ProtoBuf.Visibility visibility, - boolean isSecondary + boolean isSecondary, + boolean hasStableParameterNames ) { return HAS_ANNOTATIONS.toFlags(hasAnnotations) | VISIBILITY.toFlags(visibility) | IS_SECONDARY.toFlags(isSecondary) + | IS_CONSTRUCTOR_WITH_NON_STABLE_PARAMETER_NAMES.toFlags(!hasStableParameterNames) ; } @@ -134,7 +138,8 @@ public class Flags { boolean isTailrec, boolean isExternal, boolean isSuspend, - boolean isExpect + boolean isExpect, + boolean hasStableParameterNames ) { return HAS_ANNOTATIONS.toFlags(hasAnnotations) | VISIBILITY.toFlags(visibility) @@ -147,6 +152,7 @@ public class Flags { | IS_EXTERNAL_FUNCTION.toFlags(isExternal) | IS_SUSPEND.toFlags(isSuspend) | IS_EXPECT_FUNCTION.toFlags(isExpect) + | IS_FUNCTION_WITH_NON_STABLE_PARAMETER_NAMES.toFlags(!hasStableParameterNames) ; } diff --git a/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt index 699cf8c8d98..223c9c03b83 100644 --- a/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt +++ b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt @@ -9,7 +9,7 @@ import kotlinx.metadata.* import kotlinx.metadata.jvm.* import org.jetbrains.org.objectweb.asm.ClassWriter import org.jetbrains.org.objectweb.asm.Opcodes -import org.junit.Assert.assertEquals +import org.junit.Assert.* import org.junit.Test import java.net.URLClassLoader import kotlin.coroutines.CoroutineContext @@ -169,4 +169,38 @@ class MetadataSmokeTest { ) as KotlinClassMetadata.SyntheticClass metadata.accept(KmLambda()) } + + @Test + fun unstableParameterNames() { + @Suppress("unused") + class Test(a: String, b: Int, c: Boolean) { + fun foo(a: String, b: Int, c: Boolean) = Unit + } + + val classWithStableParameterNames = + (KotlinClassMetadata.read(Test::class.java.readMetadata()) as KotlinClassMetadata.Class).toKmClass() + + classWithStableParameterNames.constructors.forEach { assertFalse(Flag.Constructor.HAS_NON_STABLE_PARAMETER_NAMES(it.flags)) } + classWithStableParameterNames.functions.forEach { assertFalse(Flag.Function.HAS_NON_STABLE_PARAMETER_NAMES(it.flags)) } + + val newMetadata = KotlinClassMetadata.Class.Writer().let { writer -> + KmClass().apply { + classWithStableParameterNames.accept( + object : KmClassVisitor(this) { + override fun visitConstructor(flags: Flags) = + super.visitConstructor(flags + flagsOf(Flag.Constructor.HAS_NON_STABLE_PARAMETER_NAMES)) + + override fun visitFunction(flags: Flags, name: String) = + super.visitFunction(flags + flagsOf(Flag.Function.HAS_NON_STABLE_PARAMETER_NAMES), name) + } + ) + }.accept(writer) + writer.write() + } + + val classWithUnstableParameterNames = newMetadata.toKmClass() + + classWithUnstableParameterNames.constructors.forEach { assertTrue(Flag.Constructor.HAS_NON_STABLE_PARAMETER_NAMES(it.flags)) } + classWithUnstableParameterNames.functions.forEach { assertTrue(Flag.Function.HAS_NON_STABLE_PARAMETER_NAMES(it.flags)) } + } } diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/Flag.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/Flag.kt index b72cbcdcc57..91c71fd70bf 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/Flag.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/Flag.kt @@ -224,6 +224,12 @@ class Flag(private val offset: Int, private val bitWidth: Int, private val value */ @JvmField val IS_PRIMARY = Flag(F.IS_SECONDARY, 0) + + /** + * Signifies that the corresponding constructor has non-stable parameter names, i.e. cannot be called with named arguments. + */ + @JvmField + val HAS_NON_STABLE_PARAMETER_NAMES = Flag(F.IS_CONSTRUCTOR_WITH_NON_STABLE_PARAMETER_NAMES) } /** @@ -302,6 +308,12 @@ class Flag(private val offset: Int, private val bitWidth: Int, private val value */ @JvmField val IS_EXPECT = Flag(F.IS_EXPECT_FUNCTION) + + /** + * Signifies that the corresponding function has non-stable parameter names, i.e. cannot be called with named arguments. + */ + @JvmField + val HAS_NON_STABLE_PARAMETER_NAMES = Flag(F.IS_FUNCTION_WITH_NON_STABLE_PARAMETER_NAMES) } /** diff --git a/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/printers.kt b/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/printers.kt index 807ca0894a5..e92a95d391b 100644 --- a/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/printers.kt +++ b/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/printers.kt @@ -958,7 +958,8 @@ private val CLASS_FLAGS_MAP = COMMON_FLAGS_MAP + mapOf( ) private val CONSTRUCTOR_FLAGS_MAP = VISIBILITY_FLAGS_MAP + mapOf( - Flag.Constructor.IS_PRIMARY to "/* primary */" + Flag.Constructor.IS_PRIMARY to "/* primary */", + Flag.Constructor.HAS_NON_STABLE_PARAMETER_NAMES to "/* non-stable parameter names */" ) private val FUNCTION_FLAGS_MAP = COMMON_FLAGS_MAP + mapOf( @@ -973,7 +974,9 @@ private val FUNCTION_FLAGS_MAP = COMMON_FLAGS_MAP + mapOf( Flag.Function.IS_TAILREC to "tailrec", Flag.Function.IS_EXTERNAL to "external", Flag.Function.IS_SUSPEND to "suspend", - Flag.Function.IS_EXPECT to "expect" + Flag.Function.IS_EXPECT to "expect", + + Flag.Function.HAS_NON_STABLE_PARAMETER_NAMES to "/* non-stable parameter names */" ) private val PROPERTY_FLAGS_MAP = COMMON_FLAGS_MAP + mapOf(