[Native] Add HiddenFromObjC and ShouldRefineInSwift annotations

This commit is contained in:
Rick Clephas
2022-08-19 16:44:05 +02:00
committed by SvyatoslavScherbina
parent a65cb947d6
commit 6ae517a2df
14 changed files with 399 additions and 3 deletions
@@ -38,5 +38,8 @@ object KonanFqNames {
val eagerInitialization = FqName("kotlin.native.EagerInitialization")
val noReorderFields = FqName("kotlin.native.internal.NoReorderFields")
val objCName = FqName("kotlin.native.ObjCName")
val hidesFromObjC = FqName("kotlin.native.HidesFromObjC")
val refinesInSwift = FqName("kotlin.native.RefinesInSwift")
val shouldRefineInSwift = FqName("kotlin.native.ShouldRefineInSwift")
val reflectionPackageName = FqName("kotlin.native.internal.ReflectionPackageName")
}
@@ -613,7 +613,7 @@ internal class ObjCExportTranslatorImpl(
val getterSelector = getSelector(baseProperty.getter!!)
val getterName: String? = if (getterSelector != name) getterSelector else null
val declarationAttributes = mutableListOf(swiftNameAttribute(propertyName.swiftName))
val declarationAttributes = mutableListOf(property.getSwiftPrivateAttribute() ?: swiftNameAttribute(propertyName.swiftName))
declarationAttributes.addIfNotNull(mapper.getDeprecation(property)?.toDeprecationAttribute())
val visibilityComments = visibilityComments(property.visibility, "property")
@@ -701,7 +701,7 @@ internal class ObjCExportTranslatorImpl(
val swiftName = namer.getSwiftName(baseMethod)
val attributes = mutableListOf<String>()
attributes += swiftNameAttribute(swiftName)
attributes += method.getSwiftPrivateAttribute() ?: swiftNameAttribute(swiftName)
if (baseMethodBridge.returnBridge is MethodBridge.ReturnValue.WithError.ZeroForError
&& baseMethodBridge.returnBridge.successMayBeZero) {
@@ -806,7 +806,7 @@ internal class ObjCExportTranslatorImpl(
}
}
private val mustBeDocumentedAnnotationsStopList = setOf(StandardNames.FqNames.deprecated, KonanFqNames.objCName)
private val mustBeDocumentedAnnotationsStopList = setOf(StandardNames.FqNames.deprecated, KonanFqNames.objCName, KonanFqNames.shouldRefineInSwift)
private fun mustBeDocumentedAnnotations(annotations: Annotations): List<String> {
return annotations.mapNotNull { it ->
it.annotationClass?.let { annotationClass ->
@@ -1431,6 +1431,17 @@ private fun DeprecationInfo.toDeprecationAttribute(): String {
private fun renderDeprecationAttribute(attribute: String, message: String) = "$attribute(${quoteAsCStringLiteral(message)})"
private fun CallableMemberDescriptor.isRefinedInSwift(): Boolean = when {
// Note: the front-end checker requires all overridden descriptors to be either refined or not refined.
overriddenDescriptors.isNotEmpty() -> overriddenDescriptors.first().isRefinedInSwift()
else -> annotations.any { annotation ->
annotation.annotationClass?.annotations?.any { it.fqName == KonanFqNames.refinesInSwift } == true
}
}
private fun CallableMemberDescriptor.getSwiftPrivateAttribute(): String? =
if (isRefinedInSwift()) "swift_private" else null
private fun quoteAsCStringLiteral(str: String): String = buildString {
append('"')
for (c in str) {
@@ -97,9 +97,18 @@ internal fun ObjCExportMapper.shouldBeExposed(descriptor: CallableMemberDescript
// KT-42641. Don't expose componentN methods of data classes
// because they are useless in Objective-C/Swift.
isComponentNMethod(descriptor) && descriptor.overriddenDescriptors.isEmpty() -> false
descriptor.isHiddenFromObjC() -> false
else -> true
}
private fun CallableMemberDescriptor.isHiddenFromObjC(): Boolean = when {
// Note: the front-end checker requires all overridden descriptors to be either refined or not refined.
overriddenDescriptors.isNotEmpty() -> overriddenDescriptors.first().isHiddenFromObjC()
else -> annotations.any { annotation ->
annotation.annotationClass?.annotations?.any { it.fqName == KonanFqNames.hidesFromObjC } == true
}
}
internal fun ObjCExportMapper.shouldBeExposed(descriptor: ClassDescriptor): Boolean =
shouldBeVisible(descriptor) && !isSpecialMapped(descriptor) && !descriptor.defaultType.isObjCObjectType()
@@ -1418,6 +1418,40 @@ __attribute__((swift_name("OverrideMethodsOfAnyKt")))
+ (BOOL)testObj:(id)obj other:(id)other swift:(BOOL)swift error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test(obj:other:swift:)")));
@end
__attribute__((swift_name("RefinedClassA")))
@interface KtRefinedClassA : KtBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)fooRefined __attribute__((swift_private));
@end
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("RefinedClassB")))
@interface KtRefinedClassB : KtRefinedClassA
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)fooRefined __attribute__((swift_private));
@end
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("RefinedKt")))
@interface KtRefinedKt : KtBase
+ (NSString *)fooRefined __attribute__((swift_private));
/**
* @note annotations
* refined.MyShouldRefineInSwift
*/
+ (NSString *)myFooRefined __attribute__((swift_private));
@property (class, readonly) NSString *barRefined __attribute__((swift_private));
/**
* @note annotations
* refined.MyShouldRefineInSwift
*/
@property (class, readonly) NSString *myBarRefined __attribute__((swift_private));
@end
__attribute__((swift_name("Person")))
@interface KtPerson : KtBase
@end
@@ -1353,6 +1353,40 @@ __attribute__((swift_name("OverrideMethodsOfAnyKt")))
+ (BOOL)testObj:(id)obj other:(id)other swift:(BOOL)swift error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test(obj:other:swift:)")));
@end
__attribute__((swift_name("RefinedClassA")))
@interface KtRefinedClassA : KtBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)fooRefined __attribute__((swift_private));
@end
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("RefinedClassB")))
@interface KtRefinedClassB : KtRefinedClassA
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)fooRefined __attribute__((swift_private));
@end
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("RefinedKt")))
@interface KtRefinedKt : KtBase
+ (NSString *)fooRefined __attribute__((swift_private));
/**
* @note annotations
* refined.MyShouldRefineInSwift
*/
+ (NSString *)myFooRefined __attribute__((swift_private));
@property (class, readonly) NSString *barRefined __attribute__((swift_private));
/**
* @note annotations
* refined.MyShouldRefineInSwift
*/
@property (class, readonly) NSString *myBarRefined __attribute__((swift_private));
@end
__attribute__((swift_name("Person")))
@interface KtPerson : KtBase
@end
@@ -1353,6 +1353,40 @@ __attribute__((swift_name("OverrideMethodsOfAnyKt")))
+ (BOOL)testObj:(id)obj other:(id)other swift:(BOOL)swift error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test(obj:other:swift:)")));
@end
__attribute__((swift_name("RefinedClassA")))
@interface KtRefinedClassA : KtBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)fooRefined __attribute__((swift_private));
@end
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("RefinedClassB")))
@interface KtRefinedClassB : KtRefinedClassA
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)fooRefined __attribute__((swift_private));
@end
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("RefinedKt")))
@interface KtRefinedKt : KtBase
+ (NSString *)fooRefined __attribute__((swift_private));
/**
* @note annotations
* refined.MyShouldRefineInSwift
*/
+ (NSString *)myFooRefined __attribute__((swift_private));
@property (class, readonly) NSString *barRefined __attribute__((swift_private));
/**
* @note annotations
* refined.MyShouldRefineInSwift
*/
@property (class, readonly) NSString *myBarRefined __attribute__((swift_private));
@end
__attribute__((swift_name("Person")))
@interface KtPerson : KtBase
@end
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2022 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 refined
import kotlin.experimental.ExperimentalObjCRefinement
@ExperimentalObjCRefinement
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@HidesFromObjC
annotation class MyHiddenFromObjC
@ExperimentalObjCRefinement
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@RefinesInSwift
annotation class MyShouldRefineInSwift
@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
fun foo(): Int = 1
@OptIn(ExperimentalObjCRefinement::class)
@ShouldRefineInSwift
fun fooRefined(): String = foo().toString()
@OptIn(ExperimentalObjCRefinement::class)
@MyHiddenFromObjC
fun myFoo(): Int = 2
@OptIn(ExperimentalObjCRefinement::class)
@MyShouldRefineInSwift
fun myFooRefined(): String = myFoo().toString()
@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
val bar: Int = 3
@OptIn(ExperimentalObjCRefinement::class)
@ShouldRefineInSwift
val barRefined: String get() = bar.toString()
@OptIn(ExperimentalObjCRefinement::class)
@MyHiddenFromObjC
val myBar: Int = 4
@OptIn(ExperimentalObjCRefinement::class)
@MyShouldRefineInSwift
val myBarRefined: String get() = myBar.toString()
open class RefinedClassA {
@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
open fun foo(): Int = 1
@OptIn(ExperimentalObjCRefinement::class)
@ShouldRefineInSwift
open fun fooRefined(): String = foo().toString()
}
class RefinedClassB: RefinedClassA() {
override fun foo(): Int = 2
override fun fooRefined(): String {
val foo = foo()
return "$foo$foo"
}
}
@@ -0,0 +1,50 @@
import Kt
extension RefinedKt {
static func foo() -> Int {
return Int(RefinedKt.__fooRefined())! * 2
}
static func myFoo() -> Int {
return Int(RefinedKt.__myFooRefined())! * 2
}
static var bar: Int {
return Int(RefinedKt.__barRefined)! * 2
}
static var myBar: Int {
return Int(RefinedKt.__myBarRefined)! * 2
}
}
extension RefinedClassA {
func foo() -> Int {
return Int(__fooRefined())! * 2
}
}
private func testSwiftRefinements() throws {
try assertEquals(actual: RefinedKt.foo(), expected: 2)
try assertEquals(actual: RefinedKt.bar, expected: 6)
}
private func testMySwiftRefinements() throws {
try assertEquals(actual: RefinedKt.myFoo(), expected: 4)
try assertEquals(actual: RefinedKt.myBar, expected: 8)
}
private func testInheritedRefinements() throws {
try assertEquals(actual: RefinedClassA().foo(), expected: 2)
try assertEquals(actual: RefinedClassB().foo(), expected: 44)
}
class RefinedTests : SimpleTestProvider {
override init() {
super.init()
test("TestSwiftRefinements", testSwiftRefinements)
test("TestMySwiftRefinements", testMySwiftRefinements)
test("TestInheritedRefinements", testInheritedRefinements)
}
}
@@ -6,6 +6,7 @@
package kotlin.native
import kotlin.experimental.ExperimentalObjCName
import kotlin.experimental.ExperimentalObjCRefinement
import kotlin.reflect.KClass
/**
@@ -100,3 +101,58 @@ public actual annotation class CName(actual val externName: String = "", actual
@MustBeDocumented
@ExperimentalObjCName
public actual annotation class ObjCName(actual val name: String = "", actual val swiftName: String = "", actual val exact: Boolean = false)
/**
* Meta-annotation that instructs the Kotlin compiler to remove the annotated function or property from the public Objective-C API.
*
* Annotation processors that refine the public Objective-C API can annotate their annotations with this meta-annotation
* to have the original declarations automatically removed from the public API.
*
* Note: only annotations with [AnnotationTarget.FUNCTION] and/or [AnnotationTarget.PROPERTY] are supported.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@ExperimentalObjCRefinement
public actual annotation class HidesFromObjC
/**
* Instructs the Kotlin compiler to remove this function or property from the public Objective-C API.
*/
@HidesFromObjC
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@ExperimentalObjCRefinement
public actual annotation class HiddenFromObjC
/**
* Meta-annotation that instructs the Kotlin compiler to mark the annotated function or property as
* `swift_private` in the generated Objective-C API.
*
* Annotation processors that refine the public API in Swift can annotate their annotations with this meta-annotation
* to automatically hide the annotated declarations from Swift.
*
* See Apple's documentation of the [`NS_REFINED_FOR_SWIFT`](https://developer.apple.com/documentation/swift/objective-c_and_c_code_customization/improving_objective-c_api_declarations_for_swift)
* macro for more information on refining Objective-C declarations in Swift.
*
* Note: only annotations with [AnnotationTarget.FUNCTION] and/or [AnnotationTarget.PROPERTY] are supported.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@ExperimentalObjCRefinement
public actual annotation class RefinesInSwift
/**
* Instructs the Kotlin compiler to mark this function or property as `swift_private` in the generated Objective-C API.
*
* See Apple's documentation of the [`NS_REFINED_FOR_SWIFT`](https://developer.apple.com/documentation/swift/objective-c_and_c_code_customization/improving_objective-c_api_declarations_for_swift)
* macro for more information on refining Objective-C declarations in Swift.
*/
@RefinesInSwift
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@ExperimentalObjCRefinement
public actual annotation class ShouldRefineInSwift
@@ -38,6 +38,14 @@ public final annotation class ExperimentalObjCName : kotlin.Annotation {
public constructor ExperimentalObjCName()
}
@kotlin.RequiresOptIn
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.ANNOTATION_CLASS})
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
@kotlin.annotation.MustBeDocumented
public final annotation class ExperimentalObjCRefinement : kotlin.Annotation {
public constructor ExperimentalObjCRefinement()
}
@kotlin.RequiresOptIn(level = Level.ERROR)
@kotlin.annotation.MustBeDocumented
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
@@ -38,6 +38,14 @@ public final annotation class ExperimentalObjCName : kotlin.Annotation {
public constructor ExperimentalObjCName()
}
@kotlin.RequiresOptIn
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.ANNOTATION_CLASS})
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
@kotlin.annotation.MustBeDocumented
public final annotation class ExperimentalObjCRefinement : kotlin.Annotation {
public constructor ExperimentalObjCRefinement()
}
@kotlin.RequiresOptIn(level = Level.ERROR)
@kotlin.annotation.MustBeDocumented
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
@@ -6,6 +6,7 @@
package kotlin.native
import kotlin.experimental.ExperimentalObjCName
import kotlin.experimental.ExperimentalObjCRefinement
/**
* Makes top level function available from C/C++ code with the given name.
@@ -65,3 +66,62 @@ expect annotation class FreezingIsDeprecated
@OptionalExpectation
@ExperimentalObjCName
public expect annotation class ObjCName(val name: String = "", val swiftName: String = "", val exact: Boolean = false)
/**
* Meta-annotation that instructs the Kotlin compiler to remove the annotated function or property from the public Objective-C API.
*
* Annotation processors that refine the public Objective-C API can annotate their annotations with this meta-annotation
* to have the original declarations automatically removed from the public API.
*
* Note: only annotations with [AnnotationTarget.FUNCTION] and/or [AnnotationTarget.PROPERTY] are supported.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@OptionalExpectation
@ExperimentalObjCRefinement
public expect annotation class HidesFromObjC()
/**
* Instructs the Kotlin compiler to remove this function or property from the public Objective-C API.
*/
@HidesFromObjC
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@OptionalExpectation
@ExperimentalObjCRefinement
public expect annotation class HiddenFromObjC()
/**
* Meta-annotation that instructs the Kotlin compiler to mark the annotated function or property as
* `swift_private` in the generated Objective-C API.
*
* Annotation processors that refine the public API in Swift can annotate their annotations with this meta-annotation
* to automatically hide the annotated declarations from Swift.
*
* See Apple's documentation of the [`NS_REFINED_FOR_SWIFT`](https://developer.apple.com/documentation/swift/objective-c_and_c_code_customization/improving_objective-c_api_declarations_for_swift)
* macro for more information on refining Objective-C declarations in Swift.
*
* Note: only annotations with [AnnotationTarget.FUNCTION] and/or [AnnotationTarget.PROPERTY] are supported.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@OptionalExpectation
@ExperimentalObjCRefinement
public expect annotation class RefinesInSwift()
/**
* Instructs the Kotlin compiler to mark this function or property as `swift_private` in the generated Objective-C API.
*
* See Apple's documentation of the [`NS_REFINED_FOR_SWIFT`](https://developer.apple.com/documentation/swift/objective-c_and_c_code_customization/improving_objective-c_api_declarations_for_swift)
* macro for more information on refining Objective-C declarations in Swift.
*/
@RefinesInSwift
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@OptionalExpectation
@ExperimentalObjCRefinement
public expect annotation class ShouldRefineInSwift()
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2022 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 kotlin.experimental
/**
* This annotation marks the experimental Objective-C export refinement annotations.
*/
@RequiresOptIn
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public annotation class ExperimentalObjCRefinement
@@ -3148,6 +3148,9 @@ public final class kotlin/enums/EnumEntriesKt {
public abstract interface annotation class kotlin/experimental/ExperimentalObjCName : java/lang/annotation/Annotation {
}
public abstract interface annotation class kotlin/experimental/ExperimentalObjCRefinement : java/lang/annotation/Annotation {
}
public abstract interface annotation class kotlin/experimental/ExperimentalTypeInference : java/lang/annotation/Annotation {
}