Skip to content

Commit 1244679

Browse files
Native: improve thread state switches for NSSet/NSDictionary adapters
^KT-54119 (cherry picked from commit 39c73e2)
1 parent 1abfeb9 commit 1244679

File tree

6 files changed

+161
-13
lines changed

6 files changed

+161
-13
lines changed

kotlin-native/backend.native/tests/objcexport/expectedLazy.h

+19
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,25 @@ __attribute__((swift_name("Bar")))
10331033
@property (readonly) NSString *greeting __attribute__((swift_name("greeting")));
10341034
@end
10351035

1036+
__attribute__((objc_subclassing_restricted))
1037+
__attribute__((swift_name("KT54119KotlinKey")))
1038+
@interface KtKT54119KotlinKey : KtBase
1039+
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
1040+
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
1041+
@end
1042+
1043+
__attribute__((objc_subclassing_restricted))
1044+
__attribute__((swift_name("Kt54119Kt")))
1045+
@interface KtKt54119Kt : KtBase
1046+
+ (BOOL)callContainsSet:(NSSet<id> *)set __attribute__((swift_name("callContains(set:)")));
1047+
+ (id _Nullable)callGetElementSet:(NSSet<id> *)set __attribute__((swift_name("callGetElement(set:)")));
1048+
+ (BOOL)callContainsKeyMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsKey(map:)")));
1049+
+ (BOOL)callContainsValueMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsValue(map:)")));
1050+
+ (id _Nullable)callGetMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGet(map:)")));
1051+
+ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)")));
1052+
+ (BOOL)callContainsEntryMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsEntry(map:)")));
1053+
@end
1054+
10361055
__attribute__((objc_subclassing_restricted))
10371056
__attribute__((swift_name("LibraryKt")))
10381057
@interface KtLibraryKt : KtBase

kotlin-native/backend.native/tests/objcexport/expectedLazyLegacySuspendUnit.h

+19
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,25 @@ __attribute__((swift_name("Bar")))
968968
@property (readonly) NSString *greeting __attribute__((swift_name("greeting")));
969969
@end
970970

971+
__attribute__((objc_subclassing_restricted))
972+
__attribute__((swift_name("KT54119KotlinKey")))
973+
@interface KtKT54119KotlinKey : KtBase
974+
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
975+
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
976+
@end
977+
978+
__attribute__((objc_subclassing_restricted))
979+
__attribute__((swift_name("Kt54119Kt")))
980+
@interface KtKt54119Kt : KtBase
981+
+ (BOOL)callContainsSet:(NSSet<id> *)set __attribute__((swift_name("callContains(set:)")));
982+
+ (id _Nullable)callGetElementSet:(NSSet<id> *)set __attribute__((swift_name("callGetElement(set:)")));
983+
+ (BOOL)callContainsKeyMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsKey(map:)")));
984+
+ (BOOL)callContainsValueMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsValue(map:)")));
985+
+ (id _Nullable)callGetMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGet(map:)")));
986+
+ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)")));
987+
+ (BOOL)callContainsEntryMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsEntry(map:)")));
988+
@end
989+
971990
__attribute__((objc_subclassing_restricted))
972991
__attribute__((swift_name("LibraryKt")))
973992
@interface KtLibraryKt : KtBase

kotlin-native/backend.native/tests/objcexport/expectedLazyNoGenerics.h

+19
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,25 @@ __attribute__((swift_name("Bar")))
968968
@property (readonly) NSString *greeting __attribute__((swift_name("greeting")));
969969
@end
970970

971+
__attribute__((objc_subclassing_restricted))
972+
__attribute__((swift_name("KT54119KotlinKey")))
973+
@interface KtKT54119KotlinKey : KtBase
974+
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
975+
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
976+
@end
977+
978+
__attribute__((objc_subclassing_restricted))
979+
__attribute__((swift_name("Kt54119Kt")))
980+
@interface KtKt54119Kt : KtBase
981+
+ (BOOL)callContainsSet:(NSSet<id> *)set __attribute__((swift_name("callContains(set:)")));
982+
+ (id _Nullable)callGetElementSet:(NSSet<id> *)set __attribute__((swift_name("callGetElement(set:)")));
983+
+ (BOOL)callContainsKeyMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsKey(map:)")));
984+
+ (BOOL)callContainsValueMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsValue(map:)")));
985+
+ (id _Nullable)callGetMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGet(map:)")));
986+
+ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)")));
987+
+ (BOOL)callContainsEntryMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsEntry(map:)")));
988+
@end
989+
971990
__attribute__((objc_subclassing_restricted))
972991
__attribute__((swift_name("LibraryKt")))
973992
@interface KtLibraryKt : KtBase
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
4+
*/
5+
6+
package kt54119
7+
8+
class KT54119KotlinKey
9+
10+
private typealias Foo = KT54119KotlinKey
11+
12+
fun callContains(set: Set<*>) = set.contains(Foo())
13+
14+
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
15+
fun callGetElement(set: Set<*>) = (set as kotlin.native.internal.KonanSet<Any?>).getElement(Foo())
16+
17+
fun callContainsKey(map: Map<*, *>) = map.containsKey(Foo())
18+
19+
fun callContainsValue(map: Map<*, *>) = map.containsValue(Foo())
20+
21+
fun callGet(map: Map<*, *>) = map.get(Foo())
22+
23+
fun callGetOrThrowConcurrentModification(map: Map<*, *>) = map.hashCode() // calls getOrThrowConcurrentModification under the hood.
24+
fun callContainsEntry(map: Map<*, *>) = map.entries.contains(map.entries.first())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
4+
*/
5+
6+
import Foundation
7+
import Kt
8+
9+
// See https://youtrack.jetbrains.com/issue/KT-54119/Native-runtime-assertion-failed-due-to-missing-thread-state-switch
10+
11+
private func testSetContains() throws {
12+
try assertFalse(Kt54119Kt.callContains(set: ["111"]))
13+
}
14+
15+
private func testSetGetElement() throws {
16+
try assertNil(Kt54119Kt.callGetElement(set: [222]))
17+
}
18+
19+
private func testMapContainsKey() throws {
20+
try assertFalse(Kt54119Kt.callContainsKey(map: ["abc" : "def"]))
21+
}
22+
23+
private func testMapContainsValue() throws {
24+
try assertFalse(Kt54119Kt.callContainsValue(map: [KT54119KotlinKey() : 1]))
25+
}
26+
27+
private func testMapGet() throws {
28+
try assertNil(Kt54119Kt.callGet(map: [0 : 0]))
29+
}
30+
31+
private func testMapGetOrThrowConcurrentModification() throws {
32+
Kt54119Kt.callGetOrThrowConcurrentModification(map: [KT54119KotlinKey() : 2])
33+
}
34+
35+
private func testMapContainsEntry() throws {
36+
try assertTrue(Kt54119Kt.callContainsEntry(map: [KT54119KotlinKey() : 3]))
37+
}
38+
39+
class Kt54119Tests : SimpleTestProvider {
40+
override init() {
41+
super.init()
42+
43+
test("testSetContains", testSetContains)
44+
test("testSetGetElement", testSetGetElement)
45+
test("testMapContainsKey", testMapContainsKey)
46+
test("testMapContainsValue", testMapContainsValue)
47+
test("testMapGet", testMapGet)
48+
test("testMapGetOrThrowConcurrentModification", testMapGetOrThrowConcurrentModification)
49+
test("testMapContainsEntry", testMapContainsEntry)
50+
}
51+
}

kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm

+29-13
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,21 @@ static inline KInt objCSizeToKotlinOrThrow(NSUInteger size) {
119119
return objCSizeToKotlinOrThrow(set.count);
120120
}
121121

122-
NO_EXTERNAL_CALLS_CHECK
123122
extern "C" KBoolean Kotlin_NSSetAsKSet_contains(KRef thiz, KRef element) {
124123
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
125-
return [set containsObject:refToObjCOrNSNull(element)];
124+
id objCElement = refToObjCOrNSNull(element);
125+
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
126+
return [set containsObject:objCElement];
126127
}
127128

128-
NO_EXTERNAL_CALLS_CHECK
129129
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_getElement, KRef thiz, KRef element) {
130130
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
131-
id res = [set member:refToObjCOrNSNull(element)];
131+
id objCElement = refToObjCOrNSNull(element);
132+
id res;
133+
{
134+
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
135+
res = [set member:objCElement];
136+
}
132137
RETURN_RESULT_OF(refFromObjCOrNSNull, res);
133138
}
134139

@@ -148,16 +153,17 @@ static inline OBJ_GETTER(CreateKIteratorFromNSEnumerator, NSEnumerator* enumerat
148153
return objCSizeToKotlinOrThrow(dict.count);
149154
}
150155

151-
NO_EXTERNAL_CALLS_CHECK
152156
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsKey(KRef thiz, KRef key) {
153157
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
154-
return [dict objectForKey:refToObjCOrNSNull(key)] != nullptr;
158+
id objCKey = refToObjCOrNSNull(key);
159+
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
160+
return [dict objectForKey:objCKey] != nullptr;
155161
}
156162

157-
NO_EXTERNAL_CALLS_CHECK
158163
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsValue(KRef thiz, KRef value) {
159164
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
160165
id objCValue = refToObjCOrNSNull(value);
166+
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
161167
for (id key in dict) {
162168
if ([[dict objectForKey:key] isEqual:objCValue]) {
163169
return true;
@@ -167,28 +173,38 @@ static inline OBJ_GETTER(CreateKIteratorFromNSEnumerator, NSEnumerator* enumerat
167173
return false;
168174
}
169175

170-
NO_EXTERNAL_CALLS_CHECK
171176
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_get, KRef thiz, KRef key) {
172177
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
173-
id value = [dict objectForKey:refToObjCOrNSNull(key)];
178+
id objCKey = refToObjCOrNSNull(key);
179+
id value;
180+
{
181+
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
182+
value = [dict objectForKey:objCKey];
183+
}
174184
RETURN_RESULT_OF(refFromObjCOrNSNull, value);
175185
}
176186

177-
NO_EXTERNAL_CALLS_CHECK
178187
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_getOrThrowConcurrentModification, KRef thiz, KRef key) {
179188
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
180-
id value = [dict objectForKey:refToObjCOrNSNull(key)];
189+
id objCKey = refToObjCOrNSNull(key);
190+
id value;
191+
{
192+
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
193+
value = [dict objectForKey:objCKey];
194+
}
181195
if (value == nullptr) {
182196
Kotlin_ObjCExport_ThrowCollectionConcurrentModification();
183197
}
184198

185199
RETURN_RESULT_OF(refFromObjCOrNSNull, value);
186200
}
187201

188-
NO_EXTERNAL_CALLS_CHECK
189202
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsEntry(KRef thiz, KRef key, KRef value) {
190203
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
191-
return [refToObjCOrNSNull(value) isEqual:[dict objectForKey:refToObjCOrNSNull(key)]];
204+
id objCValue = refToObjCOrNSNull(value);
205+
id objCKey = refToObjCOrNSNull(key);
206+
kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative);
207+
return [objCValue isEqual:[dict objectForKey:objCKey]];
192208
}
193209

194210
NO_EXTERNAL_CALLS_CHECK

0 commit comments

Comments
 (0)