Skip to content

Commit

Permalink
Native: improve thread state switches for NSSet/NSDictionary adapters
Browse files Browse the repository at this point in the history
^KT-54119

(cherry picked from commit 39c73e2)
  • Loading branch information
SvyatoslavScherbina committed Sep 22, 2022
1 parent 1abfeb9 commit 1244679
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 13 deletions.
19 changes: 19 additions & 0 deletions kotlin-native/backend.native/tests/objcexport/expectedLazy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<id> *)set __attribute__((swift_name("callContains(set:)")));
+ (id _Nullable)callGetElementSet:(NSSet<id> *)set __attribute__((swift_name("callGetElement(set:)")));
+ (BOOL)callContainsKeyMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsKey(map:)")));
+ (BOOL)callContainsValueMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsValue(map:)")));
+ (id _Nullable)callGetMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGet(map:)")));
+ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)")));
+ (BOOL)callContainsEntryMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsEntry(map:)")));
@end

__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("LibraryKt")))
@interface KtLibraryKt : KtBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<id> *)set __attribute__((swift_name("callContains(set:)")));
+ (id _Nullable)callGetElementSet:(NSSet<id> *)set __attribute__((swift_name("callGetElement(set:)")));
+ (BOOL)callContainsKeyMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsKey(map:)")));
+ (BOOL)callContainsValueMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsValue(map:)")));
+ (id _Nullable)callGetMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGet(map:)")));
+ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)")));
+ (BOOL)callContainsEntryMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsEntry(map:)")));
@end

__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("LibraryKt")))
@interface KtLibraryKt : KtBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<id> *)set __attribute__((swift_name("callContains(set:)")));
+ (id _Nullable)callGetElementSet:(NSSet<id> *)set __attribute__((swift_name("callGetElement(set:)")));
+ (BOOL)callContainsKeyMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsKey(map:)")));
+ (BOOL)callContainsValueMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsValue(map:)")));
+ (id _Nullable)callGetMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGet(map:)")));
+ (int32_t)callGetOrThrowConcurrentModificationMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callGetOrThrowConcurrentModification(map:)")));
+ (BOOL)callContainsEntryMap:(NSDictionary<id, id> *)map __attribute__((swift_name("callContainsEntry(map:)")));
@end

__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("LibraryKt")))
@interface KtLibraryKt : KtBase
Expand Down
24 changes: 24 additions & 0 deletions kotlin-native/backend.native/tests/objcexport/kt54119.kt
Original file line number Diff line number Diff line change
@@ -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<Any?>).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())
51 changes: 51 additions & 0 deletions kotlin-native/backend.native/tests/objcexport/kt54119.swift
Original file line number Diff line number Diff line change
@@ -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)
}
}
42 changes: 29 additions & 13 deletions kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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;
Expand All @@ -167,28 +173,38 @@ 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();
}

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
Expand Down

0 comments on commit 1244679

Please sign in to comment.