[K/N] KT-56464: implementation of HiddenFromObjC for classes in backend

This commit is contained in:
Sergey Bogolepov
2023-04-25 18:05:40 +03:00
committed by Space Team
parent 6651e6ed8c
commit fce878d63a
10 changed files with 54 additions and 17 deletions
@@ -61,7 +61,7 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
)
map.put(
INVALID_OBJC_REFINEMENT_TARGETS,
"Refines annotations are only applicable to annotations with targets FUNCTION and/or PROPERTY"
"Refines annotations are only applicable to annotations with targets CLASS, FUNCTION and/or PROPERTY"
)
map.put(INAPPLICABLE_OBJC_NAME, "@ObjCName is not applicable on overrides")
map.put(INVALID_OBJC_NAME, "@ObjCName should have a name and/or swiftName")
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotation
object FirNativeObjCRefinementAnnotationChecker : FirRegularClassChecker() {
private val supportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY)
private val supportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY, KotlinTarget.CLASS)
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.classKind != ClassKind.ANNOTATION_CLASS) return
@@ -25,7 +25,7 @@ public annotation class ShouldRefineInSwift
package plugin
@HidesFromObjC
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class PluginHiddenFromObjC
@@ -45,7 +45,7 @@ import plugin.PluginShouldRefineInSwift
annotation class MyRefinedAnnotationA
<!INVALID_OBJC_REFINEMENT_TARGETS!>@HidesFromObjC<!>
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FILE)
@Retention(AnnotationRetention.BINARY)
annotation class MyRefinedAnnotationB
@@ -88,7 +88,7 @@ public interface InterfaceB {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.native.HidesFromObjC @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.CLASS}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class MyRefinedAnnotationB : kotlin.Annotation {
@kotlin.native.HidesFromObjC @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FILE}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class MyRefinedAnnotationB : kotlin.Annotation {
public constructor MyRefinedAnnotationB()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@@ -146,7 +146,7 @@ package kotlin {
package plugin {
@kotlin.native.HidesFromObjC @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class PluginHiddenFromObjC : kotlin.Annotation {
@kotlin.native.HidesFromObjC @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class PluginHiddenFromObjC : kotlin.Annotation {
public constructor PluginHiddenFromObjC()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@@ -596,9 +596,36 @@ private class BackendChecker(
if (declaration.isKotlinObjCClass()) {
checkKotlinObjCClass(declaration)
}
checkHiddenFromObjCRestrictions(declaration)
super.visitClass(declaration)
}
private fun IrClass.isHiddenFromObjC(): Boolean {
return annotations.any { annotationCall ->
val annotationClass = annotationCall.symbol.owner.parentAsClass
annotationClass.annotations.hasAnnotation(KonanFqNames.hidesFromObjC)
}
}
private fun checkHiddenFromObjCRestrictions(declaration: IrClass) {
// If declaration itself. is marked as hidden, we don't need to do anything
if (declaration.isHiddenFromObjC()) return
// Enum entries inherit from their classes, so if enum class is marked as hidden, there is no need to
// produce additional errors.
if (declaration.kind == ClassKind.ENUM_ENTRY) return
// Private and local classes does not leak to Objective-C API surface, so it is OK for them
// to inherit from hidden types.
if (DescriptorVisibilities.isPrivate(declaration.visibility) || declaration.isLocal) return
if (declaration.superClass?.isHiddenFromObjC() == true) {
reportError(declaration, "Only @HiddenFromObjC declaration can inherit from class annotated with @HiddenFromObjC")
}
declaration.getSuperInterfaces().forEach {
if (it.isHiddenFromObjC()) {
reportError(declaration, "Only @HiddenFromObjC declaration can implement interface annotated with @HiddenFromObjC")
}
}
}
}
private fun BackendChecker.checkCanGenerateCCall(expression: IrCall, isInvoke: Boolean) {
@@ -108,6 +108,16 @@ private fun CallableMemberDescriptor.isHiddenFromObjC(): Boolean = when {
}
}
/**
* Check if the given class or its enclosing declaration is marked as @HiddenFromObjC.
*/
internal fun ClassDescriptor.isHiddenFromObjC(): Boolean = when {
(this.containingDeclaration as? ClassDescriptor)?.isHiddenFromObjC() == true -> true
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()
@@ -175,7 +185,7 @@ internal fun ObjCExportMapper.shouldBeVisible(descriptor: ClassDescriptor): Bool
descriptor.isEffectivelyPublicApi && when (descriptor.kind) {
ClassKind.CLASS, ClassKind.INTERFACE, ClassKind.ENUM_CLASS, ClassKind.OBJECT -> true
ClassKind.ENUM_ENTRY, ClassKind.ANNOTATION_CLASS -> false
} && !descriptor.isExpect && !descriptor.isInlined() && !isHiddenByDeprecation(descriptor)
} && !descriptor.isExpect && !descriptor.isInlined() && !isHiddenByDeprecation(descriptor) && !descriptor.isHiddenFromObjC()
private fun ObjCExportMapper.isBase(descriptor: CallableMemberDescriptor): Boolean =
descriptor.overriddenDescriptors.all { !shouldBeExposed(it) }
@@ -107,12 +107,12 @@ public actual annotation class CName(actual val externName: String = "", actual
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.
* Meta-annotation that instructs the Kotlin compiler to remove the annotated class, 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.
* Note: only annotations with [AnnotationTarget.CLASS], [AnnotationTarget.FUNCTION] and/or [AnnotationTarget.PROPERTY] are supported.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
@@ -122,10 +122,10 @@ public actual annotation class ObjCName(actual val name: String = "", actual val
public actual annotation class HidesFromObjC
/**
* Instructs the Kotlin compiler to remove this function or property from the public Objective-C API.
* Instructs the Kotlin compiler to remove this class, function or property from the public Objective-C API.
*/
@HidesFromObjC
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@ExperimentalObjCRefinement
@@ -69,12 +69,12 @@ expect annotation class FreezingIsDeprecated
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.
* Meta-annotation that instructs the Kotlin compiler to remove the annotated class, 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.
* Note: only annotations with [AnnotationTarget.CLASS], [AnnotationTarget.FUNCTION] and/or [AnnotationTarget.PROPERTY] are supported.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.BINARY)
@@ -85,10 +85,10 @@ public expect annotation class ObjCName(val name: String = "", val swiftName: St
public expect annotation class HidesFromObjC()
/**
* Instructs the Kotlin compiler to remove this function or property from the public Objective-C API.
* Instructs the Kotlin compiler to remove this class, function or property from the public Objective-C API.
*/
@HidesFromObjC
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@OptionalExpectation
@@ -64,7 +64,7 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
)
put(
ErrorsNative.INVALID_OBJC_REFINEMENT_TARGETS,
"Refines annotations are only applicable to annotations with targets FUNCTION and/or PROPERTY"
"Refines annotations are only applicable to annotations with targets CLASS, FUNCTION and/or PROPERTY"
)
}
}
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeObjCRefinementChecke
object NativeObjCRefinementAnnotationChecker : DeclarationChecker {
private val supportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY)
private val supportedTargets = arrayOf(KotlinTarget.FUNCTION, KotlinTarget.PROPERTY, KotlinTarget.CLASS)
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is ClassDescriptor || descriptor.kind != ClassKind.ANNOTATION_CLASS) return