Closed vganin closed 3 years ago
NSException
isn't forwarded through Kotlin frames as intended.
But is there any workaround for now? My case is to wrap foreign library API which throws (which is intended by library designers). I can wrap it completely in ObjC to catch all exceptions but is it the only way now?
Which languages do you expect these wrappers to be called from? Swift, Objective-C, Kotlin?
I'm calling it from Kotlin Common actually in MPP setting.
Since NSException
isn't a Throwable
, I'm trying to write function something like native_objc_try_catch
which will allow to catch NSException
s in Kotlin. And I stumbled upon this problem that NSException
cannot be catched through Kotlin frame even in Objective-C.
There is no workaround available currently. Btw, is this library usable from Swift?
Yes it is.
How do you catch an Objective-C exception when calling this library from Swift?
I have the following helper in objective-c:
// NSException+YXTry.h
#import <Foundation/Foundation.h>
typedef void (^YXTryBlock)(void);
typedef void (^YXCatchBlock)(NSException *exception);
@interface NSException (YXTry)
+ (void)yx_try:(YXTryBlock)tryBlock catch:(YXCatchBlock)catchBlock;
@end
// NSException+YXTry.m
@implementation NSException (YXTry)
+ (void)yx_try:(YXTryBlock)tryBlock catch:(YXCatchBlock)catchBlock {
@try {
if (tryBlock) {
tryBlock();
}
}
@catch (NSException *exception) {
if (catchBlock) {
catchBlock(exception);
}
}
}
@end
And then I use it in Swift like this
NSException.yx_try(
{
// Something
},
catch: {
// Something
}
)
Doesn't this cause memory leaks? Both Objective-C and Swift may leak if an exception is thrown.
I think it does not cause memory leaks if you compile with -fobjc-arc-exceptions
but I may be wrong.
AFAIK, it doesn't affect Swift.
Btw, which library do you use?
Yandex MapKit - C++ library with platform bindings. It can throw unexpectedly so we are trying to wrap it to report all exceptions to some crash-reporting service safely.
It can throw unexpectedly so we are trying to wrap it to report all exceptions to some crash-reporting service safely.
Should the application get terminated after reporting such an exception?
I think it shouldn't.
Any updates on this issue? I am having same problem.
The code:
private fun canEvaluatePolicyWrapper(policy: LAPolicy): Boolean {
val authContext = LAContext()
try {
return authContext.canEvaluatePolicy(policy, null)
} catch(e: Exception) {
print("Error while evaluating policy. The message is: ${e.message}")
return false
}
}
}
Error
2020-08-27 23:56:12.105991+0300 iosSDKDemo[5310:76767] [LAErrorHelper] Error Domain=com.apple.LocalAuthentication Code=-1001 "Unknown policy: '0'" UserInfo={NSLocalizedDescription=Unknown policy: '0'}
2020-08-27 23:56:12.122039+0300 iosSDKDemo[5310:76767] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Error Domain=com.apple.LocalAuthentication Code=-1001 "Unknown policy: '0'" UserInfo={NSLocalizedDescription=Unknown policy: '0'}'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff23e3de6e __exceptionPreprocess + 350
1 libobjc.A.dylib 0x00007fff512539b2 objc_exception_throw + 48
2 CoreFoundation 0x00007fff23e3dcac +[NSException raise:format:] + 188
3 SharedUtils 0x00007fff2775d797 +[LAErrorHelper raiseExceptionOnError:] + 165
4 LocalAuthentication 0x00007fff277327ad -[LAClient evaluatePolicy:options:uiDelegate:reply:] + 240
5 LocalAuthentication 0x00007fff27732e26 -[LAClient evaluatePolicy:options:reply:] + 91
6 LocalAuthentication 0x00007fff27736686 __51-[LAContext _evaluatePolicy:options:log:cid:reply:]_block_invoke + 168
7 libdispatch.dylib 0x000000010acb0e8e _dispatch_client_callout + 8
8 libdispatch.dylib 0x000000010acb3da2 _dispatch_block_invoke_direct + 300
9 libdispatch.dylib 0x000000010acb3c6b dispatch_block_perform + 124
10 LocalAuthentication 0x00007fff2773653a -[LAContext _evaluatePolicy:options:log:cid:reply:] + 470
11 LocalAuthentication 0x00007fff27736cec -[LAContext _evaluatePolicy:options:log:cid:error:] + 262
12 LocalAuthentication 0x00007fff27737b19 -[LAContext canEvaluatePolicy:error:] + 398
13 iosSDKDemo 0x000000010a29645f _416e64726f6964554146313153646b3a536861726564436f6465_knbridge13 + 31
14 iosSDKDemo 0x000000010a23f4cb kfun:uaf.asms.primary.interfaces.Crypto.canEvaluatePolicyWrapper#internal + 955
15 iosSDKDemo 0x000000010a23ddfa kfun:uaf.asms.primary.interfaces.Crypto#getAvailableBiometrics(){}uaf.asms.primary.interfaces.ASMBiometricsInfo + 154
16 iosSDKDemo 0x000000010a1d6973 kfun:uaf.asms.primary.BaseASM.$registerCOROUTINE$4#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 5091
17 iosSDKDemo 0x000000010a1d8eab kfun:uaf.asms.primary.BaseASM#register(kotlin.String;kotlin.String;kotlin.ByteArray){}kotlin.Pair<uaf.interfaces.registry.ASMErrorCodes,uaf.interfaces.uglybugfix.UAFResponseAssertion?> + 475
18 iosSDKDemo 0x000000010a1d0a58 kfun:uaf.Client.$processUAFRequestCOROUTINE$3#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 11256
19 iosSDKDemo 0x000000010a1d3670 kfun:uaf.Client#processUAFRequest(kotlin.String){}kotlin.Pair<kotlin.Int,kotlin.String?> + 320
20 iosSDKDemo 0x000000010a24a2f1 objc2kotlin.34 + 369
21 SwiftUI 0x00007fff2c3f7dc0 $s7SwiftUI33PrimitiveButtonStyleConfigurationV7triggeryyF + 16
22 SwiftUI 0x00007fff2c4f2ca0 $s7SwiftUI33PrimitiveButtonStyleConfigurationV7triggeryyFTA + 16
23 SwiftUI 0x00007fff2c722aa4 $s7SwiftUI25PressableGestureCallbacksV8dispatch5phase5stateyycSgAA0D5PhaseOyxG_SbztFyycfU_TA + 36
24 SwiftUI 0x00007fff2c574acc $sIeg_ytIegr_TR + 12
25 SwiftUI 0x00007fff2c6e4fd1 $sIeg_ytIegr_TRTA + 17
26 SwiftUI 0x00007fff2c6e5e89 $sIeg_ytIegr_TRTA.46 + 9
27 SwiftUI 0x00007fff2c574aec $sytIegr_Ieg_TR + 12
28 SwiftUI 0x00007fff2c574acc $sIeg_ytIegr_TR + 12
29 SwiftUI 0x00007fff2c6e4fd1 $sIeg_ytIegr_TRTA + 17
30 SwiftUI 0x00007fff2c6e5e99 $sIeg_ytIegr_TRTA.54 + 9
31 SwiftUI 0x00007fff2c567a5e $s7SwiftUI6UpdateO15dispatchActionsyyFZ + 414
32 SwiftUI 0x00007fff2c56745a $s7SwiftUI6UpdateO3endyyFZ + 106
33 SwiftUI 0x00007fff2c5a6f5c $s7SwiftUI19EventBindingManagerC4sendyySDyAA0C2IDVAA0C4Type_pGF + 284
34 SwiftUI 0x00007fff2c8bf993 $s7SwiftUI22UIKitGestureRecognizerC4send025_062C14327F4C9197D92807A7H6DF7F3BLL7touches5event5phaseSayAA7EventIDVGShySo7UITouchCG_So7UIEventCAA0Q5PhaseOtF + 67
35 SwiftUI 0x00007fff2c8bfc2c $s7SwiftUI22UIKitGestureRecognizerC12touchesEnded_4withyShySo7UITouchCG_So7UIEventCtF + 12
36 SwiftUI 0x00007fff2c8c0343 $s7SwiftUI22UIKitGestureRecognizerC12touchesBegan_4withyShySo7UITouchCG_So7UIEventCtFToTm + 131
37 SwiftUI 0x00007fff2c8bfc68 $s7SwiftUI22UIKitGestureRecognizerC12touchesEnded_4withyShySo7UITouchCG_So7UIEventCtFTo + 40
38 UIKitCore 0x00007fff48eb8f88 -[UIGestureRecognizer _touchesEnded:withEvent:] + 141
39 UIKitCore 0x00007fff493aea38 -[UITouchesEvent _sendEventToGestureRecognizer:] + 673
40 UIKitCore 0x00007fff48eaccd2 __47-[UIGestureEnvironment _updateForEvent:window:]_block_invoke + 70
41 UIKitCore 0x00007fff48eace04 -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 259
42 UIKitCore 0x00007fff48eacc5c -[UIGestureEnvironment _updateForEvent:window:] + 225
43 UIKitCore 0x00007fff49364277 -[UIWindow sendEvent:] + 4479
44 UIKitCore 0x00007fff4933e6d1 -[UIApplication sendEvent:] + 356
45 UIKit 0x000000010ede60b8 -[UIApplicationAccessibility sendEvent:] + 85
46 UIKitCore 0x00007fff493c94ce __dispatchPreprocessedEventFromEventQueue + 7628
47 UIKitCore 0x00007fff493cc692 __handleEventQueueInternal + 6584
48 UIKitCore 0x00007fff493c2f35 __handleHIDEventFetcherDrain + 88
49 CoreFoundation 0x00007fff23da1c91 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
50 CoreFoundation 0x00007fff23da1bbc __CFRunLoopDoSource0 + 76
51 CoreFoundation 0x00007fff23da1394 __CFRunLoopDoSources0 + 180
52 CoreFoundation 0x00007fff23d9bf8e __CFRunLoopRun + 974
53 CoreFoundation 0x00007fff23d9b8a4 CFRunLoopRunSpecific + 404
54 GraphicsServices 0x00007fff38c39bbe GSEventRunModal + 139
55 UIKitCore 0x00007fff49325968 UIApplicationMain + 1605
56 iosSDKDemo 0x000000010a1c279b main + 75
57 libdyld.dylib 0x00007fff520ce1fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
@herrjemand
Any updates on this issue? I am having same problem.
Yes. Work in progress: https://github.com/JetBrains/kotlin-native/pull/4307
@SvyatoslavScherbina any way I can temporary work around?
@herrjemand
any way I can temporary work around?
Wrapping Objective-C methods that throw exceptions to Objective-C methods or top-level functions with @try-@catch
should help.
Please see #4307
The commit provides opt-in mode for wrapping unhandled NSException
in Kotlin exception object.
HowTo:
Use command line cinterop -Xforeign-exception-mode objc-wrap
or add foreignExceptionMode = objc-wrap
property to .def
file.
The option affects kotlin-native binding to all functions and methods exported from this native module.
Kotlin runtime environment will handle and wrap any NSException
propagating from this native library, i.e. NSException that raised and not caught in native. The wrapper type is ForeignException
, as any other Kotlin exception it will not propagate back to native and must be handled in Kotlin code. The original NSException object is stored as ForeignException.nativeException
property, so it may be accessed inside of ForeignException catch block for advanced use.
Turns out that it is impossible to catch
NSException
s in any code (neither Kotlin nor ObjC) with Kotlin 1.3.50 if it has Kotlin stack frame on the unwinding path.It is impossible to catch it from Kotlin because
NSException
is not aThrowable
.It is impossible to catch it with regular ObjC
@try { ... } @catch { ... }
blocks but I don't know why really. It seems that it should just work unless Kotlin frame breaks stack unwinding for ObjC somehow.Simple example proves it:
It crashes with