From 156c341eebcd7d112524058f3162ae8eca1a3eae Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Mon, 9 Oct 2023 11:16:48 +0200 Subject: [PATCH] [K/N] Implement objc overridablity condition for IR ^KT-61511 --- .../fir/backend/jvm/JvmFir2IrExtensions.kt | 2 + .../kotlin/fir/backend/Fir2IrConverter.kt | 2 +- .../kotlin/fir/backend/Fir2IrExtensions.kt | 3 + .../native/FirNativeOverrideChecker.kt | 6 ++ .../IrObjCOverridablilityCondidtion.kt | 77 +++++++++++++++++++ .../kotlin/ir/objcinterop/ObjCInterop.kt | 12 ++- .../ObjCOverridabilityCondition.kt | 4 + .../jetbrains/kotlin/backend/konan/Fir2Ir.kt | 17 +++- .../kotlin/backend/konan/KonanDriver.kt | 1 + .../jetbrains/kotlin/backend/konan/PsiToIr.kt | 4 +- .../konan/serialization/KonanIrLinker.kt | 7 +- .../backend.native/tests/build.gradle | 11 +++ .../objc/overridabilityCondition/lib.def | 2 + .../objc/overridabilityCondition/lib.h | 10 +++ .../objc/overridabilityCondition/lib.m | 18 +++++ .../objc/overridabilityCondition/main.kt | 54 +++++++++++++ 16 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/IrObjCOverridablilityCondidtion.kt create mode 100644 kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.def create mode 100644 kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.h create mode 100644 kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.m create mode 100644 kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/main.kt diff --git a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt index fd69569ef3a..aad17cf5371 100644 --- a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt +++ b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.overrides.IrExternalOverridabilityCondition import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JvmAnnotationNames @@ -32,6 +33,7 @@ class JvmFir2IrExtensions( private val irDeserializer: JvmIrDeserializer, private val mangler: KotlinMangler.IrMangler, ) : Fir2IrExtensions, JvmGeneratorExtensions { + override val externalOverridabilityConditions: List = emptyList() // TODO: KT-61370, KT-61804 override val classNameOverride: MutableMap = mutableMapOf() override val cachedFields = CachedFieldsForObjectInstances(IrFactoryImpl, configuration.languageVersionSettings) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt index 1c8c605aa6d..ecf5dd3563c 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt @@ -561,7 +561,7 @@ class Fir2IrConverter( IrFakeOverrideBuilder( typeContextProvider(irBuiltins), Fir2IrFakeOverrideStrategy(friendModulesMap(session), commonMemberStorage.symbolTable, irMangler), - externalOverridabilityConditions = emptyList(), // TODO: KT-61370, KT-61804. + fir2IrExtensions.externalOverridabilityConditions ) }, moduleDescriptor, commonMemberStorage, irMangler, specialSymbolProvider, initializedIrBuiltIns diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt index 4d0fcceaf34..9b23060d426 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt @@ -8,10 +8,12 @@ package org.jetbrains.kotlin.fir.backend import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrMemberWithContainerSource +import org.jetbrains.kotlin.ir.overrides.IrExternalOverridabilityCondition import org.jetbrains.kotlin.ir.util.SymbolTable interface Fir2IrExtensions { val irNeedsDeserialization: Boolean + val externalOverridabilityConditions: List fun generateOrGetFacadeClass(declaration: IrMemberWithContainerSource, components: Fir2IrComponents): IrClass? fun deserializeToplevelClass(irClass: IrClass, components: Fir2IrComponents): Boolean @@ -22,6 +24,7 @@ interface Fir2IrExtensions { override val irNeedsDeserialization: Boolean get() = false + override val externalOverridabilityConditions: List = emptyList() override fun generateOrGetFacadeClass(declaration: IrMemberWithContainerSource, components: Fir2IrComponents): IrClass? = null override fun deserializeToplevelClass(irClass: IrClass, components: Fir2IrComponents): Boolean = false override fun registerDeclarations(symbolTable: SymbolTable) {} diff --git a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt index d9323ecccce..df772265198 100644 --- a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt +++ b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/FirNativeOverrideChecker.kt @@ -14,6 +14,12 @@ import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.scopes.FirOverrideChecker import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker +/** + * The same code, as in this class also exists in [org.jetbrains.kotlin.ir.objcinterop.ObjCOverridabilityCondition] + * and in [org.jetbrains.kotlin.ir.objcinterop.IrObjCOverridabilityCondition]. + * + * When modifying, all three copies should be synchronized. + */ class FirNativeOverrideChecker(private val session: FirSession) : FirOverrideChecker { private val standardOverrideChecker = FirStandardOverrideChecker(session) diff --git a/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/IrObjCOverridablilityCondidtion.kt b/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/IrObjCOverridablilityCondidtion.kt new file mode 100644 index 00000000000..305afe342db --- /dev/null +++ b/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/IrObjCOverridablilityCondidtion.kt @@ -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 + } +} \ No newline at end of file diff --git a/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/ObjCInterop.kt b/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/ObjCInterop.kt index b50d21eed8e..a753103fa5e 100644 --- a/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/ObjCInterop.kt +++ b/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/ObjCInterop.kt @@ -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 } diff --git a/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/ObjCOverridabilityCondition.kt b/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/ObjCOverridabilityCondition.kt index bb044200a5a..4a132520cc7 100644 --- a/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/ObjCOverridabilityCondition.kt +++ b/compiler/ir/ir.objcinterop/src/org/jetbrains/kotlin/ir/objcinterop/ObjCOverridabilityCondition.kt @@ -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 { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt index d98b5ade4d8..7325b1dd1b2 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt @@ -26,11 +26,15 @@ import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.backend.native.FirNativeKotlinMangler import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor import org.jetbrains.kotlin.fir.pipeline.convertToIrAndActualize +import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.fir.signaturer.Ir2FirManglerAdapter import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment +import org.jetbrains.kotlin.ir.declarations.IrMemberWithContainerSource +import org.jetbrains.kotlin.ir.objcinterop.IrObjCOverridabilityCondition import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.acceptVoid @@ -40,11 +44,18 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager internal val KlibFactories = KlibMetadataFactories(::KonanBuiltIns, DynamicTypeDeserializer) +internal object NativeFir2IrExtensions : Fir2IrExtensions { + override val irNeedsDeserialization = false + override val externalOverridabilityConditions = listOf(IrObjCOverridabilityCondition) + override fun generateOrGetFacadeClass(declaration: IrMemberWithContainerSource, components: Fir2IrComponents) = null + override fun deserializeToplevelClass(irClass: IrClass, components: Fir2IrComponents) = false + override fun registerDeclarations(symbolTable: SymbolTable) {} + override fun findInjectedValue(calleeReference: FirReference, conversionScope: Fir2IrConversionScope) = null +} + internal fun PhaseContext.fir2Ir( input: FirOutput.Full, ): Fir2IrOutput { - val fir2IrExtensions = Fir2IrExtensions.Default - var builtInsModule: KotlinBuiltIns? = null val resolvedLibraries = config.resolvedLibraries.getFullResolvedList() @@ -85,7 +96,7 @@ internal fun PhaseContext.fir2Ir( useIrFakeOverrideBuilder = configuration.getBoolean(CommonConfigurationKeys.USE_IR_FAKE_OVERRIDE_BUILDER), ) val (irModuleFragment, components, pluginContext, irActualizedResult) = input.firResult.convertToIrAndActualize( - fir2IrExtensions, + NativeFir2IrExtensions, fir2IrConfiguration, IrGenerationExtension.getInstances(config.project), signatureComposer = DescriptorSignatureComposerStub(KonanManglerDesc), diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt index 430fc0dc9f3..0c756c33f6d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt @@ -172,6 +172,7 @@ class KonanDriver( copy(BinaryOptions.objcExportDisableSwiftMemberNameMangling) copy(BinaryOptions.objcExportIgnoreInterfaceMethodCollisions) copy(KonanConfigKeys.OBJC_GENERICS) + copy(CommonConfigurationKeys.USE_IR_FAKE_OVERRIDE_BUILDER) } // For the second stage, remove already compiled source files from the configuration. diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt index 23fe1e75122..1d529d3c4e2 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.builders.TranslationPluginContext import org.jetbrains.kotlin.ir.declarations.DescriptorMetadataSource import org.jetbrains.kotlin.ir.linkage.IrDeserializer import org.jetbrains.kotlin.ir.linkage.partial.partialLinkageConfig +import org.jetbrains.kotlin.ir.objcinterop.IrObjCOverridabilityCondition import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.acceptVoid @@ -154,7 +155,8 @@ internal fun PsiToIrContext.psiToIr( cachedLibraries = config.cachedLibraries, lazyIrForCaches = config.lazyIrForCaches, libraryBeingCached = config.libraryToCache, - userVisibleIrModulesSupport = config.userVisibleIrModulesSupport + userVisibleIrModulesSupport = config.userVisibleIrModulesSupport, + externalOverridabilityConditions = listOf(IrObjCOverridabilityCondition) ).also { linker -> // context.config.librariesWithDependencies could change at each iteration. diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrLinker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrLinker.kt index 771af9a8bd3..e1726eccb13 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrLinker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrLinker.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.builders.TranslationPluginContext import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass +import org.jetbrains.kotlin.ir.overrides.IrExternalOverridabilityCondition import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrPublicSymbolBase import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl @@ -306,7 +307,8 @@ internal class KonanIrLinker( private val cachedLibraries: CachedLibraries, private val lazyIrForCaches: Boolean, private val libraryBeingCached: PartialCacheInfo?, - override val userVisibleIrModulesSupport: UserVisibleIrModulesSupport + override val userVisibleIrModulesSupport: UserVisibleIrModulesSupport, + externalOverridabilityConditions: List, ) : KotlinIrLinker(currentModule, messageLogger, builtIns, symbolTable, exportedDependencies) { override fun isBuiltInModule(moduleDescriptor: ModuleDescriptor): Boolean = moduleDescriptor.isNativeStdlib() @@ -321,7 +323,8 @@ internal class KonanIrLinker( typeSystem = IrTypeSystemContextImpl(builtIns), friendModules = friendModules, partialLinkageSupport = partialLinkageSupport, - platformSpecificClassFilter = KonanFakeOverrideClassFilter + platformSpecificClassFilter = KonanFakeOverrideClassFilter, + externalOverridabilityConditions = externalOverridabilityConditions, ) val moduleDeserializers = mutableMapOf() diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 44fb458f0e9..13cdf64b18d 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -4101,6 +4101,11 @@ if (PlatformInfo.isAppleTarget(project)) { createInterop("kt59167") { it.defFile 'interop/kt59167/kt59167.def' } + createInterop("overridabilityCondition") { + it.defFile 'interop/objc/overridabilityCondition/lib.def' + it.headers "$projectDir/interop/objc/overridabilityCondition/lib.h" + it.extraOpts "-Xcompile-source", "$projectDir/interop/objc/overridabilityCondition/lib.m" + } } createInterop("withSpaces") { @@ -4673,6 +4678,12 @@ if (PlatformInfo.isAppleTarget(project)) { interop = "objcDirect" } + interopTest("interop_objc_overridabilityCondition") { + interop = "overridabilityCondition" + source = "interop/objc/overridabilityCondition/main.kt" + flags = [ "-Xuse-ir-fake-override-builder" ] + } + interopTest("interop_objc_foreignException") { source = "interop/objc/foreignException/objc_wrap.kt" interop = 'foreignException' diff --git a/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.def b/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.def new file mode 100644 index 00000000000..e5459088575 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.def @@ -0,0 +1,2 @@ +language = Objective-C +headersFilter = **/lib.h \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.h b/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.h new file mode 100644 index 00000000000..357e3f52ac1 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.h @@ -0,0 +1,10 @@ +#import +#import +#import + +@interface ObjCClass : NSObject +- (NSString*)fooWithArg:(int)arg arg2:(NSString*)arg2; +- (NSString*)fooWithArg:(int)ohNoOtherName name2:(NSString*)name2; +- (NSString*)fooWithArg:(int)arg name3:(NSString*)name3; +@end + diff --git a/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.m b/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.m new file mode 100644 index 00000000000..53bd80a924a --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/lib.m @@ -0,0 +1,18 @@ +#import "lib.h" + +@implementation ObjCClass { +} + +- (NSString*)fooWithArg:(int)arg arg2:(NSString*)arg2 { + return @"A"; +} + +- (NSString*)fooWithArg:(int)ohNoOtherName name2:(NSString*)name2 { + return @"B"; +} + +- (NSString*)fooWithArg:(int)arg name3:(NSString*)name3 { + return @"C"; +} + +@end \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/main.kt b/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/main.kt new file mode 100644 index 00000000000..6edb7723b4b --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/objc/overridabilityCondition/main.kt @@ -0,0 +1,54 @@ +@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) + +import lib.ObjCClass + +@Suppress("CONFLICTING_OVERLOADS") +class OverrideAll : ObjCClass() { + override fun fooWithArg(arg: Int, arg2: String?) = "D" + override fun fooWithArg(ohNoOtherName: Int, name2: String?) = "E" + override fun fooWithArg(arg: Int, name3: String?) = "F" +} + +@Suppress("CONFLICTING_OVERLOADS") +class OverrideNone : ObjCClass() { +} + +@Suppress("CONFLICTING_OVERLOADS") +class OverrideOne : ObjCClass() { + override fun fooWithArg(arg: Int, arg2: String?) = "G" +} + +@Suppress("CONFLICTING_OVERLOADS") +class OverrideWithDifferentFirstArgName : ObjCClass() { + override fun fooWithArg(a: Int, arg2: String?) = "H" + override fun fooWithArg(b: Int, name2: String?) = "I" + override fun fooWithArg(c: Int, name3: String?) = "J" +} + +fun test(x: ObjCClass, expected: String) { + val res = x.fooWithArg(arg = 0, arg2 = "") + + x.fooWithArg(ohNoOtherName = 0, name2="") + + x.fooWithArg(arg = 0, name3 = "") + if (res != expected) throw IllegalStateException("Fail ${x::class}: ${res} instead of $expected") +} + +fun main() { + test(ObjCClass(), "ABC") + test(OverrideAll(), "DEF") + test(OverrideNone(), "ABC") + test(OverrideOne(), "GBC") + test(OverrideWithDifferentFirstArgName(), "HIJ") + +// Also test non-virtual calls + val x1 = OverrideAll() + val res1 = x1.fooWithArg(arg = 0, arg2 = "") + + x1.fooWithArg(ohNoOtherName = 0, name2="") + + x1.fooWithArg(arg = 0, name3 = "") + if (res1 != "DEF") throw IllegalStateException("Fail OverrideAll non-virtual: ${res1} instead of DEF") + + val x2 = OverrideNone() + val res2 = x2.fooWithArg(arg = 0, arg2 = "") + + x2.fooWithArg(ohNoOtherName = 0, name2="") + + x2.fooWithArg(arg = 0, name3 = "") + if (res2 != "ABC") throw IllegalStateException("Fail OverrideNone non-virtual: ${res2} instead of ABC") +} \ No newline at end of file