[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 { interface CirCallableMemberWithParameters {
val valueParameters: List<CirValueParameter> var valueParameters: List<CirValueParameter>
val hasStableParameterNames: Boolean var hasStableParameterNames: Boolean
} }
/** /**
@@ -14,8 +14,8 @@ data class CirClassConstructorImpl(
override val typeParameters: List<CirTypeParameter>, override val typeParameters: List<CirTypeParameter>,
override val visibility: Visibility, override val visibility: Visibility,
override val containingClassDetails: CirContainingClassDetails, override val containingClassDetails: CirContainingClassDetails,
override val valueParameters: List<CirValueParameter>, override var valueParameters: List<CirValueParameter>,
override val hasStableParameterNames: Boolean, override var hasStableParameterNames: Boolean,
override val isPrimary: Boolean, override val isPrimary: Boolean,
override val kind: CallableMemberDescriptor.Kind override val kind: CallableMemberDescriptor.Kind
) : CirClassConstructor ) : CirClassConstructor
@@ -18,8 +18,8 @@ data class CirFunctionImpl(
override val visibility: Visibility, override val visibility: Visibility,
override val modality: Modality, override val modality: Modality,
override val containingClassDetails: CirContainingClassDetails?, override val containingClassDetails: CirContainingClassDetails?,
override val valueParameters: List<CirValueParameter>, override var valueParameters: List<CirValueParameter>,
override val hasStableParameterNames: Boolean, override var hasStableParameterNames: Boolean,
override val extensionReceiver: CirExtensionReceiver?, override val extensionReceiver: CirExtensionReceiver?,
override val returnType: CirType, override val returnType: CirType,
override val kind: CallableMemberDescriptor.Kind, override val kind: CallableMemberDescriptor.Kind,
@@ -51,4 +51,9 @@ abstract class AbstractListCommonizer<T, R>(
return !error 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 lateinit var kind: CallableMemberDescriptor.Kind
private val visibility = VisibilityCommonizer.equalizing() private val visibility = VisibilityCommonizer.equalizing()
private val typeParameters = TypeParameterListCommonizer(cache) private val typeParameters = TypeParameterListCommonizer(cache)
private val valueParameters = ValueParameterListCommonizer(cache) private val valueParameters = CallableValueParametersCommonizer(cache)
private var hasStableParameterNames = true
override fun commonizationResult() = CirClassConstructorFactory.create( override fun commonizationResult(): CirClassConstructor {
annotations = emptyList(), val valueParameters = valueParameters.result
typeParameters = typeParameters.result, valueParameters.patchCallables()
visibility = visibility.result,
containingClassDetails = CirContainingClassDetailsFactory.DOES_NOT_MATTER, return CirClassConstructorFactory.create(
valueParameters = valueParameters.result, annotations = emptyList(),
hasStableParameterNames = hasStableParameterNames, typeParameters = typeParameters.result,
isPrimary = isPrimary, visibility = visibility.result,
kind = kind containingClassDetails = CirContainingClassDetailsFactory.DOES_NOT_MATTER,
) valueParameters = valueParameters.valueParameters,
hasStableParameterNames = valueParameters.hasStableParameterNames,
isPrimary = isPrimary,
kind = kind
)
}
override fun initialize(first: CirClassConstructor) { override fun initialize(first: CirClassConstructor) {
isPrimary = first.isPrimary isPrimary = first.isPrimary
@@ -37,18 +41,12 @@ class ClassConstructorCommonizer(cache: CirClassifiersCache) : AbstractStandardC
} }
override fun doCommonizeWith(next: CirClassConstructor): Boolean { 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) && next.containingClassDetails.modality != Modality.SEALED // don't commonize constructors for sealed classes (not not their subclasses)
&& isPrimary == next.isPrimary && isPrimary == next.isPrimary
&& kind == next.kind && kind == next.kind
&& visibility.commonizeWith(next) && visibility.commonizeWith(next)
&& typeParameters.commonizeWith(next.typeParameters) && typeParameters.commonizeWith(next.typeParameters)
&& valueParameters.commonizeWith(next.valueParameters) && valueParameters.commonizeWith(next)
if (result) {
hasStableParameterNames = hasStableParameterNames && next.hasStableParameterNames
}
return result
} }
} }
@@ -12,34 +12,32 @@ import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCach
class FunctionCommonizer(cache: CirClassifiersCache) : AbstractFunctionOrPropertyCommonizer<CirFunction>(cache) { class FunctionCommonizer(cache: CirClassifiersCache) : AbstractFunctionOrPropertyCommonizer<CirFunction>(cache) {
private val annotations = AnnotationsCommonizer() private val annotations = AnnotationsCommonizer()
private val modifiers = FunctionModifiersCommonizer() private val modifiers = FunctionModifiersCommonizer()
private val valueParameters = ValueParameterListCommonizer(cache) private val valueParameters = CallableValueParametersCommonizer(cache)
private var hasStableParameterNames = true
override fun commonizationResult() = CirFunctionFactory.create( override fun commonizationResult(): CirFunction {
annotations = annotations.result, val valueParameters = valueParameters.result
name = name, valueParameters.patchCallables()
typeParameters = typeParameters.result,
visibility = visibility.result, return CirFunctionFactory.create(
modality = modality.result, annotations = annotations.result,
containingClassDetails = null, name = name,
valueParameters = valueParameters.result, typeParameters = typeParameters.result,
hasStableParameterNames = hasStableParameterNames, visibility = visibility.result,
extensionReceiver = extensionReceiver.result, modality = modality.result,
returnType = returnType.result, containingClassDetails = null,
kind = kind, valueParameters = valueParameters.valueParameters,
modifiers = modifiers.result hasStableParameterNames = valueParameters.hasStableParameterNames,
) extensionReceiver = extensionReceiver.result,
returnType = returnType.result,
kind = kind,
modifiers = modifiers.result
)
}
override fun doCommonizeWith(next: CirFunction): Boolean { override fun doCommonizeWith(next: CirFunction): Boolean {
val result = super.doCommonizeWith(next) return super.doCommonizeWith(next)
&& annotations.commonizeWith(next.annotations) && annotations.commonizeWith(next.annotations)
&& modifiers.commonizeWith(next.modifiers) && modifiers.commonizeWith(next.modifiers)
&& valueParameters.commonizeWith(next.valueParameters) && valueParameters.commonizeWith(next)
if (result) {
hasStableParameterNames = hasStableParameterNames && next.hasStableParameterNames
}
return result
} }
} }
@@ -39,7 +39,6 @@ class ValueParameterCommonizer(cache: CirClassifiersCache) : AbstractStandardCom
override fun doCommonizeWith(next: CirValueParameter): Boolean { override fun doCommonizeWith(next: CirValueParameter): Boolean {
val result = !next.declaresDefaultValue val result = !next.declaresDefaultValue
&& varargElementType.isNull() == next.varargElementType.isNull() && varargElementType.isNull() == next.varargElementType.isNull()
&& name == next.name
&& returnType.commonizeWith(next.returnType) && returnType.commonizeWith(next.returnType)
if (result) { if (result) {
@@ -49,4 +48,8 @@ class ValueParameterCommonizer(cache: CirClassifiersCache) : AbstractStandardCom
return result 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.cir.CirValueParameter
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCache import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCache
import org.jetbrains.kotlin.name.Name
class ValueParameterListCommonizer(cache: CirClassifiersCache) : AbstractListCommonizer<CirValueParameter, CirValueParameter>( class ValueParameterListCommonizer(cache: CirClassifiersCache) : AbstractListCommonizer<CirValueParameter, CirValueParameter>(
singleElementCommonizerFactory = { ValueParameterCommonizer(cache) } 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 */ /** Used for approximation of [SimpleFunctionDescriptor]s before running concrete [Commonizer]s */
data class FunctionApproximationKey( data class FunctionApproximationKey(
val name: Name, val name: Name,
val valueParameters: List<Pair<Name, CirTypeSignature>>, val valueParameters: List<CirTypeSignature>,
val extensionReceiverParameter: CirTypeSignature? val extensionReceiverParameter: CirTypeSignature?
) { ) {
constructor(function: SimpleFunctionDescriptor) : this( constructor(function: SimpleFunctionDescriptor) : this(
function.name.intern(), function.name.intern(),
function.valueParameters.map { it.name.intern() to it.type.signature }, function.valueParameters.map { it.type.signature },
function.extensionReceiverParameter?.type?.signature function.extensionReceiverParameter?.type?.signature
) )
} }
/** Used for approximation of [ConstructorDescriptor]s before running concrete [Commonizer]s */ /** Used for approximation of [ConstructorDescriptor]s before running concrete [Commonizer]s */
data class ConstructorApproximationKey( data class ConstructorApproximationKey(
val valueParameters: List<Pair<Name, CirTypeSignature>> val valueParameters: List<CirTypeSignature>
) { ) {
constructor(constructor: ConstructorDescriptor) : this( 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() 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 internal val FqName.isUnderStandardKotlinPackages: Boolean
get() = hasAnyPrefix(STANDARD_KOTLIN_PACKAGES) get() = hasAnyPrefix(STANDARD_KOTLIN_PACKAGES)
@@ -35,7 +42,7 @@ internal val FqName.isUnderKotlinNativeSyntheticPackages: Boolean
get() = hasAnyPrefix(KOTLIN_NATIVE_SYNTHETIC_PACKAGES) get() = hasAnyPrefix(KOTLIN_NATIVE_SYNTHETIC_PACKAGES)
internal val FqName.isUnderDarwinPackage: Boolean internal val FqName.isUnderDarwinPackage: Boolean
get() = asString().hasPrefix(DARWIN_PACKAGE_PREFIX) get() = asString().hasPrefix(DARWIN_PACKAGE)
private fun FqName.hasAnyPrefix(prefixes: List<String>): Boolean = private fun FqName.hasAnyPrefix(prefixes: List<String>): Boolean =
asString().let { fqName -> prefixes.any(fqName::hasPrefix) } asString().let { fqName -> prefixes.any(fqName::hasPrefix) }
@@ -48,3 +55,6 @@ private fun String.hasPrefix(prefix: String): Boolean {
else -> false else -> false
} }
} }
internal val ClassId.isObjCInteropCallableAnnotation: Boolean
get() = packageFqName.asString() == CINTEROP_PACKAGE && relativeClassName.asString() in OBJC_INTEROP_CALLABLE_ANNOTATIONS