From 2d0dbf9729e40100a3212686099bf46c9370c393 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Wed, 7 Jul 2021 13:53:27 +0000 Subject: [PATCH] Native: improve ObjCExport thread state switching --- .../src/main/cpp/ObjCExportCollectionUtils.mm | 27 +++++++++++++++++ .../runtime/src/main/cpp/ObjCExportErrors.mm | 22 ++++++++++---- .../runtime/src/mm/cpp/CallsChecker.cpp | 30 ------------------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm b/kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm index b1f5a7e8e72..621270952ca 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExportCollectionUtils.mm @@ -53,22 +53,35 @@ static inline KInt objCSizeToKotlinOrThrow(NSUInteger size) { // Exported to ObjCExportUtils.kt: +// Note: a lot of functions below call Objective-C virtual methods of arbitrary classes +// in Runnable thread state. Some these functions are marked with NO_EXTERNAL_CALLS_CHECK. +// On the one hand, these calls aren't guaranteed to be fast, and also can call back to Kotlin. +// Anything of this is harmful in Runnable state. +// On the other hand, anything of this is unlikely to actually happen, and switching thread state +// to Native might kill the performance, because these functions are just tiny wrappers for tiny functions. +// So let's just ignore this for now, and deal with it later. +// TODO: come up with a proper solution. + +NO_EXTERNAL_CALLS_CHECK extern "C" KInt Kotlin_NSArrayAsKList_getSize(KRef obj) { NSArray* array = (NSArray*) GetAssociatedObject(obj); return objCSizeToKotlinOrThrow([array count]); } +NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSArrayAsKList_get, KRef obj, KInt index) { NSArray* array = (NSArray*) GetAssociatedObject(obj); id element = [array objectAtIndex:index]; RETURN_RESULT_OF(refFromObjCOrNSNull, element); } +NO_EXTERNAL_CALLS_CHECK extern "C" void Kotlin_NSMutableArrayAsKMutableList_add(KRef thiz, KInt index, KRef element) { NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz); [mutableArray insertObject:refToObjCOrNSNull(element) atIndex:index]; } +NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_removeAt, KRef thiz, KInt index) { NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz); @@ -78,6 +91,7 @@ extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_removeAt, KRef thiz, K return res; } +NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_set, KRef thiz, KInt index, KRef element) { NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz); @@ -87,6 +101,7 @@ extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_set, KRef thiz, KInt i return res; } +NO_EXTERNAL_CALLS_CHECK extern "C" void Kotlin_NSEnumeratorAsKIterator_computeNext(KRef thiz) { NSEnumerator* enumerator = (NSEnumerator*) GetAssociatedObject(thiz); id next = [enumerator nextObject]; @@ -98,16 +113,19 @@ extern "C" void Kotlin_NSEnumeratorAsKIterator_computeNext(KRef thiz) { } } +NO_EXTERNAL_CALLS_CHECK extern "C" KInt Kotlin_NSSetAsKSet_getSize(KRef thiz) { NSSet* set = (NSSet*) GetAssociatedObject(thiz); 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)]; } +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)]; @@ -118,21 +136,25 @@ static inline OBJ_GETTER(CreateKIteratorFromNSEnumerator, NSEnumerator* enumerat RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSEnumeratorAsKIterator_create, objc_retain(enumerator)); } +NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_iterator, KRef thiz) { NSSet* set = (NSSet*) GetAssociatedObject(thiz); RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [set objectEnumerator]); } +NO_EXTERNAL_CALLS_CHECK extern "C" KInt Kotlin_NSDictionaryAsKMap_getSize(KRef thiz) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); 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; } +NO_EXTERNAL_CALLS_CHECK extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsValue(KRef thiz, KRef value) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); id objCValue = refToObjCOrNSNull(value); @@ -145,12 +167,14 @@ extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsValue(KRef thiz, KRef valu 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)]; 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)]; @@ -161,16 +185,19 @@ extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_getOrThrowConcurrentModification 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)]]; } +NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_keyIterator, KRef thiz) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [dict keyEnumerator]); } +NO_EXTERNAL_CALLS_CHECK extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_valueIterator, KRef thiz) { NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz); RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [dict objectEnumerator]); diff --git a/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm b/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm index 2d7c364d66b..e7fb2330e0f 100644 --- a/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm +++ b/kotlin-native/runtime/src/main/cpp/ObjCExportErrors.mm @@ -74,13 +74,18 @@ extern "C" id Kotlin_ObjCExport_ExceptionAsNSError(KRef exception, const TypeInf } extern "C" id Kotlin_ObjCExport_WrapExceptionToNSError(KRef exception) { - NSMutableDictionary* userInfo = [[NSMutableDictionary new] autorelease]; - userInfo[@"KotlinException"] = Kotlin_ObjCExport_refToObjC(exception); - userInfo[@"KotlinExceptionOrigin"] = @(&kotlinExceptionOriginChar); // Support for different Kotlin runtimes loaded. - ObjHolder messageHolder; KRef message = Kotlin_Throwable_getMessage(exception, messageHolder.slot()); NSString* description = Kotlin_Interop_CreateNSStringFromKString(message); + + id exceptionObjCRef = Kotlin_ObjCExport_refToObjC(exception); + + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + + NSMutableDictionary* userInfo = [[NSMutableDictionary new] autorelease]; + userInfo[@"KotlinException"] = exceptionObjCRef; + userInfo[@"KotlinExceptionOrigin"] = @(&kotlinExceptionOriginChar); // Support for different Kotlin runtimes loaded. + if (description != nullptr) { userInfo[NSLocalizedDescriptionKey] = description; } @@ -97,9 +102,12 @@ extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsExceptionImpl, KRef message, KR extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsException, id error) { NSString* description; + id wrappedKotlinException = nullptr; NSError* e = (NSError*) error; if (e != nullptr) { + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + auto userInfo = e.userInfo; if (userInfo != nullptr) { id kotlinException = userInfo[@"KotlinException"]; @@ -107,7 +115,7 @@ extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsException, id error) { if (kotlinException != nullptr && kotlinExceptionOrigin != nullptr && [kotlinExceptionOrigin isEqual:@(&kotlinExceptionOriginChar)] ) { - RETURN_RESULT_OF(Kotlin_ObjCExport_refFromObjC, kotlinException); + wrappedKotlinException = kotlinException; } } description = e.localizedDescription; @@ -115,6 +123,10 @@ extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsException, id error) { description = nullptr; } + if (wrappedKotlinException != nullptr) { + RETURN_RESULT_OF(Kotlin_ObjCExport_refFromObjC, wrappedKotlinException); + } + ObjHolder messageHolder, errorHolder; KRef message = Kotlin_Interop_CreateKStringFromNSString(description, messageHolder.slot()); KRef kotlinError = Kotlin_ObjCExport_refFromObjC(error, errorHolder.slot()); // TODO: a simple opaque wrapper would be enough. diff --git a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp index 6203ef62770..3f00630caf4 100644 --- a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp +++ b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp @@ -141,7 +141,6 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "pthread_mutex_unlock", "pthread_self", - "+[NSError errorWithDomain:code:userInfo:]", "+[NSMethodSignature signatureWithObjCTypes:]", "+[NSNull null]", "+[NSObject allocWithZone:]", @@ -152,9 +151,6 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "+[NSString stringWithFormat:]", "+[NSString stringWithUTF8String:]", "+[NSValue valueWithPointer:]", - "-[NSDictionary objectForKeyedSubscript:]", - "-[NSError localizedDescription]", - "-[NSError userInfo]", "-[NSException name]", "-[NSException reason]", "-[NSMethodSignature getArgumentTypeAtIndex:]", @@ -167,11 +163,6 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "-[NSPlaceholderString initWithBytes:length:encoding:]", "-[NSPlaceholderString initWithBytesNoCopy:length:encoding:freeWhenDone:]", "-[NSValue pointerValue]", - "-[__NSArray0 count]", - "-[__NSArrayI count]", - "-[__NSArrayI objectAtIndex:]", - "-[__NSArrayM count]", - "-[__NSArrayM objectAtIndex:]", "-[__NSCFBoolean boolValue]", "-[__NSCFNumber doubleValue]", "-[__NSCFNumber floatValue]", @@ -179,9 +170,6 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "-[__NSCFNumber longLongValue]", "-[__NSCFNumber objCType]", "-[__NSCFString isEqual:]", - "-[__NSDictionaryM setObject:forKeyedSubscript:]", - "-[__NSFrozenDictionaryM objectForKeyedSubscript:]", - "-[__SwiftNativeNSError userInfo]", "CFStringCreateCopy", "CFStringGetCharacters", "CFStringGetLength", @@ -221,24 +209,6 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "protocol_getName", "sel_registerName", - - // @objc Swift._ContiguousArrayStorage.count.getter : Swift.Int - "$ss23_ContiguousArrayStorageC5countSivgTo", - // @objc Swift.__SwiftDeferredNSArray.count.getter : Swift.Int - "$ss22__SwiftDeferredNSArrayC5countSivgTo", - // @objc Swift._ContiguousArrayStorage.objectAt(Swift.Int) -> Swift.Unmanaged - "$ss23_ContiguousArrayStorageC8objectAtys9UnmanagedVyyXlGSiFTo", - // @objc Swift.__SwiftNativeNSArrayWithContiguousStorage.objectAt(Swift.Int) -> Swift.Unmanaged - "$ss41__SwiftNativeNSArrayWithContiguousStorageC8objectAtys9UnmanagedVyyXlGSiFTo", - // @objc Swift.__EmptyDictionarySingleton.count.getter : Swift.Int - "$ss26__EmptyDictionarySingletonC5countSivgTo", - // @objc Swift._SwiftDeferredNSDictionary.count.getter : Swift.Int - "$ss26_SwiftDeferredNSDictionaryC5countSivgTo", - // @objc Swift._SwiftDeferredNSDictionary.keyEnumerator() -> Swift._NSEnumerator - "$ss26_SwiftDeferredNSDictionaryC13keyEnumerators13_NSEnumerator_pyFTo", - // @objc Swift._SwiftDictionaryNSEnumerator.nextObject() -> Swift.AnyObject? - "$ss28_SwiftDictionaryNSEnumeratorC10nextObjectyXlSgyFTo", - "llvm.assume", "llvm.ceil.*", "llvm.copysign.*",