[K/N] Implement objc overridablity condition for IR

^KT-61511
This commit is contained in:
Pavel Kunyavskiy
2023-10-09 11:16:48 +02:00
committed by Space Team
parent 202f30f910
commit 156c341eeb
16 changed files with 219 additions and 11 deletions
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2023 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.ir.objcinterop
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrOverridableMember
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.overrides.IrExternalOverridabilityCondition
/**
* Describes method overriding rules for Objective-C methods.
*
* The same code, as in this class also exists in [org.jetbrains.kotlin.fir.backend.native.FirNativeOverrideChecker]
* and in [org.jetbrains.kotlin.ir.objcinterop.ObjCOverridabilityCondition].
*
* When modifying, all three copies should be synchronized.
*
*/
object IrObjCOverridabilityCondition : IrExternalOverridabilityCondition {
override val contract = IrExternalOverridabilityCondition.Contract.BOTH
override fun isOverridable(
superMember: IrOverridableMember,
subMember: IrOverridableMember
): IrExternalOverridabilityCondition.Result {
if (superMember.name == subMember.name) { // Slow path:
if (superMember is IrFunction && subMember is IrFunction) {
superMember.getExternalObjCMethodInfo()?.let { superInfo ->
val subInfo = subMember.getExternalObjCMethodInfo()
if (subInfo != null) {
// Overriding Objective-C method by Objective-C method in interop stubs.
// Don't even check method signatures:
return if (superInfo.selector == subInfo.selector) {
IrExternalOverridabilityCondition.Result.OVERRIDABLE
} else {
IrExternalOverridabilityCondition.Result.INCOMPATIBLE
}
} else {
// Overriding Objective-C method by Kotlin method.
if (!parameterNamesMatch(superMember, subMember)) {
return IrExternalOverridabilityCondition.Result.INCOMPATIBLE
}
}
}
} else if (superMember.isExternalObjCClassProperty() && subMember.isExternalObjCClassProperty()) {
return IrExternalOverridabilityCondition.Result.OVERRIDABLE
}
}
return IrExternalOverridabilityCondition.Result.UNKNOWN
}
private fun IrOverridableMember.isExternalObjCClassProperty() = this is IrProperty &&
(this.parent as? IrClass)?.isExternalObjCClass() == true
private fun parameterNamesMatch(first: IrFunction, second: IrFunction): Boolean {
// The original Objective-C method selector is represented as
// function name and parameter names (except first).
if (first.valueParameters.size != second.valueParameters.size) {
return false
}
first.valueParameters.forEachIndexed { index, parameter ->
if (index > 0 && parameter.name != second.valueParameters[index].name) {
return false
}
}
return true
}
}
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.ir.objcinterop
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
@@ -18,7 +17,6 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NativeForwardDeclarationKind
import org.jetbrains.kotlin.name.NativeStandardInteropNames
import org.jetbrains.kotlin.native.interop.ObjCMethodInfo
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.annotations.getAnnotationStringValue
import org.jetbrains.kotlin.resolve.annotations.getArgumentValueOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -27,7 +25,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlin.utils.atMostOne
private fun IrFunction.isFakeOverrideInProgressOfBuilding() = this is IrFunctionWithLateBinding && !isBound && isFakeOverride
internal val interopPackageName = NativeStandardInteropNames.cInteropPackage
internal val objCObjectFqName = NativeStandardInteropNames.objCObjectClassId.asSingleFqName()
@@ -118,7 +117,7 @@ private fun FunctionDescriptor.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
}
private fun IrFunction.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
assert (this.isReal)
require(this.isReal || this.isFakeOverrideInProgressOfBuilding())
val methodInfo = this.annotations.findAnnotation(objCMethodFqName)?.let {
ObjCMethodInfo(
@@ -158,6 +157,11 @@ private fun FunctionDescriptor.getObjCMethodInfo(onlyExternal: Boolean): ObjCMet
* @param onlyExternal indicates whether to accept overriding methods from Kotlin classes
*/
private fun IrSimpleFunction.getObjCMethodInfo(onlyExternal: Boolean): ObjCMethodInfo? {
// During fake override building we need this method, but overriddenSymbols are not valid yet.
// So let's pretend this override is real. It has annotation copied, if it exists
if (this.isFakeOverrideInProgressOfBuilding()) {
decodeObjCMethodAnnotation()?.let { return it }
}
if (this.isReal) {
this.decodeObjCMethodAnnotation()?.let { return it }
@@ -17,6 +17,10 @@ import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
* This class is applied at [org.jetbrains.kotlin.resolve.OverridingUtil] as configured with
* `META-INF/services/org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition` resource.
*
* The same code, as in this class also exists in [org.jetbrains.kotlin.fir.backend.native.FirNativeOverrideChecker]
* and in [org.jetbrains.kotlin.ir.objcinterop.IrObjCOverridabilityCondition].
*
* When modifying, all three copies should be synchronized.
*
*/
class ObjCOverridabilityCondition : ExternalOverridabilityCondition {