From b13836bb6b0828956b56e3d72c6667a23295a97e Mon Sep 17 00:00:00 2001 From: "eugene.levenetc" Date: Thu, 17 Oct 2024 14:11:04 +0200 Subject: [PATCH] [ObjCExport] Fix class type extension translation KT-72350 --- .../objcexport/getObjCPropertyGetter.kt | 28 ++++++++++++++++ .../objcexport/getObjCPropertySetter.kt | 29 ++++++++++++++++ .../objcexport/translateToObjCProperty.kt | 33 ++++++------------- .../objcexport/valueParametersAssociated.kt | 11 +------ .../tests/ObjCExportHeaderGeneratorTest.kt | 5 +++ .../!classTypePropertyTranslation.h | 32 ++++++++++++++++++ .../classTypePropertyTranslation/Foo.kt | 3 ++ 7 files changed, 108 insertions(+), 33 deletions(-) create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCPropertyGetter.kt create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCPropertySetter.kt create mode 100644 native/objcexport-header-generator/testData/headers/classTypePropertyTranslation/!classTypePropertyTranslation.h create mode 100644 native/objcexport-header-generator/testData/headers/classTypePropertyTranslation/Foo.kt diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCPropertyGetter.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCPropertyGetter.kt new file mode 100644 index 0000000000000..661c40db8fc49 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCPropertyGetter.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2024 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.objcexport + +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertySymbol +import org.jetbrains.kotlin.objcexport.analysisApiUtils.getFunctionMethodBridge + +/** + * Getter needs to be defined only for properties with [reservedPropertyNames] + * ```c + * @interface Foo + * @property (getter=bool, setter=setBool) BOOL bool_ + * @end + * ``` + */ +internal fun ObjCExportContext.getObjCPropertyGetter(symbol: KaPropertySymbol, objCName: String): String? { + + if (!symbol.hasReservedName) return null + + val symbolGetter = symbol.getter + val getterBridge = if (symbolGetter == null) error("KtPropertySymbol.getter is undefined") else getFunctionMethodBridge(symbolGetter) + val getterSelector = getSelector(symbolGetter, getterBridge) + + return if (getterSelector != objCName && getterSelector.isNotBlank()) getterSelector else null +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCPropertySetter.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCPropertySetter.kt new file mode 100644 index 0000000000000..5edabd075897e --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCPropertySetter.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2024 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.objcexport + +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertySymbol +import org.jetbrains.kotlin.objcexport.analysisApiUtils.getFunctionMethodBridge + +/** + * Setter needs to be defined only for properties with [reservedPropertyNames] + * ```c + * @interface Foo + * @property (getter=bool, setter=setBool) BOOL bool_ + * @end + * ``` + */ +internal fun ObjCExportContext.getObjCPropertySetter(symbol: KaPropertySymbol, objCName: String): String? { + + if (!symbol.hasReservedName) return null + + val setterName = symbol.setter?.let { + val setterSelector = getSelector(it, getFunctionMethodBridge(it)) + if (setterSelector == objCName.asSetterSelector) null else setterSelector + } + + return if (setterName.isNullOrBlank()) null else setterName +} \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCProperty.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCProperty.kt index b25e4d2063931..06af1be9f4f90 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCProperty.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCProperty.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.objcexport import org.jetbrains.kotlin.analysis.api.symbols.KaPropertySymbol -import org.jetbrains.kotlin.backend.konan.objcexport.ObjCIdType import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProperty import org.jetbrains.kotlin.backend.konan.objcexport.isInstance import org.jetbrains.kotlin.backend.konan.objcexport.swiftNameAttribute @@ -30,25 +29,10 @@ fun ObjCExportContext.buildProperty(symbol: KaPropertySymbol): ObjCProperty { val getterBridge = if (symbolGetter == null) error("KtPropertySymbol.getter is undefined") else getFunctionMethodBridge(symbolGetter) val type = mapReturnType(symbolGetter, getterBridge.returnBridge) val attributes = mutableListOf() - val setterName: String? + val declarationAttributes = mutableListOf(symbol.getSwiftPrivateAttribute() ?: swiftNameAttribute(propertyName.swiftName)) if (!analysisSession.getBridgeReceiverType(symbol).isInstance) attributes += "class" - - val propertySetter = symbol.setter - // Note: the condition below is similar to "toObjCMethods" logic in [ObjCExportedInterface.createCodeSpec]. - val shouldBeSetterExposed = true //TODO: mapper.shouldBeExposed - - if (propertySetter != null && shouldBeSetterExposed) { - val setterSelector = getSelector(propertySetter, getFunctionMethodBridge(propertySetter)) - setterName = if (setterSelector == name.asSetterSelector) null else setterSelector - } else { - attributes += "readonly" - setterName = null - } - - val getterSelector = getSelector(symbolGetter, getterBridge) - val getterName: String? = if (getterSelector != name && getterSelector.isNotBlank() == true) getterSelector else null - val declarationAttributes = mutableListOf(symbol.getSwiftPrivateAttribute() ?: swiftNameAttribute(propertyName.swiftName)) + if (symbol.setter == null) attributes += "readonly" declarationAttributes.addIfNotNull(analysisSession.getObjCDeprecationStatus(symbol)) @@ -56,13 +40,16 @@ fun ObjCExportContext.buildProperty(symbol: KaPropertySymbol): ObjCProperty { name = name, comment = analysisSession.translateToObjCComment(symbol.annotations), origin = analysisSession.getObjCExportStubOrigin(symbol), - type = type ?: ObjCIdType, //[ObjCIdType] temp fix, should be translated properly, see KT-65709 + type = type, propertyAttributes = attributes, - setterName = if (setterName.isNullOrBlank()) null else setterName, - getterName = getterName, + setterName = getObjCPropertySetter(symbol, name), + getterName = getObjCPropertyGetter(symbol, name), declarationAttributes = declarationAttributes ) } -private val String.asSetterSelector: String - get() = "set" + replaceFirstChar(Char::uppercase) + ":" \ No newline at end of file +internal val String.asSetterSelector: String + get() = "set" + replaceFirstChar(Char::uppercase) + ":" + +internal val KaPropertySymbol.hasReservedName: Boolean + get() = name.asString().isReservedPropertyName \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/valueParametersAssociated.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/valueParametersAssociated.kt index 429153ebdd87a..748aa3e45ca94 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/valueParametersAssociated.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/valueParametersAssociated.kt @@ -150,15 +150,6 @@ internal fun KaSession.getObjCReceiverType(symbol: KaFunctionSymbol?): KaType? { else if (getClassIfCategory(symbol) == null) receiverType else null } else if (symbol is KaPropertyGetterSymbol || symbol is KaPropertySetterSymbol) { val property = symbol.containingDeclaration as KaPropertySymbol - val isExtension = property.isExtension - val receiverType = property.receiverType - if (isExtension) { - if (getClassIfCategory(receiverType) == null) { - receiverType - } else { - null - } - } else null - + if (property.isExtension) property.receiverType else null } else null } diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt index d1e0de54f78bf..1e3fb4c898a76 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportHeaderGeneratorTest.kt @@ -584,6 +584,11 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) { doTest(headersTestDataDir.resolve("mangleGenerics")) } + @Test + fun `test - class type property translation`() { + doTest(headersTestDataDir.resolve("classTypePropertyTranslation")) + } + private fun doTest(root: File, configuration: Configuration = Configuration()) { if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory") val generatedHeaders = generator.generateHeaders(root, configuration).toString() diff --git a/native/objcexport-header-generator/testData/headers/classTypePropertyTranslation/!classTypePropertyTranslation.h b/native/objcexport-header-generator/testData/headers/classTypePropertyTranslation/!classTypePropertyTranslation.h new file mode 100644 index 0000000000000..3afedb41e60dd --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classTypePropertyTranslation/!classTypePropertyTranslation.h @@ -0,0 +1,32 @@ +#import +#import +#import +#import +#import +#import +#import + +@class Foo; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +__attribute__((objc_subclassing_restricted)) +@interface Foo : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)extensionProperty:(Foo *)receiver __attribute__((swift_name("extensionProperty(_:)"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END diff --git a/native/objcexport-header-generator/testData/headers/classTypePropertyTranslation/Foo.kt b/native/objcexport-header-generator/testData/headers/classTypePropertyTranslation/Foo.kt new file mode 100644 index 0000000000000..cb461d30df448 --- /dev/null +++ b/native/objcexport-header-generator/testData/headers/classTypePropertyTranslation/Foo.kt @@ -0,0 +1,3 @@ +class Foo { + val Foo.extensionProperty: Int get() = 42 +} \ No newline at end of file