[Commonizer] Allow rewriting callables names to succeed commonization

^KT-34602
This commit is contained in:
Dmitriy Dolovov
2020-08-05 20:20:12 +07:00
parent 8904f5652b
commit 4418dc85ca
11 changed files with 319 additions and 58 deletions
@@ -52,8 +52,8 @@ interface CirHasTypeParameters {
}
interface CirCallableMemberWithParameters {
val valueParameters: List<CirValueParameter>
val hasStableParameterNames: Boolean
var valueParameters: List<CirValueParameter>
var hasStableParameterNames: Boolean
}
/**
@@ -14,8 +14,8 @@ data class CirClassConstructorImpl(
override val typeParameters: List<CirTypeParameter>,
override val visibility: Visibility,
override val containingClassDetails: CirContainingClassDetails,
override val valueParameters: List<CirValueParameter>,
override val hasStableParameterNames: Boolean,
override var valueParameters: List<CirValueParameter>,
override var hasStableParameterNames: Boolean,
override val isPrimary: Boolean,
override val kind: CallableMemberDescriptor.Kind
) : CirClassConstructor
@@ -18,8 +18,8 @@ data class CirFunctionImpl(
override val visibility: Visibility,
override val modality: Modality,
override val containingClassDetails: CirContainingClassDetails?,
override val valueParameters: List<CirValueParameter>,
override val hasStableParameterNames: Boolean,
override var valueParameters: List<CirValueParameter>,
override var hasStableParameterNames: Boolean,
override val extensionReceiver: CirExtensionReceiver?,
override val returnType: CirType,
override val kind: CallableMemberDescriptor.Kind,
@@ -51,4 +51,9 @@ abstract class AbstractListCommonizer<T, R>(
return !error
}
protected fun forEachSingleElementCommonizer(action: (index: Int, Commonizer<T, R>) -> Unit) {
val commonizers = commonizers ?: failInEmptyState()
commonizers.forEachIndexed(action)
}
}
@@ -0,0 +1,240 @@
/*
* 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.descriptors.commonizer.core
import com.intellij.util.containers.FactoryMap
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirCallableMemberWithParameters
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirClassifierId
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirHasAnnotations
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirValueParameter
import org.jetbrains.kotlin.descriptors.commonizer.cir.factory.CirValueParameterFactory
import org.jetbrains.kotlin.descriptors.commonizer.core.CallableValueParametersCommonizer.CallableToPatch.Companion.doNothing
import org.jetbrains.kotlin.descriptors.commonizer.core.CallableValueParametersCommonizer.CallableToPatch.Companion.patchCallables
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCache
import org.jetbrains.kotlin.descriptors.commonizer.utils.intern
import org.jetbrains.kotlin.descriptors.commonizer.utils.isObjCInteropCallableAnnotation
import org.jetbrains.kotlin.name.Name
class CallableValueParametersCommonizer(
cache: CirClassifiersCache
) : Commonizer<CirCallableMemberWithParameters, CallableValueParametersCommonizer.Result> {
class Result(
val hasStableParameterNames: Boolean,
val valueParameters: List<CirValueParameter>,
val patchCallables: () -> Unit
)
private class CallableToPatch(
val callable: CirCallableMemberWithParameters,
val originalNames: ValueParameterNames
) {
init {
check(originalNames is ValueParameterNames.Generated || originalNames is ValueParameterNames.Real)
}
val canNamesBeOverwritten by lazy { callable.canNamesBeOverwritten() }
companion object {
fun doNothing(): () -> Unit = {}
fun List<CallableToPatch>.patchCallables(generated: Boolean, newNames: List<Name>): () -> Unit {
val callablesToPatch = filter { it.originalNames is ValueParameterNames.Generated == generated }
.takeIf { it.isNotEmpty() }
?: return doNothing()
return {
callablesToPatch.forEach { callableToPatch ->
val callable = callableToPatch.callable
callable.hasStableParameterNames = false
callable.valueParameters = callable.valueParameters.mapIndexed { index, valueParameter ->
val newName = newNames[index]
if (valueParameter.name != newName) {
CirValueParameterFactory.create(
annotations = valueParameter.annotations,
name = newName,
returnType = valueParameter.returnType,
varargElementType = valueParameter.varargElementType,
declaresDefaultValue = valueParameter.declaresDefaultValue,
isCrossinline = valueParameter.isCrossinline,
isNoinline = valueParameter.isNoinline
)
} else valueParameter
}
}
}
}
}
}
private sealed class ValueParameterNames {
object Generated : ValueParameterNames()
data class Real(val names: List<Name>) : ValueParameterNames()
class MultipleReal(valueParameters: List<CirValueParameter>) : ValueParameterNames() {
val generatedNames: List<Name> = generatedNames(valueParameters)
}
companion object {
fun buildFor(callable: CirCallableMemberWithParameters): ValueParameterNames {
val valueParameters = callable.valueParameters
if (valueParameters.isEmpty())
return Real(emptyList())
var real = false
val names = callable.valueParameters.mapIndexed { index, valueParameter ->
val name = valueParameter.name
val plainName = name.asString()
if (valueParameter.varargElementType != null) {
if (plainName != VARIADIC_ARGUMENTS) {
real = true
}
} else {
if (!plainName.startsWith(REGULAR_ARGUMENT_PREFIX)
|| index.toString() != plainName.substring(REGULAR_ARGUMENT_PREFIX.length)
) {
real = true
}
}
name
}
return if (real) Real(names) else Generated
}
fun generatedNames(valueParameters: List<CirValueParameter>): List<Name> =
valueParameters.mapIndexed { index, valueParameter ->
if (valueParameter.varargElementType != null) {
VARIADIC_ARGUMENTS_NAME
} else {
REGULAR_ARGUMENT_NAMES.getValue(index)
}
}
}
}
private val valueParameters = ValueParameterListCommonizer(cache)
private val callables: MutableList<CallableToPatch> = mutableListOf()
private var hasStableParameterNames = true
private var valueParameterNames: ValueParameterNames? = null
private var error = false
override val result: Result
get() {
// don't inline `patchCallables` property;
// valueParameters.overwriteNames() should be called strongly before valueParameters.result
val patchCallables = when (val valueParameterNames = checkState(valueParameterNames, error)) {
ValueParameterNames.Generated -> doNothing()
is ValueParameterNames.Real -> {
val newNames = valueParameterNames.names
valueParameters.overwriteNames(newNames)
callables.patchCallables(generated = true, newNames)
}
is ValueParameterNames.MultipleReal -> {
val generatedNames = valueParameterNames.generatedNames
valueParameters.overwriteNames(generatedNames)
callables.patchCallables(generated = false, generatedNames)
}
}
return Result(
hasStableParameterNames = hasStableParameterNames,
valueParameters = valueParameters.result,
patchCallables = patchCallables
)
}
override fun commonizeWith(next: CirCallableMemberWithParameters): Boolean {
if (error)
return false
error = !valueParameters.commonizeWith(next.valueParameters)
|| !commonizeValueParameterNames(next)
return !error
}
private fun commonizeValueParameterNames(next: CirCallableMemberWithParameters): Boolean {
val nextNames = ValueParameterNames.buildFor(next)
val nextCallable = CallableToPatch(next, nextNames)
valueParameterNames = when (val currentNames = valueParameterNames) {
null -> {
when (nextNames) {
ValueParameterNames.Generated,
is ValueParameterNames.Real -> {
hasStableParameterNames = next.hasStableParameterNames
}
else -> failIllegalState(currentNames, nextNames)
}
nextNames
}
ValueParameterNames.Generated -> {
@Suppress("LiftReturnOrAssignment")
when (nextNames) {
ValueParameterNames.Generated -> {
hasStableParameterNames = hasStableParameterNames && next.hasStableParameterNames
}
is ValueParameterNames.Real -> {
if (callables.any { !it.canNamesBeOverwritten }) return false
hasStableParameterNames = false
}
else -> failIllegalState(currentNames, nextNames)
}
nextNames
}
is ValueParameterNames.Real -> {
when (nextNames) {
ValueParameterNames.Generated -> {
if (!nextCallable.canNamesBeOverwritten) return false
hasStableParameterNames = false
currentNames
}
is ValueParameterNames.Real -> {
if (nextNames == currentNames) {
hasStableParameterNames = hasStableParameterNames && next.hasStableParameterNames
currentNames
} else {
if (callables.any { !it.canNamesBeOverwritten } || !nextCallable.canNamesBeOverwritten) return false
hasStableParameterNames = false
ValueParameterNames.MultipleReal(nextCallable.callable.valueParameters)
}
}
else -> failIllegalState(currentNames, nextNames)
}
}
is ValueParameterNames.MultipleReal -> {
if (!nextCallable.canNamesBeOverwritten) return false
currentNames
}
}
callables += nextCallable
return true
}
companion object {
private const val VARIADIC_ARGUMENTS = "variadicArguments"
private const val REGULAR_ARGUMENT_PREFIX = "arg"
private val VARIADIC_ARGUMENTS_NAME = Name.identifier(VARIADIC_ARGUMENTS).intern()
private val REGULAR_ARGUMENT_NAMES = FactoryMap.create<Int, Name> { index ->
Name.identifier(REGULAR_ARGUMENT_PREFIX + index).intern()
}
private fun CirCallableMemberWithParameters.canNamesBeOverwritten(): Boolean {
return (this as CirHasAnnotations).annotations.none { annotation ->
(annotation.type.classifierId as? CirClassifierId.ClassOrTypeAlias)?.classId?.isObjCInteropCallableAnnotation == true
}
}
private fun failIllegalState(current: ValueParameterNames?, next: ValueParameterNames): Nothing =
throw IllegalCommonizerStateException("unexpected next state $next with current state $current")
}
}
@@ -17,19 +17,23 @@ class ClassConstructorCommonizer(cache: CirClassifiersCache) : AbstractStandardC
private lateinit var kind: CallableMemberDescriptor.Kind
private val visibility = VisibilityCommonizer.equalizing()
private val typeParameters = TypeParameterListCommonizer(cache)
private val valueParameters = ValueParameterListCommonizer(cache)
private var hasStableParameterNames = true
private val valueParameters = CallableValueParametersCommonizer(cache)
override fun commonizationResult() = CirClassConstructorFactory.create(
annotations = emptyList(),
typeParameters = typeParameters.result,
visibility = visibility.result,
containingClassDetails = CirContainingClassDetailsFactory.DOES_NOT_MATTER,
valueParameters = valueParameters.result,
hasStableParameterNames = hasStableParameterNames,
isPrimary = isPrimary,
kind = kind
)
override fun commonizationResult(): CirClassConstructor {
val valueParameters = valueParameters.result
valueParameters.patchCallables()
return CirClassConstructorFactory.create(
annotations = emptyList(),
typeParameters = typeParameters.result,
visibility = visibility.result,
containingClassDetails = CirContainingClassDetailsFactory.DOES_NOT_MATTER,
valueParameters = valueParameters.valueParameters,
hasStableParameterNames = valueParameters.hasStableParameterNames,
isPrimary = isPrimary,
kind = kind
)
}
override fun initialize(first: CirClassConstructor) {
isPrimary = first.isPrimary
@@ -37,18 +41,12 @@ class ClassConstructorCommonizer(cache: CirClassifiersCache) : AbstractStandardC
}
override fun doCommonizeWith(next: CirClassConstructor): Boolean {
val result = !next.containingClassDetails.kind.isSingleton // don't commonize constructors for objects and enum entries
return !next.containingClassDetails.kind.isSingleton // don't commonize constructors for objects and enum entries
&& next.containingClassDetails.modality != Modality.SEALED // don't commonize constructors for sealed classes (not not their subclasses)
&& isPrimary == next.isPrimary
&& kind == next.kind
&& visibility.commonizeWith(next)
&& typeParameters.commonizeWith(next.typeParameters)
&& valueParameters.commonizeWith(next.valueParameters)
if (result) {
hasStableParameterNames = hasStableParameterNames && next.hasStableParameterNames
}
return result
&& valueParameters.commonizeWith(next)
}
}
@@ -12,34 +12,32 @@ import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCach
class FunctionCommonizer(cache: CirClassifiersCache) : AbstractFunctionOrPropertyCommonizer<CirFunction>(cache) {
private val annotations = AnnotationsCommonizer()
private val modifiers = FunctionModifiersCommonizer()
private val valueParameters = ValueParameterListCommonizer(cache)
private var hasStableParameterNames = true
private val valueParameters = CallableValueParametersCommonizer(cache)
override fun commonizationResult() = CirFunctionFactory.create(
annotations = annotations.result,
name = name,
typeParameters = typeParameters.result,
visibility = visibility.result,
modality = modality.result,
containingClassDetails = null,
valueParameters = valueParameters.result,
hasStableParameterNames = hasStableParameterNames,
extensionReceiver = extensionReceiver.result,
returnType = returnType.result,
kind = kind,
modifiers = modifiers.result
)
override fun commonizationResult(): CirFunction {
val valueParameters = valueParameters.result
valueParameters.patchCallables()
return CirFunctionFactory.create(
annotations = annotations.result,
name = name,
typeParameters = typeParameters.result,
visibility = visibility.result,
modality = modality.result,
containingClassDetails = null,
valueParameters = valueParameters.valueParameters,
hasStableParameterNames = valueParameters.hasStableParameterNames,
extensionReceiver = extensionReceiver.result,
returnType = returnType.result,
kind = kind,
modifiers = modifiers.result
)
}
override fun doCommonizeWith(next: CirFunction): Boolean {
val result = super.doCommonizeWith(next)
return super.doCommonizeWith(next)
&& annotations.commonizeWith(next.annotations)
&& modifiers.commonizeWith(next.modifiers)
&& valueParameters.commonizeWith(next.valueParameters)
if (result) {
hasStableParameterNames = hasStableParameterNames && next.hasStableParameterNames
}
return result
&& valueParameters.commonizeWith(next)
}
}
@@ -39,7 +39,6 @@ class ValueParameterCommonizer(cache: CirClassifiersCache) : AbstractStandardCom
override fun doCommonizeWith(next: CirValueParameter): Boolean {
val result = !next.declaresDefaultValue
&& varargElementType.isNull() == next.varargElementType.isNull()
&& name == next.name
&& returnType.commonizeWith(next.returnType)
if (result) {
@@ -49,4 +48,8 @@ class ValueParameterCommonizer(cache: CirClassifiersCache) : AbstractStandardCom
return result
}
fun overwriteName(name: Name) {
this.name = name
}
}
@@ -7,7 +7,14 @@ package org.jetbrains.kotlin.descriptors.commonizer.core
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirValueParameter
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCache
import org.jetbrains.kotlin.name.Name
class ValueParameterListCommonizer(cache: CirClassifiersCache) : AbstractListCommonizer<CirValueParameter, CirValueParameter>(
singleElementCommonizerFactory = { ValueParameterCommonizer(cache) }
)
) {
fun overwriteNames(names: List<Name>) {
forEachSingleElementCommonizer { index, singleElementCommonizer ->
(singleElementCommonizer as ValueParameterCommonizer).overwriteName(names[index])
}
}
}
@@ -28,21 +28,21 @@ data class PropertyApproximationKey(
/** Used for approximation of [SimpleFunctionDescriptor]s before running concrete [Commonizer]s */
data class FunctionApproximationKey(
val name: Name,
val valueParameters: List<Pair<Name, CirTypeSignature>>,
val valueParameters: List<CirTypeSignature>,
val extensionReceiverParameter: CirTypeSignature?
) {
constructor(function: SimpleFunctionDescriptor) : this(
function.name.intern(),
function.valueParameters.map { it.name.intern() to it.type.signature },
function.valueParameters.map { it.type.signature },
function.extensionReceiverParameter?.type?.signature
)
}
/** Used for approximation of [ConstructorDescriptor]s before running concrete [Commonizer]s */
data class ConstructorApproximationKey(
val valueParameters: List<Pair<Name, CirTypeSignature>>
val valueParameters: List<CirTypeSignature>
) {
constructor(constructor: ConstructorDescriptor) : this(
constructor.valueParameters.map { it.name.intern() to it.type.signature }
constructor.valueParameters.map { it.type.signature }
)
}
@@ -26,7 +26,14 @@ private val KOTLIN_NATIVE_SYNTHETIC_PACKAGES = ForwardDeclarationsFqNames.synthe
fqName.asString()
}
private const val DARWIN_PACKAGE_PREFIX = "platform.darwin"
private const val CINTEROP_PACKAGE = "kotlinx.cinterop"
private const val DARWIN_PACKAGE = "platform.darwin"
private val OBJC_INTEROP_CALLABLE_ANNOTATIONS = listOf(
"ObjCMethod",
"ObjCConstructor",
"ObjCFactory"
)
internal val FqName.isUnderStandardKotlinPackages: Boolean
get() = hasAnyPrefix(STANDARD_KOTLIN_PACKAGES)
@@ -35,7 +42,7 @@ internal val FqName.isUnderKotlinNativeSyntheticPackages: Boolean
get() = hasAnyPrefix(KOTLIN_NATIVE_SYNTHETIC_PACKAGES)
internal val FqName.isUnderDarwinPackage: Boolean
get() = asString().hasPrefix(DARWIN_PACKAGE_PREFIX)
get() = asString().hasPrefix(DARWIN_PACKAGE)
private fun FqName.hasAnyPrefix(prefixes: List<String>): Boolean =
asString().let { fqName -> prefixes.any(fqName::hasPrefix) }
@@ -48,3 +55,6 @@ private fun String.hasPrefix(prefix: String): Boolean {
else -> false
}
}
internal val ClassId.isObjCInteropCallableAnnotation: Boolean
get() = packageFqName.asString() == CINTEROP_PACKAGE && relativeClassName.asString() in OBJC_INTEROP_CALLABLE_ANNOTATIONS