From 070ad452b5b75812565a505a01490aee2ad2b0bc Mon Sep 17 00:00:00 2001 From: SvyatoslavScherbina Date: Wed, 25 Mar 2020 14:31:20 +0300 Subject: [PATCH] Fix #4002 (#4009) Fix ObjCExport initialization order when static initializer triggers early KotlinBase initialization. For example, _swift_eager_class_initialization can do this. --- .../tests/objcexport/expectedLazy.h | 13 ++++++++++ backend.native/tests/objcexport/gh4002.kt | 4 +++ backend.native/tests/objcexport/gh4002.swift | 26 +++++++++++++++++++ runtime/src/main/cpp/ObjCExport.mm | 3 +++ runtime/src/objc/cpp/ObjCExportClasses.mm | 7 ++++- 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 backend.native/tests/objcexport/gh4002.kt create mode 100644 backend.native/tests/objcexport/gh4002.swift diff --git a/backend.native/tests/objcexport/expectedLazy.h b/backend.native/tests/objcexport/expectedLazy.h index b0607115696..dd2bc9965f0 100644 --- a/backend.native/tests/objcexport/expectedLazy.h +++ b/backend.native/tests/objcexport/expectedLazy.h @@ -191,6 +191,19 @@ __attribute__((swift_name("CoroutinesKt"))) + (BOOL)callSuspendBridgeBridge:(KtAbstractSuspendBridge *)bridge resultHolder:(KtResultHolder *)resultHolder error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("callSuspendBridge(bridge:resultHolder:)"))); @end; +__attribute__((swift_name("GH4002ArgumentBase"))) +@interface KtGH4002ArgumentBase : 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("GH4002Argument"))) +@interface KtGH4002Argument : KtGH4002ArgumentBase +- (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("DelegateClass"))) @interface KtDelegateClass : KtBase diff --git a/backend.native/tests/objcexport/gh4002.kt b/backend.native/tests/objcexport/gh4002.kt new file mode 100644 index 00000000000..ed11b94e8f6 --- /dev/null +++ b/backend.native/tests/objcexport/gh4002.kt @@ -0,0 +1,4 @@ +package gh4002 + +open class GH4002ArgumentBase +class GH4002Argument : GH4002ArgumentBase() \ No newline at end of file diff --git a/backend.native/tests/objcexport/gh4002.swift b/backend.native/tests/objcexport/gh4002.swift new file mode 100644 index 00000000000..d962f27bf20 --- /dev/null +++ b/backend.native/tests/objcexport/gh4002.swift @@ -0,0 +1,26 @@ +import Kt + +// See https://github.com/JetBrains/kotlin-native/issues/4002 + +class GH4002Base0 : NSObject, NSCoding { + required init(coder: NSCoder) { fatalError() } + + func encode(with coder: NSCoder) { fatalError() } +} + +class GH4002Base1 : GH4002Base0 {} + +@objc(ObjCGH4002) +class GH4002 : GH4002Base1 {} + +private func test1() throws { + try assertEquals(actual: String(cString: class_getName(GH4002.self)), expected: "ObjCGH4002") +} + +class Gh4002Tests : SimpleTestProvider { + override init() { + super.init() + + test("Test1", test1) + } +} \ No newline at end of file diff --git a/runtime/src/main/cpp/ObjCExport.mm b/runtime/src/main/cpp/ObjCExport.mm index c888eee295f..2e06e351593 100644 --- a/runtime/src/main/cpp/ObjCExport.mm +++ b/runtime/src/main/cpp/ObjCExport.mm @@ -325,6 +325,9 @@ static void initTypeAdapters() { } static void Kotlin_ObjCExport_initializeImpl() { + RuntimeCheck(Kotlin_ObjCExport_toKotlinSelector != nullptr, "unexpected initialization order"); + RuntimeCheck(Kotlin_ObjCExport_releaseAsAssociatedObjectSelector != nullptr, "unexpected initialization order"); + initTypeAdapters(); SEL toKotlinSelector = Kotlin_ObjCExport_toKotlinSelector; diff --git a/runtime/src/objc/cpp/ObjCExportClasses.mm b/runtime/src/objc/cpp/ObjCExportClasses.mm index c366c4668a7..e679895d76f 100644 --- a/runtime/src/objc/cpp/ObjCExportClasses.mm +++ b/runtime/src/objc/cpp/ObjCExportClasses.mm @@ -36,6 +36,8 @@ extern "C" id objc_autoreleaseReturnValue(id self); -(BOOL)_tryRetain; @end; +static void injectToRuntime(); + @implementation KotlinBase { BackRefFromAssociatedObject refHolder; } @@ -44,6 +46,10 @@ extern "C" id objc_autoreleaseReturnValue(id self); RETURN_OBJ(refHolder.ref()); } ++(void)load { + injectToRuntime(); +} + +(void)initialize { if (self == [KotlinBase class]) { Kotlin_ObjCExport_initialize(); @@ -204,7 +210,6 @@ OBJ_GETTER(Kotlin_boxDouble, KDouble value); } @end; -__attribute__((constructor)) static void injectToRuntime() { RuntimeCheck(Kotlin_ObjCExport_toKotlinSelector == nullptr, "runtime injected twice"); Kotlin_ObjCExport_toKotlinSelector = @selector(toKotlin:);