Skip to content

Commit

Permalink
[ObjCExport] Fix class type extension translation
Browse files Browse the repository at this point in the history
KT-72350
  • Loading branch information
elevenetc committed Oct 22, 2024
1 parent 115e027 commit b13836b
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,39 +29,27 @@ 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<String>()
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))

return 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) + ":"
internal val String.asSetterSelector: String
get() = "set" + replaceFirstChar(Char::uppercase) + ":"

internal val KaPropertySymbol.hasReservedName: Boolean
get() = name.asString().isReservedPropertyName
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>

@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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Foo {
val Foo.extensionProperty: Int get() = 42
}

0 comments on commit b13836b

Please sign in to comment.