diff options
Diffstat (limited to 'lib/system/excpt.nim')
-rw-r--r-- | lib/system/excpt.nim | 184 |
1 files changed, 108 insertions, 76 deletions
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index aec25e3f3..dae5c4a4a 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -73,26 +73,45 @@ type when NimStackTraceMsgs: var frameMsgBuf* {.threadvar.}: string + +when not defined(nimV2): + var + framePtr {.threadvar.}: PFrame + var - framePtr {.threadvar.}: PFrame - excHandler {.threadvar.}: PSafePoint - # list of exception handlers - # a global variable for the root of all try blocks currException {.threadvar.}: ref Exception - gcFramePtr {.threadvar.}: GcFrame -type - FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame, - excHandler: PSafePoint, currException: ref Exception] +when not gotoBasedExceptions: + var + excHandler {.threadvar.}: PSafePoint + # list of exception handlers + # a global variable for the root of all try blocks + gcFramePtr {.threadvar.}: GcFrame + +when gotoBasedExceptions: + type + FrameState = tuple[framePtr: PFrame, + currException: ref Exception] +else: + type + FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame, + excHandler: PSafePoint, currException: ref Exception] proc getFrameState*(): FrameState {.compilerRtl, inl.} = - return (gcFramePtr, framePtr, excHandler, currException) + when gotoBasedExceptions: + return (framePtr, currException) + else: + return (gcFramePtr, framePtr, excHandler, currException) proc setFrameState*(state: FrameState) {.compilerRtl, inl.} = - gcFramePtr = state.gcFramePtr - framePtr = state.framePtr - excHandler = state.excHandler - currException = state.currException + when gotoBasedExceptions: + framePtr = state.framePtr + currException = state.currException + else: + gcFramePtr = state.gcFramePtr + framePtr = state.framePtr + excHandler = state.excHandler + currException = state.currException proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr @@ -114,20 +133,21 @@ when false: proc setFrame*(s: PFrame) {.compilerRtl, inl.} = framePtr = s -proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr -proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev -proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s -proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} = - s.prev = gcFramePtr - zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer)) - gcFramePtr = s +when not gotoBasedExceptions: + proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr + proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev + proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s + proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} = + s.prev = gcFramePtr + zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer)) + gcFramePtr = s -proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = - s.prev = excHandler - excHandler = s + proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = + s.prev = excHandler + excHandler = s -proc popSafePoint {.compilerRtl, inl.} = - excHandler = excHandler.prev + proc popSafePoint {.compilerRtl, inl.} = + excHandler = excHandler.prev proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} = e.up = currException @@ -146,7 +166,7 @@ proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = # some platforms have native support for stack traces: const - nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and + nativeStackTraceSupported = (defined(macosx) or defined(linux)) and not NimStackTrace hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or (defined(nativeStackTrace) and nativeStackTraceSupported) @@ -242,7 +262,7 @@ template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) = for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') add(s, f.procname) when NimStackTraceMsgs: - when type(f) is StackTraceEntry: + when typeof(f) is StackTraceEntry: add(s, f.frameMsg) else: var first = if f.prev == nil: 0 else: f.prev.frameMsgLen @@ -351,10 +371,10 @@ var onUnhandledException*: (proc (errorMsg: string) {. nimcall, gcsafe.}) ## Set this error \ ## handler to override the existing behaviour on an unhandled exception. ## - ## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``. + ## The default is to write a stacktrace to `stderr` and then call `quit(1)`. ## Unstable API. -proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = +proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = when hasSomeStackTrace: var buf = newStringOfCap(2000) if e.trace.len == 0: @@ -362,7 +382,8 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = else: var trace = $e.trace add(buf, trace) - `=destroy`(trace) + {.gcsafe.}: + `=destroy`(trace) add(buf, "Error: unhandled exception: ") add(buf, e.msg) add(buf, " [") @@ -373,12 +394,13 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = onUnhandledException(buf) else: showErrorMessage2(buf) - `=destroy`(buf) + {.gcsafe.}: + `=destroy`(buf) else: # ugly, but avoids heap allocations :-) template xadd(buf, s, slen) = if L + slen < high(buf): - copyMem(addr(buf[L]), cstring(s), slen) + copyMem(addr(buf[L]), (when s is cstring: s else: cstring(s)), slen) inc L, slen template add(buf, s) = xadd(buf, s, s.len) @@ -387,39 +409,34 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = if e.trace.len != 0: var trace = $e.trace add(buf, trace) - `=destroy`(trace) + {.gcsafe.}: + `=destroy`(trace) add(buf, "Error: unhandled exception: ") add(buf, e.msg) add(buf, " [") xadd(buf, e.name, e.name.len) add(buf, "]\n") - when defined(nimNoArrayToCstringConversion): - template tbuf(): untyped = addr buf - else: - template tbuf(): untyped = buf - if onUnhandledException != nil: - onUnhandledException($tbuf()) + onUnhandledException($cast[cstring](buf.addr)) else: - showErrorMessage(tbuf(), L) + showErrorMessage(cast[cstring](buf.addr), L) -proc reportUnhandledError(e: ref Exception) {.nodestroy.} = +proc reportUnhandledError(e: ref Exception) {.nodestroy, gcsafe.} = if unhandledExceptionHook != nil: unhandledExceptionHook(e) when hostOS != "any": reportUnhandledErrorAux(e) - else: - discard () -proc nimLeaveFinally() {.compilerRtl.} = - when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions: - {.emit: "throw;".} - else: - if excHandler != nil: - c_longjmp(excHandler.context, 1) +when not gotoBasedExceptions: + proc nimLeaveFinally() {.compilerRtl.} = + when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions: + {.emit: "throw;".} else: - reportUnhandledError(currException) - quit(1) + if excHandler != nil: + c_longjmp(excHandler.context, 1) + else: + reportUnhandledError(currException) + rawQuit(1) when gotoBasedExceptions: var nimInErrorMode {.threadvar.}: bool @@ -428,19 +445,19 @@ when gotoBasedExceptions: result = addr(nimInErrorMode) proc nimTestErrorFlag() {.compilerRtl.} = - ## This proc must be called before ``currException`` is destroyed. + ## This proc must be called before `currException` is destroyed. ## It also must be called at the end of every thread to ensure no ## error is swallowed. if nimInErrorMode and currException != nil: reportUnhandledError(currException) currException = nil - quit(1) + rawQuit(1) proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = when defined(nimPanics): if e of Defect: reportUnhandledError(e) - quit(1) + rawQuit(1) if localRaiseHook != nil: if not localRaiseHook(e): return @@ -451,11 +468,9 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = {.emit: "throw;".} else: pushCurrentException(e) - {.emit: "throw e;".} - elif defined(nimQuirky) or gotoBasedExceptions: - # XXX This check should likely also be done in the setjmp case below. - if e != currException: - pushCurrentException(e) + {.emit: "throw `e`;".} + elif quirkyExceptions or gotoBasedExceptions: + pushCurrentException(e) when gotoBasedExceptions: inc nimInErrorMode else: @@ -464,7 +479,7 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = c_longjmp(excHandler.context, 1) else: reportUnhandledError(e) - quit(1) + rawQuit(1) proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring, line: int) {.compilerRtl, nodestroy.} = @@ -507,7 +522,7 @@ proc threadTrouble() = if currException != nil: reportUnhandledError(currException) except: discard - quit 1 + rawQuit 1 proc writeStackTrace() = when hasSomeStackTrace: @@ -530,13 +545,10 @@ proc getStackTrace(e: ref Exception): string = else: result = "" -proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = - ## Returns the attached stack trace to the exception ``e`` as - ## a ``seq``. This is not yet available for the JS backend. - when not defined(nimSeqsV2): - shallowCopy(result, e.trace) - else: - result = move(e.trace) +proc getStackTraceEntries*(e: ref Exception): lent seq[StackTraceEntry] = + ## Returns the attached stack trace to the exception `e` as + ## a `seq`. This is not yet available for the JS backend. + e.trace proc getStackTraceEntries*(): seq[StackTraceEntry] = ## Returns the stack trace entries for the current stack trace. @@ -553,7 +565,7 @@ proc callDepthLimitReached() {.noinline.} = "-d:nimCallDepthLimit=<int> but really try to avoid deep " & "recursions instead.\n" showErrorMessage2(msg) - quit(1) + rawQuit(1) proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = if framePtr == nil: @@ -569,12 +581,12 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = when defined(cpp) and appType != "lib" and not gotoBasedExceptions and not defined(js) and not defined(nimscript) and hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions) and - not defined(nimQuirky): + not quirkyExceptions: type StdException {.importcpp: "std::exception", header: "<exception>".} = object - proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())".} + proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())", nodecl.} proc setTerminate(handler: proc() {.noconv.}) {.importc: "std::set_terminate", header: "<exception>".} @@ -606,9 +618,12 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and else: writeToStdErr msg & "\n" - quit 1 + rawQuit 1 when not defined(noSignalHandler) and not defined(useNimRtl): + type Sighandler = proc (a: cint) {.noconv, benign.} + # xxx factor with ansi_c.CSighandlerT, posix.Sighandler + proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} = template processSignal(s, action: untyped) {.dirty.} = if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") @@ -647,9 +662,23 @@ when not defined(noSignalHandler) and not defined(useNimRtl): # unless there's a good reason to use cstring in signal handler to avoid # using gc? showErrorMessage(msg, msg.len) - quit(1) # always quit when SIGABRT + + when defined(posix): + # reset the signal handler to OS default + c_signal(sign, SIG_DFL) + + # re-raise the signal, which will arrive once this handler exit. + # this lets the OS perform actions like core dumping and will + # also return the correct exit code to the shell. + discard c_raise(sign) + else: + rawQuit(1) + + var SIG_IGN {.importc: "SIG_IGN", header: "<signal.h>".}: Sighandler proc registerSignalHandler() = + # xxx `signal` is deprecated and has many caveats, we should use `sigaction` instead, e.g. + # https://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal c_signal(SIGINT, signalHandler) c_signal(SIGSEGV, signalHandler) c_signal(SIGABRT, signalHandler) @@ -658,14 +687,17 @@ when not defined(noSignalHandler) and not defined(useNimRtl): when declared(SIGBUS): c_signal(SIGBUS, signalHandler) when declared(SIGPIPE): - c_signal(SIGPIPE, signalHandler) + when defined(nimLegacySigpipeHandler): + c_signal(SIGPIPE, signalHandler) + else: + c_signal(SIGPIPE, SIG_IGN) registerSignalHandler() # call it in initialization section proc setControlCHook(hook: proc () {.noconv.}) = # ugly cast, but should work on all architectures: - type SignalHandler = proc (sign: cint) {.noconv, benign.} - c_signal(SIGINT, cast[SignalHandler](hook)) + when declared(Sighandler): + c_signal(SIGINT, cast[Sighandler](hook)) when not defined(noSignalHandler) and not defined(useNimRtl): proc unsetControlCHook() = |