Metadata: 'non-stable parameter names' flag for callables

^KT-34602
This commit is contained in:
Dmitriy Dolovov
2020-07-20 18:21:34 +07:00
parent 8cace2bab4
commit 3d9093583f
16 changed files with 334 additions and 15 deletions
@@ -310,7 +310,8 @@ class FirElementSerializer private constructor(
simpleFunction?.isTailRec == true, simpleFunction?.isTailRec == true,
simpleFunction?.isExternal == true, simpleFunction?.isExternal == true,
simpleFunction?.isSuspend == true, simpleFunction?.isSuspend == true,
simpleFunction?.isExpect == true simpleFunction?.isExpect == true,
true // TODO: supply 'hasStableParameterNames' flag for metadata
) )
if (flags != builder.flags) { if (flags != builder.flags) {
builder.flags = flags builder.flags = flags
@@ -439,7 +440,8 @@ class FirElementSerializer private constructor(
val flags = Flags.getConstructorFlags( val flags = Flags.getConstructorFlags(
constructor.nonSourceAnnotations(session).isNotEmpty(), constructor.nonSourceAnnotations(session).isNotEmpty(),
ProtoEnumFlags.visibility(normalizeVisibility(constructor)), ProtoEnumFlags.visibility(normalizeVisibility(constructor)),
!constructor.isPrimary !constructor.isPrimary,
true // TODO: supply 'hasStableParameterNames' flag for metadata
) )
if (flags != builder.flags) { if (flags != builder.flags) {
builder.flags = flags builder.flags = flags
@@ -264,7 +264,9 @@ private class FirCallArgumentsProcessor(private val function: FirFunction<*>) {
private val FirExpression.argumentName: Name? private val FirExpression.argumentName: Name?
get() = (this as? FirNamedArgumentExpression)?.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 private val FirFunction<*>.hasStableParameterNames: Boolean
get() = true get() = true
} }
@@ -74,10 +74,10 @@ inline class FunctionFlags(val flags: Long) {
val modality = ProtoEnumFlags.modality(modality) val modality = ProtoEnumFlags.modality(modality)
val kind = if (isFakeOverride) ProtoBuf.MemberKind.FAKE_OVERRIDE else ProtoBuf.MemberKind.DECLARATION val kind = if (isFakeOverride) ProtoBuf.MemberKind.FAKE_OVERRIDE else ProtoBuf.MemberKind.DECLARATION
val flags = IrFlags.getFunctionFlags( val flags = IrFlags.getFunctionFlags(
hasAnnotation, visibility, modality, kind, 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() return flags.toLong()
@@ -291,6 +291,14 @@ class DescriptorSerializer private constructor(
else else
descriptor.visibility 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? { fun functionProto(descriptor: FunctionDescriptor): ProtoBuf.Function.Builder? {
if (!extension.shouldSerializeFunction(descriptor)) return null if (!extension.shouldSerializeFunction(descriptor)) return null
@@ -304,7 +312,7 @@ class DescriptorSerializer private constructor(
ProtoEnumFlags.modality(descriptor.modality), ProtoEnumFlags.modality(descriptor.modality),
ProtoEnumFlags.memberKind(descriptor.kind), ProtoEnumFlags.memberKind(descriptor.kind),
descriptor.isOperator, descriptor.isInfix, descriptor.isInline, descriptor.isTailrec, descriptor.isExternal, descriptor.isOperator, descriptor.isInfix, descriptor.isInline, descriptor.isTailrec, descriptor.isExternal,
descriptor.isSuspend, descriptor.isExpect descriptor.isSuspend, descriptor.isExpect, shouldSerializeHasStableParameterNames(descriptor)
) )
if (flags != builder.flags) { if (flags != builder.flags) {
builder.flags = flags builder.flags = flags
@@ -367,7 +375,8 @@ class DescriptorSerializer private constructor(
val local = createChildSerializer(descriptor) val local = createChildSerializer(descriptor)
val flags = Flags.getConstructorFlags( 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) { if (flags != builder.flags) {
builder.flags = flags builder.flags = flags
@@ -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
}
}
@@ -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<File>, 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
}
}
@@ -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<FunctionDescriptorImpl> {
fun DeclarationDescriptor.castToFunctionImpl(): FunctionDescriptorImpl =
assertedCast { "Not an instance of ${FunctionDescriptorImpl::class.java}: ${this::class.java}, $this" }
val result = mutableListOf<FunctionDescriptorImpl>()
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(')')
}
}
}
@@ -15,7 +15,7 @@ class KlibMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
companion object { companion object {
@JvmField @JvmField
val INSTANCE = KlibMetadataVersion(1, 4, 0) val INSTANCE = KlibMetadataVersion(1, 4, 1)
@JvmField @JvmField
val INVALID_VERSION = KlibMetadataVersion() val INVALID_VERSION = KlibMetadataVersion()
@@ -272,6 +272,7 @@ class MemberDeserializer(private val c: DeserializationContext) {
ProtoEnumFlags.memberKind(Flags.MEMBER_KIND.get(flags)), proto, c.nameResolver, c.typeTable, versionRequirementTable, ProtoEnumFlags.memberKind(Flags.MEMBER_KIND.get(flags)), proto, c.nameResolver, c.typeTable, versionRequirementTable,
c.containerSource c.containerSource
) )
val local = c.childContext(function, proto.typeParameterList) val local = c.childContext(function, proto.typeParameterList)
function.initializeWithCoroutinesExperimentalityStatus( function.initializeWithCoroutinesExperimentalityStatus(
@@ -294,6 +295,7 @@ class MemberDeserializer(private val c: DeserializationContext) {
function.isTailrec = Flags.IS_TAILREC.get(flags) function.isTailrec = Flags.IS_TAILREC.get(flags)
function.isSuspend = Flags.IS_SUSPEND.get(flags) function.isSuspend = Flags.IS_SUSPEND.get(flags)
function.isExpect = Flags.IS_EXPECT_FUNCTION.get(flags) function.isExpect = Flags.IS_EXPECT_FUNCTION.get(flags)
function.setHasStableParameterNames(!Flags.IS_FUNCTION_WITH_NON_STABLE_PARAMETER_NAMES.get(flags))
val mapValueForContract = val mapValueForContract =
c.components.contractDeserializer.deserializeContractFromFunction(proto, function, c.typeTable, local.typeDeserializer) 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, isPrimary, CallableMemberDescriptor.Kind.DECLARATION, proto, c.nameResolver, c.typeTable, c.versionRequirementTable,
c.containerSource c.containerSource
) )
val local = c.childContext(descriptor, listOf()) val local = c.childContext(descriptor, listOf())
descriptor.initialize( descriptor.initialize(
local.memberDeserializer.valueParameters(proto.valueParameterList, proto, AnnotatedCallableKind.FUNCTION), local.memberDeserializer.valueParameters(proto.valueParameterList, proto, AnnotatedCallableKind.FUNCTION),
@@ -344,6 +347,8 @@ class MemberDeserializer(private val c: DeserializationContext) {
) )
descriptor.returnType = classDescriptor.defaultType descriptor.returnType = classDescriptor.defaultType
descriptor.setHasStableParameterNames(!Flags.IS_CONSTRUCTOR_WITH_NON_STABLE_PARAMETER_NAMES.get(proto.flags))
val doesClassContainIncompatibility = val doesClassContainIncompatibility =
(c.containingDeclaration as? DeserializedClassDescriptor) (c.containingDeclaration as? DeserializedClassDescriptor)
?.c?.typeDeserializer?.experimentalSuspendFunctionTypeEncountered == true ?.c?.typeDeserializer?.experimentalSuspendFunctionTypeEncountered == true
@@ -120,6 +120,7 @@ class DeserializedSimpleFunctionDescriptor(
newOwner, original as SimpleFunctionDescriptor?, annotations, newName ?: name, kind, newOwner, original as SimpleFunctionDescriptor?, annotations, newName ?: name, kind,
proto, nameResolver, typeTable, versionRequirementTable, containerSource, source proto, nameResolver, typeTable, versionRequirementTable, containerSource, source
).also { ).also {
it.setHasStableParameterNames(hasStableParameterNames())
it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode
} }
} }
@@ -209,7 +210,10 @@ class DeserializedClassConstructorDescriptor(
return DeserializedClassConstructorDescriptor( return DeserializedClassConstructorDescriptor(
newOwner as ClassDescriptor, original as ConstructorDescriptor?, annotations, isPrimary, kind, newOwner as ClassDescriptor, original as ConstructorDescriptor?, annotations, isPrimary, kind,
proto, nameResolver, typeTable, versionRequirementTable, containerSource, source proto, nameResolver, typeTable, versionRequirementTable, containerSource, source
).also { it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode } ).also {
it.setHasStableParameterNames(hasStableParameterNames())
it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode
}
} }
override fun isExternal(): Boolean = false override fun isExternal(): Boolean = false
@@ -26,7 +26,7 @@ class JvmMetadataVersion(versionArray: IntArray, val isStrictSemantics: Boolean)
companion object { companion object {
@JvmField @JvmField
val INSTANCE = JvmMetadataVersion(1, 4, 0) val INSTANCE = JvmMetadataVersion(1, 4, 1)
@JvmField @JvmField
val INVALID_VERSION = JvmMetadataVersion() val INVALID_VERSION = JvmMetadataVersion()
+2
View File
@@ -270,6 +270,7 @@ message Constructor {
hasAnnotations hasAnnotations
Visibility Visibility
isSecondary isSecondary
hasNonStableParameterNames
*/ */
optional int32 flags = 1 [default = 6 /* public constructor, no annotations */]; optional int32 flags = 1 [default = 6 /* public constructor, no annotations */];
@@ -294,6 +295,7 @@ message Function {
isExternal isExternal
isSuspend isSuspend
isExpect isExpect
hasNonStableParameterNames
*/ */
optional int32 flags = 9 [default = 6 /* public final function, no annotations */]; optional int32 flags = 9 [default = 6 /* public final function, no annotations */];
optional int32 old_flags = 1 [default = 6]; optional int32 old_flags = 1 [default = 6];
@@ -35,6 +35,7 @@ public class Flags {
// Constructors // Constructors
public static final BooleanFlagField IS_SECONDARY = FlagField.booleanAfter(VISIBILITY); 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 // Callables
@@ -49,6 +50,7 @@ public class Flags {
public static final BooleanFlagField IS_EXTERNAL_FUNCTION = FlagField.booleanAfter(IS_TAILREC); 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_SUSPEND = FlagField.booleanAfter(IS_EXTERNAL_FUNCTION);
public static final BooleanFlagField IS_EXPECT_FUNCTION = FlagField.booleanAfter(IS_SUSPEND); 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 // Properties
@@ -115,11 +117,13 @@ public class Flags {
public static int getConstructorFlags( public static int getConstructorFlags(
boolean hasAnnotations, boolean hasAnnotations,
@NotNull ProtoBuf.Visibility visibility, @NotNull ProtoBuf.Visibility visibility,
boolean isSecondary boolean isSecondary,
boolean hasStableParameterNames
) { ) {
return HAS_ANNOTATIONS.toFlags(hasAnnotations) return HAS_ANNOTATIONS.toFlags(hasAnnotations)
| VISIBILITY.toFlags(visibility) | VISIBILITY.toFlags(visibility)
| IS_SECONDARY.toFlags(isSecondary) | IS_SECONDARY.toFlags(isSecondary)
| IS_CONSTRUCTOR_WITH_NON_STABLE_PARAMETER_NAMES.toFlags(!hasStableParameterNames)
; ;
} }
@@ -134,7 +138,8 @@ public class Flags {
boolean isTailrec, boolean isTailrec,
boolean isExternal, boolean isExternal,
boolean isSuspend, boolean isSuspend,
boolean isExpect boolean isExpect,
boolean hasStableParameterNames
) { ) {
return HAS_ANNOTATIONS.toFlags(hasAnnotations) return HAS_ANNOTATIONS.toFlags(hasAnnotations)
| VISIBILITY.toFlags(visibility) | VISIBILITY.toFlags(visibility)
@@ -147,6 +152,7 @@ public class Flags {
| IS_EXTERNAL_FUNCTION.toFlags(isExternal) | IS_EXTERNAL_FUNCTION.toFlags(isExternal)
| IS_SUSPEND.toFlags(isSuspend) | IS_SUSPEND.toFlags(isSuspend)
| IS_EXPECT_FUNCTION.toFlags(isExpect) | IS_EXPECT_FUNCTION.toFlags(isExpect)
| IS_FUNCTION_WITH_NON_STABLE_PARAMETER_NAMES.toFlags(!hasStableParameterNames)
; ;
} }
@@ -9,7 +9,7 @@ import kotlinx.metadata.*
import kotlinx.metadata.jvm.* import kotlinx.metadata.jvm.*
import org.jetbrains.org.objectweb.asm.ClassWriter import org.jetbrains.org.objectweb.asm.ClassWriter
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.junit.Assert.assertEquals import org.junit.Assert.*
import org.junit.Test import org.junit.Test
import java.net.URLClassLoader import java.net.URLClassLoader
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@@ -169,4 +169,38 @@ class MetadataSmokeTest {
) as KotlinClassMetadata.SyntheticClass ) as KotlinClassMetadata.SyntheticClass
metadata.accept(KmLambda()) 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)) }
}
} }
@@ -224,6 +224,12 @@ class Flag(private val offset: Int, private val bitWidth: Int, private val value
*/ */
@JvmField @JvmField
val IS_PRIMARY = Flag(F.IS_SECONDARY, 0) 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 @JvmField
val IS_EXPECT = Flag(F.IS_EXPECT_FUNCTION) 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)
} }
/** /**
@@ -958,7 +958,8 @@ private val CLASS_FLAGS_MAP = COMMON_FLAGS_MAP + mapOf(
) )
private val CONSTRUCTOR_FLAGS_MAP = VISIBILITY_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( 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_TAILREC to "tailrec",
Flag.Function.IS_EXTERNAL to "external", Flag.Function.IS_EXTERNAL to "external",
Flag.Function.IS_SUSPEND to "suspend", 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( private val PROPERTY_FLAGS_MAP = COMMON_FLAGS_MAP + mapOf(