Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

objective C exceptions not handled properly #12653

Open
timotheecour opened this issue Nov 14, 2019 · 1 comment
Open

objective C exceptions not handled properly #12653

timotheecour opened this issue Nov 14, 2019 · 1 comment

Comments

@timotheecour
Copy link
Member

timotheecour commented Nov 14, 2019

objective C exceptions not handled properly

D20191113T130903

Example

clang++ -shared -framework Foundation -o $build_D/temp/libt0782b.dylib $timn_D/tests/nim/all/t0782b.mm
nim cpp -r $timn_D/tests/nim/all/t0782.nim
# t0782.nim:
{.passL: "-L$build_D/temp/ -lt0782b".}
proc fun_cpp() {.importc.}
proc fun_objc() {.importc.}

proc main()=
  when defined(case_fun_cpp):
    fun_cpp()
  else:
    fun_objc()

main()
// t0782b.mm:
#include <stdio.h>
#include <exception>
#include <stdexcept>

// #import <Foundation/NSExceptions.h>
#import <Foundation/Foundation.h>

extern "C"{
void fun_objc(){
  printf("in fun1\n");
  NSException* myException = [NSException
          exceptionWithName:@"MyException"
          reason:@"blah blah"
          userInfo:nil];
  @throw myException;
}

void fun_cpp(){
  printf("in fun2\n");
  throw std::runtime_error("bad");
}

}

Current Output

in fun1
Error: unhandled unknown cpp exception

Expected Output

should show stacktrace, exception name, reason, userData (see https://developer.apple.com/documentation/foundation/nsexception)

Additional Information

  • this is because of current implementation of setTerminate in excpt.nim which doesn't handle the objc case.

  • this is especially problematic when such exceptions are thrown in an unsuspecting way, eg if calling a (nim-wrapped) cv::imshow from a non-main-thread, on OSX, it'll throw a objc exception, but nim will hide all useful info and just write Error: unhandled unknown cpp exception instead of smthg like:

2019-11-13 13:07:43.073 smartcam_main[48737:17265707] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSWindow drag regions should only be invalidated on the Main Thread!'
*** First throw call stack:
(
        0   CoreFoundation                      0x00007fff2dc9bf53 __exceptionPreprocess + 250
        1   libobjc.A.dylib                     0x00007fff63d61835 objc_exception_throw + 48
        2   CoreFoundation                      0x00007fff2dcb652c -[NSException raise] + 9
        3   AppKit                              0x00007fff2ae6a6b2 -[NSWindow(NSWindow_Theme) _postWindowNeedsToResetDragMarginsUnlessPostingDisabled] + 310
        4   AppKit                              0x00007fff2ae67bc1 -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1416
        5   AppKit                              0x00007fff2ae67633 -[NSWindow initWithContentRect:styleMask:backing:defer:] + 42
        6   AppKit                              0x00007fff2b0fa0d8 -[NSWindow initWithContentRect:styleMask:backing:defer:screen:] + 52
        7   libopencv_highgui.4.1.dylib         0x000000010e055496 cvNamedWindow + 368
        8   libopencv_highgui.4.1.dylib         0x000000010e054fa4 cvShowImage + 95

when nim's setTerminate isn't called

@timotheecour
Copy link
Member Author

timotheecour commented Nov 14, 2019

I tried adapting https://github.com/wix/Detox/blob/master/detox/ios/Detox/DetoxCrashHandler.mm and it works.

except.nim:

  const timn_objc_handler = defined(timn_objc_handler) # D20191113T130903
  when timn_objc_handler:
    {.passL: "-L$build_D/temp/ -lnimextras_objc".}
  setTerminate proc() {.noconv.} =
    proc terminate_objc_handler(msg: cstring) {.importc: "terminate_objc_handler".}
    # Remove ourself as a handler, reinstalling the default handler.
    setTerminate(nil)
    var msg = "Unknown error in unexpected exception handler"
    try:
      raise
    except Exception:
      msg = currException.getStackTrace() & "Error: unhandled exception: " &
        currException.msg & " [" & $currException.name & "]"
    except StdException as e:
      when timn_objc_handler:
        terminate_objc_handler($e.what())
      msg = "Error: unhandled cpp exception: " & $e.what()
    except:
      when timn_objc_handler:
        terminate_objc_handler("")
      msg = "Error: unhandled unknown cpp exception"

    when defined(genode):
      # stderr not available by default, use the LOG session
      echo msg
    else:
      writeToStdErr msg & "\n"

    quit 1

nimextras_objc.mm:

extern "C"
void terminate_objc_handler(const char* msg){
    // prelude
    @try {
      __cxxabiv1::__cxa_rethrow();
    }
    @catch (id e) { // handle objc exception to print stacktrace, name, userInfo, reason
    } 
    @catch (...) {  // handle C++ exception ; use __cxa_current_exception_type, __cxa_demangle etc
    }
}

clang++ -shared -framework Foundation -o $build_D/temp/libnimextras_objc.dylib $timn_D/src/timn/nimextras_objc.mm

it works but I'm wondering whether there's a simpler way

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants