diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h index 8176fda254c23..c3f3e813644ed 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazy.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazy.h @@ -1033,6 +1033,25 @@ __attribute__((swift_name("Bar"))) @property (readonly) NSString *greeting __attribute__((swift_name("greeting"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("KT54119KotlinKey"))) +@interface KtKT54119KotlinKey : 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("Kt54119Kt"))) +@interface KtKt54119Kt : KtBase ++ (BOOL)callContainsSet:(NSSet *)set __attribute__((swift_name("callContains(set:)"))); ++ (id _Nullable)callGetElementSet:(NSSet *)set __attribute__((swift_name("callGetElement(set:)"))); ++ (BOOL)callContainsKeyMap:(NSDictionary *)map __attribute__((swift_name("callContainsKey(map:)"))); ++ (BOOL)callContainsValueMap:(NSDictionary *)map __attribute__((swift_name("callContainsValue(map:)"))); ++ (id _Nullable)callGetMap:(NSDictionary *)map __attribute__((swift_name("callGet(map:)"))); ++ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)"))); ++ (BOOL)callContainsEntryMap:(NSDictionary *)map __attribute__((swift_name("callContainsEntry(map:)"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("LibraryKt"))) @interface KtLibraryKt : KtBase diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h index 8850c9dd114d2..28e6b4307e9ab 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h @@ -968,6 +968,25 @@ __attribute__((swift_name("Bar"))) @property (readonly) NSString *greeting __attribute__((swift_name("greeting"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("KT54119KotlinKey"))) +@interface KtKT54119KotlinKey : 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("Kt54119Kt"))) +@interface KtKt54119Kt : KtBase ++ (BOOL)callContainsSet:(NSSet *)set __attribute__((swift_name("callContains(set:)"))); ++ (id _Nullable)callGetElementSet:(NSSet *)set __attribute__((swift_name("callGetElement(set:)"))); ++ (BOOL)callContainsKeyMap:(NSDictionary *)map __attribute__((swift_name("callContainsKey(map:)"))); ++ (BOOL)callContainsValueMap:(NSDictionary *)map __attribute__((swift_name("callContainsValue(map:)"))); ++ (id _Nullable)callGetMap:(NSDictionary *)map __attribute__((swift_name("callGet(map:)"))); ++ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)"))); ++ (BOOL)callContainsEntryMap:(NSDictionary *)map __attribute__((swift_name("callContainsEntry(map:)"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("LibraryKt"))) @interface KtLibraryKt : KtBase diff --git a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h index 4c33c1d151580..b77ea56872cad 100644 --- a/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h +++ b/kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h @@ -968,6 +968,25 @@ __attribute__((swift_name("Bar"))) @property (readonly) NSString *greeting __attribute__((swift_name("greeting"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("KT54119KotlinKey"))) +@interface KtKT54119KotlinKey : 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("Kt54119Kt"))) +@interface KtKt54119Kt : KtBase ++ (BOOL)callContainsSet:(NSSet *)set __attribute__((swift_name("callContains(set:)"))); ++ (id _Nullable)callGetElementSet:(NSSet *)set __attribute__((swift_name("callGetElement(set:)"))); ++ (BOOL)callContainsKeyMap:(NSDictionary *)map __attribute__((swift_name("callContainsKey(map:)"))); ++ (BOOL)callContainsValueMap:(NSDictionary *)map __attribute__((swift_name("callContainsValue(map:)"))); ++ (id _Nullable)callGetMap:(NSDictionary *)map __attribute__((swift_name("callGet(map:)"))); ++ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)"))); ++ (BOOL)callContainsEntryMap:(NSDictionary *)map __attribute__((swift_name("callContainsEntry(map:)"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("LibraryKt"))) @interface KtLibraryKt : KtBase diff --git a/kotlin-native/backend.native/tests/objcexport/kt54119.kt b/kotlin-native/backend.native/tests/objcexport/kt54119.kt new file mode 100644 index 0000000000000..8af22be6e94f3 --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/kt54119.kt @@ -0,0 +1,24 @@ +/* + * 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 kt54119 + +class KT54119KotlinKey + +private typealias Foo = KT54119KotlinKey + +fun callContains(set: Set<*>) = set.contains(Foo()) + +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +fun callGetElement(set: Set<*>) = (set as kotlin.native.internal.KonanSet).getElement(Foo()) + +fun callContainsKey(map: Map<*, *>) = map.containsKey(Foo()) + +fun callContainsValue(map: Map<*, *>) = map.containsValue(Foo()) + +fun callGet(map: Map<*, *>) = map.get(Foo()) + +fun callGetOrThrowConcurrentModification(map: Map<*, *>) = map.hashCode() // calls getOrThrowConcurrentModification under the hood. +fun callContainsEntry(map: Map<*, *>) = map.entries.contains(map.entries.first()) diff --git a/kotlin-native/backend.native/tests/objcexport/kt54119.swift b/kotlin-native/backend.native/tests/objcexport/kt54119.swift new file mode 100644 index 0000000000000..ac9198392c280 --- /dev/null +++ b/kotlin-native/backend.native/tests/objcexport/kt54119.swift @@ -0,0 +1,51 @@ +/* + * 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. + */ + +import Foundation +import Kt + +// See https://youtrack.jetbrains.com/issue/KT-54119/Native-runtime-assertion-failed-due-to-missing-thread-state-switch + +private func testSetContains() throws { + try assertFalse(Kt54119Kt.callContains(set: ["111"])) +} + +private func testSetGetElement() throws { + try assertNil(Kt54119Kt.callGetElement(set: [222])) +} + +private func testMapContainsKey() throws { + try assertFalse(Kt54119Kt.callContainsKey(map: ["abc" : "def"])) +} + +private func testMapContainsValue() throws { + try assertFalse(Kt54119Kt.callContainsValue(map: [KT54119KotlinKey() : 1])) +} + +private func testMapGet() throws { + try assertNil(Kt54119Kt.callGet(map: [0 : 0])) +} + +private func testMapGetOrThrowConcurrentModification() throws { + Kt54119Kt.callGetOrThrowConcurrentModification(map: [KT54119KotlinKey() : 2]) +} + +private func testMapContainsEntry() throws { + try assertTrue(Kt54119Kt.callContainsEntry(map: [KT54119KotlinKey() : 3])) +} + +class Kt54119Tests : SimpleTestProvider { + override init() { + super.init() + + test("testSetContains", testSetContains) + test("testSetGetElement", testSetGetElement) + test("testMapContainsKey", testMapContainsKey) + test("testMapContainsValue", testMapContainsValue) + test("testMapGet", testMapGet) + test("testMapGetOrThrowConcurrentModification", testMapGetOrThrowConcurrentModification) + test("testMapContainsEntry", testMapContainsEntry) + } +} diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm b/kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm index 621270952ca6c..1db58b09bc694 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm @@ -119,16 +119,21 @@ static inline KInt objCSizeToKotlinOrThrow(NSUInteger size) { return objCSizeToKotlinOrThrow(set.count); } -NO_EXTERNAL_CALLS_CHECK extern "C" KBoolean Kotlin_NSSetAsKSet_contains(KRef thiz, KRef element) { NSSet* set = (NSSet*) GetAssociatedObject(thiz); - return [set containsObject:refToObjCOrNSNull(element)]; + id objCElement = refToObjCOrNSNull(element); + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + return [set containsObject:objCElement]; } -NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_getElement, KRef thiz, KRef element) { NSSet* set = (NSSet*) GetAssociatedObject(thiz); - id res = [set member:refToObjCOrNSNull(element)]; + id objCElement = refToObjCOrNSNull(element); + id res; + { + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + res = [set member:objCElement]; + } RETURN_RESULT_OF(refFromObjCOrNSNull, res); } @@ -148,16 +153,17 @@ static inline OBJ_GETTER(CreateKIteratorFromNSEnumerator, NSEnumerator* enumerat return objCSizeToKotlinOrThrow(dict.count); } -NO_EXTERNAL_CALLS_CHECK extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsKey(KRef thiz, KRef key) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); - return [dict objectForKey:refToObjCOrNSNull(key)] != nullptr; + id objCKey = refToObjCOrNSNull(key); + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + return [dict objectForKey:objCKey] != nullptr; } -NO_EXTERNAL_CALLS_CHECK extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsValue(KRef thiz, KRef value) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); id objCValue = refToObjCOrNSNull(value); + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); for (id key in dict) { if ([[dict objectForKey:key] isEqual:objCValue]) { return true; @@ -167,17 +173,25 @@ static inline OBJ_GETTER(CreateKIteratorFromNSEnumerator, NSEnumerator* enumerat return false; } -NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_get, KRef thiz, KRef key) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); - id value = [dict objectForKey:refToObjCOrNSNull(key)]; + id objCKey = refToObjCOrNSNull(key); + id value; + { + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + value = [dict objectForKey:objCKey]; + } RETURN_RESULT_OF(refFromObjCOrNSNull, value); } -NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_getOrThrowConcurrentModification, KRef thiz, KRef key) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); - id value = [dict objectForKey:refToObjCOrNSNull(key)]; + id objCKey = refToObjCOrNSNull(key); + id value; + { + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + value = [dict objectForKey:objCKey]; + } if (value == nullptr) { Kotlin_ObjCExport_ThrowCollectionConcurrentModification(); } @@ -185,10 +199,12 @@ static inline OBJ_GETTER(CreateKIteratorFromNSEnumerator, NSEnumerator* enumerat RETURN_RESULT_OF(refFromObjCOrNSNull, value); } -NO_EXTERNAL_CALLS_CHECK extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsEntry(KRef thiz, KRef key, KRef value) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); - return [refToObjCOrNSNull(value) isEqual:[dict objectForKey:refToObjCOrNSNull(key)]]; + id objCValue = refToObjCOrNSNull(value); + id objCKey = refToObjCOrNSNull(key); + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + return [objCValue isEqual:[dict objectForKey:objCKey]]; } NO_EXTERNAL_CALLS_CHECK