From 68445deae81b8097dbcfa4561632303d0f1c98e2 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Wed, 22 Sep 2021 11:49:47 +0300 Subject: [PATCH] [K/N] Forbid calling sealed class constructor from objc --- .../konan/objcexport/ObjCExportMapper.kt | 5 +- .../tests/objcexport/expectedLazy.h | 56 +++++++++++++++++-- .../tests/objcexport/expectedLazyNoGenerics.h | 56 +++++++++++++++++-- .../tests/objcexport/sealedClass.kt | 15 +++++ .../tests/objcexport/sealedClass.swift | 26 +++++++++ 5 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 kotlin-native/backend.native/tests/objcexport/sealedClass.kt create mode 100644 kotlin-native/backend.native/tests/objcexport/sealedClass.swift diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt index 85a26093284..ca01c9b015b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.common.descriptors.allParameters -import org.jetbrains.kotlin.backend.common.descriptors.isSuspend import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors import org.jetbrains.kotlin.backend.konan.descriptors.isArray @@ -71,10 +70,12 @@ internal fun ObjCExportMapper.getClassIfCategory(extensionReceiverType: KotlinTy } } +private fun isSealedClassConstructor(descriptor: ConstructorDescriptor) = descriptor.constructedClass.isSealed() + // Note: partially duplicated in ObjCExportLazyImpl.translateTopLevels. internal fun ObjCExportMapper.shouldBeExposed(descriptor: CallableMemberDescriptor): Boolean = descriptor.isEffectivelyPublicApi && !descriptor.isExpect && - !isHiddenByDeprecation(descriptor) + !isHiddenByDeprecation(descriptor) && !(descriptor is ConstructorDescriptor && isSealedClassConstructor(descriptor)) internal fun ObjCExportMapper.shouldBeExposed(descriptor: ClassDescriptor): Boolean = shouldBeVisible(descriptor) && !isSpecialMapped(descriptor) && !descriptor.defaultType.isObjCObjectType() diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h index f766b5149ac..82af320aad5 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h @@ -1113,6 +1113,56 @@ __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("Person"))) +@interface KtPerson : KtBase +@end; + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("Person.User"))) +@interface KtPersonUser : KtPerson +- (instancetype)initWithId:(int32_t)id __attribute__((swift_name("init(id:)"))) __attribute__((objc_designated_initializer)); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +- (int32_t)component1 __attribute__((swift_name("component1()"))); +- (KtPersonUser *)doCopyId:(int32_t)id __attribute__((swift_name("doCopy(id:)"))); +@property (readonly) int32_t id __attribute__((swift_name("id"))); +@end; + +__attribute__((swift_name("Person.Worker"))) +@interface KtPersonWorker : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end; + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("Person.WorkerEmployee"))) +@interface KtPersonWorkerEmployee : KtPersonWorker +- (instancetype)initWithId:(int32_t)id __attribute__((swift_name("init(id:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable)); ++ (instancetype)new __attribute__((unavailable)); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +- (int32_t)component1 __attribute__((swift_name("component1()"))); +- (KtPersonWorkerEmployee *)doCopyId:(int32_t)id __attribute__((swift_name("doCopy(id:)"))); +@property (readonly) int32_t id __attribute__((swift_name("id"))); +@end; + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("Person.WorkerContractor"))) +@interface KtPersonWorkerContractor : KtPersonWorker +- (instancetype)initWithId:(int32_t)id __attribute__((swift_name("init(id:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable)); ++ (instancetype)new __attribute__((unavailable)); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +- (int32_t)component1 __attribute__((swift_name("component1()"))); +- (KtPersonWorkerContractor *)doCopyId:(int32_t)id __attribute__((swift_name("doCopy(id:)"))); +@property (readonly) int32_t id __attribute__((swift_name("id"))); +@end; + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("ThrowableAsError"))) @interface KtThrowableAsError : KtKotlinThrowable @@ -2633,8 +2683,6 @@ __attribute__((swift_name("ValuesKt"))) __attribute__((swift_name("InvariantSuper"))) @interface KtInvariantSuper : KtBase -- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); -+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); @end; __attribute__((objc_subclassing_restricted)) @@ -2646,8 +2694,6 @@ __attribute__((swift_name("Invariant"))) __attribute__((swift_name("OutVariantSuper"))) @interface KtOutVariantSuper<__covariant T> : KtBase -- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); -+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); @end; __attribute__((objc_subclassing_restricted)) @@ -2659,8 +2705,6 @@ __attribute__((swift_name("OutVariant"))) __attribute__((swift_name("InVariantSuper"))) @interface KtInVariantSuper<__contravariant T> : KtBase -- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); -+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); @end; __attribute__((objc_subclassing_restricted)) diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h index 5be86035121..97650fb3f43 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h @@ -1055,6 +1055,56 @@ __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("Person"))) +@interface KtPerson : KtBase +@end; + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("Person.User"))) +@interface KtPersonUser : KtPerson +- (instancetype)initWithId:(int32_t)id __attribute__((swift_name("init(id:)"))) __attribute__((objc_designated_initializer)); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +- (int32_t)component1 __attribute__((swift_name("component1()"))); +- (KtPersonUser *)doCopyId:(int32_t)id __attribute__((swift_name("doCopy(id:)"))); +@property (readonly) int32_t id __attribute__((swift_name("id"))); +@end; + +__attribute__((swift_name("Person.Worker"))) +@interface KtPersonWorker : KtBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +@end; + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("Person.WorkerEmployee"))) +@interface KtPersonWorkerEmployee : KtPersonWorker +- (instancetype)initWithId:(int32_t)id __attribute__((swift_name("init(id:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable)); ++ (instancetype)new __attribute__((unavailable)); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +- (int32_t)component1 __attribute__((swift_name("component1()"))); +- (KtPersonWorkerEmployee *)doCopyId:(int32_t)id __attribute__((swift_name("doCopy(id:)"))); +@property (readonly) int32_t id __attribute__((swift_name("id"))); +@end; + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("Person.WorkerContractor"))) +@interface KtPersonWorkerContractor : KtPersonWorker +- (instancetype)initWithId:(int32_t)id __attribute__((swift_name("init(id:)"))) __attribute__((objc_designated_initializer)); +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable)); ++ (instancetype)new __attribute__((unavailable)); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +- (int32_t)component1 __attribute__((swift_name("component1()"))); +- (KtPersonWorkerContractor *)doCopyId:(int32_t)id __attribute__((swift_name("doCopy(id:)"))); +@property (readonly) int32_t id __attribute__((swift_name("id"))); +@end; + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("ThrowableAsError"))) @interface KtThrowableAsError : KtKotlinThrowable @@ -2575,8 +2625,6 @@ __attribute__((swift_name("ValuesKt"))) __attribute__((swift_name("InvariantSuper"))) @interface KtInvariantSuper : KtBase -- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); -+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); @end; __attribute__((objc_subclassing_restricted)) @@ -2588,8 +2636,6 @@ __attribute__((swift_name("Invariant"))) __attribute__((swift_name("OutVariantSuper"))) @interface KtOutVariantSuper : KtBase -- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); -+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); @end; __attribute__((objc_subclassing_restricted)) @@ -2601,8 +2647,6 @@ __attribute__((swift_name("OutVariant"))) __attribute__((swift_name("InVariantSuper"))) @interface KtInVariantSuper : KtBase -- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); -+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); @end; __attribute__((objc_subclassing_restricted)) diff --git a/kotlin-native/backend.native/tests/objcexport/sealedClass.kt b/kotlin-native/backend.native/tests/objcexport/sealedClass.kt new file mode 100644 index 00000000000..c5ea6f4330a --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/sealedClass.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2021 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 sealedClass + +sealed class Person() { + data class User(val id: Int) : Person() + + abstract class Worker() { + data class Employee(val id: Int) : Worker() + data class Contractor(val id: Int) : Worker() + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/objcexport/sealedClass.swift b/kotlin-native/backend.native/tests/objcexport/sealedClass.swift new file mode 100644 index 00000000000..9c060293775 --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/sealedClass.swift @@ -0,0 +1,26 @@ +import Kt + +private func test1() throws { + let user = Person.User(id: 1) + let worker = Person.Worker() + let employee = Person.WorkerEmployee(id: 2) + let contractor = Person.WorkerContractor(id: 3) + let userAsPerson: Person = user + let employeeAsWorker: Person.Worker = employee + let contractorAsWorker: Person.Worker = contractor + + try assertEquals(actual: user.id, expected: 1) + try assertEquals(actual: (userAsPerson as! Person.User).id, expected: 1) + try assertEquals(actual: employee.id, expected: 2) + try assertEquals(actual: (employeeAsWorker as! Person.WorkerEmployee).id, expected: 2) + try assertEquals(actual: contractor.id, expected: 3) + try assertEquals(actual: (contractorAsWorker as! Person.WorkerContractor).id, expected: 3) +} + +class SealedClassTests : SimpleTestProvider { + override init() { + super.init() + + test("Test1", test1) + } +} \ No newline at end of file