diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/CirDeclaration.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/CirDeclaration.kt index ac9899ed592..3e0190b3f6b 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/CirDeclaration.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/CirDeclaration.kt @@ -52,8 +52,8 @@ interface CirHasTypeParameters { } interface CirCallableMemberWithParameters { - val valueParameters: List - val hasStableParameterNames: Boolean + var valueParameters: List + var hasStableParameterNames: Boolean } /** diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirClassConstructorImpl.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirClassConstructorImpl.kt index deaa3db9944..73f1a5051f6 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirClassConstructorImpl.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirClassConstructorImpl.kt @@ -14,8 +14,8 @@ data class CirClassConstructorImpl( override val typeParameters: List, override val visibility: Visibility, override val containingClassDetails: CirContainingClassDetails, - override val valueParameters: List, - override val hasStableParameterNames: Boolean, + override var valueParameters: List, + override var hasStableParameterNames: Boolean, override val isPrimary: Boolean, override val kind: CallableMemberDescriptor.Kind ) : CirClassConstructor diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirFunctionImpl.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirFunctionImpl.kt index 92d35c7d22c..c299920f6c9 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirFunctionImpl.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/impl/CirFunctionImpl.kt @@ -18,8 +18,8 @@ data class CirFunctionImpl( override val visibility: Visibility, override val modality: Modality, override val containingClassDetails: CirContainingClassDetails?, - override val valueParameters: List, - override val hasStableParameterNames: Boolean, + override var valueParameters: List, + override var hasStableParameterNames: Boolean, override val extensionReceiver: CirExtensionReceiver?, override val returnType: CirType, override val kind: CallableMemberDescriptor.Kind, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractListCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractListCommonizer.kt index 63bfaa2da16..d8d96e4bc32 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractListCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractListCommonizer.kt @@ -51,4 +51,9 @@ abstract class AbstractListCommonizer( return !error } + + protected fun forEachSingleElementCommonizer(action: (index: Int, Commonizer) -> Unit) { + val commonizers = commonizers ?: failInEmptyState() + commonizers.forEachIndexed(action) + } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/CallableValueParametersCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/CallableValueParametersCommonizer.kt new file mode 100644 index 00000000000..0980a8038a3 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/CallableValueParametersCommonizer.kt @@ -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 { + class Result( + val hasStableParameterNames: Boolean, + val valueParameters: List, + 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.patchCallables(generated: Boolean, newNames: List): () -> 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) : ValueParameterNames() + + class MultipleReal(valueParameters: List) : ValueParameterNames() { + val generatedNames: List = 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): List = + 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 = 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 { 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") + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ClassConstructorCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ClassConstructorCommonizer.kt index 0aaded2d314..5d2ec24ff59 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ClassConstructorCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ClassConstructorCommonizer.kt @@ -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) } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/FunctionCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/FunctionCommonizer.kt index 41f38c14a7a..d58532ba8b6 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/FunctionCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/FunctionCommonizer.kt @@ -12,34 +12,32 @@ import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCach class FunctionCommonizer(cache: CirClassifiersCache) : AbstractFunctionOrPropertyCommonizer(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) } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ValueParameterCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ValueParameterCommonizer.kt index dec0dfc1053..4306b80f27c 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ValueParameterCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ValueParameterCommonizer.kt @@ -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 + } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ValueParameterListCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ValueParameterListCommonizer.kt index 635ddad9f44..ee093d06750 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ValueParameterListCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ValueParameterListCommonizer.kt @@ -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( singleElementCommonizerFactory = { ValueParameterCommonizer(cache) } -) +) { + fun overwriteNames(names: List) { + forEachSingleElementCommonizer { index, singleElementCommonizer -> + (singleElementCommonizer as ValueParameterCommonizer).overwriteName(names[index]) + } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/approximationKeys.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/approximationKeys.kt index 85d8b07d913..53ad4f7a998 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/approximationKeys.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/approximationKeys.kt @@ -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>, + val valueParameters: List, 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> + val valueParameters: List ) { constructor(constructor: ConstructorDescriptor) : this( - constructor.valueParameters.map { it.name.intern() to it.type.signature } + constructor.valueParameters.map { it.type.signature } ) } \ No newline at end of file diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt index 13bfba4a171..fbfce5b9216 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt @@ -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): 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