summary refs log tree commit diff stats
path: root/lib/system/excpt.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system/excpt.nim')
-rw-r--r--[-rwxr-xr-x]lib/system/excpt.nim893
1 files changed, 656 insertions, 237 deletions
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 4d7b41da2..dae5c4a4a 100755..100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -1,286 +1,705 @@
 #
 #
-#            Nimrod's Runtime Library
-#        (c) Copyright 2009 Andreas Rumpf
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
+# Exception handling code. Carefully coded so that tiny programs which do not
+# use the heap (and nor exceptions) do not include the GC or memory allocator.
 
-# Exception handling code. This is difficult because it has
-# to work if there is no more memory. Do not use ``sprintf``, etc. as they are
-# unsafe!
+import std/private/miscdollars
+import stacktraces
 
-when not defined(windows) or not defined(guiapp):
-  proc writeToStdErr(msg: CString) = write(stdout, msg)
+const noStacktraceAvailable = "No stack traceback available\n"
+
+var
+  errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign,
+                                            nimcall.})
+    ## Function that will be called
+    ## instead of `stdmsg.write` when printing stacktrace.
+    ## Unstable API.
+
+when defined(windows):
+  proc GetLastError(): int32 {.header: "<windows.h>", nodecl.}
+  const ERROR_BAD_EXE_FORMAT = 193
 
+when not defined(windows) or not defined(guiapp):
+  proc writeToStdErr(msg: cstring) = rawWrite(cstderr, msg)
+  proc writeToStdErr(msg: cstring, length: int) =
+    rawWriteString(cstderr, msg, length)
 else:
-  proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
+  proc MessageBoxA(hWnd: pointer, lpText, lpCaption: cstring, uType: int): int32 {.
     header: "<windows.h>", nodecl.}
+  proc writeToStdErr(msg: cstring) =
+    discard MessageBoxA(nil, msg, nil, 0)
+  proc writeToStdErr(msg: cstring, length: int) =
+    discard MessageBoxA(nil, msg, nil, 0)
+
+proc writeToStdErr(msg: string) {.inline.} =
+  # fix bug #13115: handles correctly '\0' unlike default implicit conversion to cstring
+  writeToStdErr(msg.cstring, msg.len)
+
+proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} =
+  var toWrite = true
+  if errorMessageWriter != nil:
+    try:
+      errorMessageWriter($data)
+      toWrite = false
+    except:
+      discard
+  if toWrite:
+    when defined(genode):
+      # stderr not available by default, use the LOG session
+      echo data
+    else:
+      writeToStdErr(data, length)
 
-  proc writeToStdErr(msg: CString) =
-    discard MessageBoxA(0, msg, nil, 0)
+proc showErrorMessage2(data: string) {.inline.} =
+  showErrorMessage(data.cstring, data.len)
 
-proc raiseException(e: ref E_Base, ename: CString) {.compilerproc.}
-proc reraiseException() {.compilerproc.}
+proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
+proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
+proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
+proc chckNil(p: pointer) {.noinline, compilerproc, benign.}
 
-proc registerSignalHandler() {.compilerproc.}
+type
+  GcFrame = ptr GcFrameHeader
+  GcFrameHeader {.compilerproc.} = object
+    len: int
+    prev: ptr GcFrameHeader
 
-proc chckIndx(i, a, b: int): int {.inline, compilerproc.}
-proc chckRange(i, a, b: int): int {.inline, compilerproc.}
-proc chckRangeF(x, a, b: float): float {.inline, compilerproc.}
-proc chckNil(p: pointer) {.inline, compilerproc.}
+when NimStackTraceMsgs:
+  var frameMsgBuf* {.threadvar.}: string
 
-type
-  PSafePoint = ptr TSafePoint
-  TSafePoint {.compilerproc, final.} = object
-    prev: PSafePoint # points to next safe point ON THE STACK
-    exc: ref E_Base
-    status: int
-    context: C_JmpBuf
+when not defined(nimV2):
+  var
+    framePtr {.threadvar.}: PFrame
 
 var
-  excHandler {.compilerproc.}: PSafePoint = nil
-    # list of exception handlers
-    # a global variable for the root of all try blocks
+  currException {.threadvar.}: 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 reraiseException() =
-  if excHandler == nil:
-    raise newException(ENoExceptionToReraise, "no exception to reraise")
+proc getFrameState*(): FrameState {.compilerRtl, inl.} =
+  when gotoBasedExceptions:
+    return (framePtr, currException)
   else:
-    c_longjmp(excHandler.context, 1)
+    return (gcFramePtr, framePtr, excHandler, currException)
 
-type
-  PFrame = ptr TFrame
-  TFrame {.importc, nodecl, final.} = object
-    prev: PFrame
-    procname: CString
-    line: int # current line number
-    filename: CString
-    len: int  # length of slots (when not debugging always zero)
+proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
+  when gotoBasedExceptions:
+    framePtr = state.framePtr
+    currException = state.currException
+  else:
+    gcFramePtr = state.gcFramePtr
+    framePtr = state.framePtr
+    excHandler = state.excHandler
+    currException = state.currException
 
-var
-  buf: string       # cannot be allocated on the stack!
-  assertBuf: string # we need a different buffer for
-                    # assert, as it raises an exception and
-                    # exception handler needs the buffer too
+proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr
+
+proc popFrame {.compilerRtl, inl.} =
+  framePtr = framePtr.prev
 
-  framePtr {.exportc.}: PFrame
+when false:
+  proc popFrameOfAddr(s: PFrame) {.compilerRtl.} =
+    var it = framePtr
+    if it == s:
+      framePtr = framePtr.prev
+    else:
+      while it != nil:
+        if it == s:
+          framePtr = it.prev
+          break
+        it = it.prev
+
+proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
+  framePtr = 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 popSafePoint {.compilerRtl, inl.} =
+    excHandler = excHandler.prev
+
+proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} =
+  e.up = currException
+  currException = e
+  #showErrorMessage2 "A"
+
+proc popCurrentException {.compilerRtl, inl.} =
+  currException = currException.up
+  #showErrorMessage2 "B"
+
+proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
+  discard "only for bootstrapping compatbility"
+
+proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
+  currException = e
+
+# some platforms have native support for stack traces:
+const
+  nativeStackTraceSupported = (defined(macosx) or defined(linux)) and
+                              not NimStackTrace
+  hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or
+    (defined(nativeStackTrace) and nativeStackTraceSupported)
+
+
+when defined(nativeStacktrace) and nativeStackTraceSupported:
+  type
+    TDl_info {.importc: "Dl_info", header: "<dlfcn.h>",
+               final, pure.} = object
+      dli_fname: cstring
+      dli_fbase: pointer
+      dli_sname: cstring
+      dli_saddr: pointer
+
+  proc backtrace(symbols: ptr pointer, size: int): int {.
+    importc: "backtrace", header: "<execinfo.h>".}
+  proc dladdr(addr1: pointer, info: ptr TDl_info): int {.
+    importc: "dladdr", header: "<dlfcn.h>".}
+
+  when not hasThreadSupport:
+    var
+      tempAddresses: array[maxStackTraceLines, pointer] # should not be alloc'd on stack
+      tempDlInfo: TDl_info
+
+  proc auxWriteStackTraceWithBacktrace(s: var string) =
+    when hasThreadSupport:
+      var
+        tempAddresses: array[maxStackTraceLines, pointer] # but better than a threadvar
+        tempDlInfo: TDl_info
+    # This is allowed to be expensive since it only happens during crashes
+    # (but this way you don't need manual stack tracing)
+    var size = backtrace(cast[ptr pointer](addr(tempAddresses)),
+                         len(tempAddresses))
+    var enabled = false
+    for i in 0..size-1:
+      var dlresult = dladdr(tempAddresses[i], addr(tempDlInfo))
+      if enabled:
+        if dlresult != 0:
+          var oldLen = s.len
+          add(s, tempDlInfo.dli_fname)
+          if tempDlInfo.dli_sname != nil:
+            for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
+            add(s, tempDlInfo.dli_sname)
+        else:
+          add(s, '?')
+        add(s, "\n")
+      else:
+        if dlresult != 0 and tempDlInfo.dli_sname != nil and
+            c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
+          # Once we're past signalHandler, we're at what the user is
+          # interested in
+          enabled = true
+
+when hasSomeStackTrace and not hasThreadSupport:
+  var
+    tempFrames: array[maxStackTraceLines, PFrame] # should not be alloc'd on stack
 
-  tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
-  
-  stackTraceNewLine* = "\n" ## undocumented feature
+template reraisedFrom(z): untyped =
+  StackTraceEntry(procname: nil, line: z, filename: nil)
 
-proc auxWriteStackTrace(f: PFrame, s: var string) =
-  const 
-    firstCalls = 32
+proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
   var
     it = f
     i = 0
-    total = 0
-  while it != nil and i <= high(tempFrames)-(firstCalls-1):
-    # the (-1) is for a nil entry that marks where the '...' should occur
-    tempFrames[i] = it
+  while it != nil:
     inc(i)
-    inc(total)
     it = it.prev
-  var b = it
+  var last = i-1
+  when true: # not defined(gcDestructors):
+    if s.len == 0:
+      s = newSeq[StackTraceEntry](i)
+    else:
+      last = s.len + i - 1
+      s.setLen(last+1)
+  it = f
   while it != nil:
-    inc(total)
+    s[last] = StackTraceEntry(procname: it.procname,
+                              line: it.line,
+                              filename: it.filename)
+    when NimStackTraceMsgs:
+      let first = if it.prev == nil: 0 else: it.prev.frameMsgLen
+      if it.frameMsgLen > first:
+        s[last].frameMsg.setLen(it.frameMsgLen - first)
+        # somehow string slicing not available here
+        for i in first .. it.frameMsgLen-1:
+          s[last].frameMsg[i-first] = frameMsgBuf[i]
     it = it.prev
-  for j in 1..total-i-(firstCalls-1): 
-    if b != nil: b = b.prev
-  if total != i:
-    tempFrames[i] = nil
-    inc(i)
-  while b != nil and i <= high(tempFrames):
-    tempFrames[i] = b
-    inc(i)
-    b = b.prev
-  for j in countdown(i-1, 0):
-    if tempFrames[j] == nil: 
-      add(s, "(")
-      add(s, $(total-i-1))
-      add(s, " calls omitted) ...")
+    dec last
+
+template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) =
+  var oldLen = s.len
+  s.toLocation(f.filename, f.line, 0)
+  for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
+  add(s, f.procname)
+  when NimStackTraceMsgs:
+    when typeof(f) is StackTraceEntry:
+      add(s, f.frameMsg)
     else:
-      add(s, $tempFrames[j].procname)
-      if tempFrames[j].line > 0:
-        add(s, ", line: ")
-        add(s, $tempFrames[j].line)
-    add(s, stackTraceNewLine)
+      var first = if f.prev == nil: 0 else: f.prev.frameMsgLen
+      for i in first..<f.frameMsgLen: add(s, frameMsgBuf[i])
+  add(s, "\n")
 
-proc rawWriteStackTrace(s: var string) =
-  if framePtr == nil:
-    add(s, "No stack traceback available")
-    add(s, stackTraceNewLine)
+proc `$`(stackTraceEntries: seq[StackTraceEntry]): string =
+  when defined(nimStackTraceOverride):
+    let s = addDebuggingInfo(stackTraceEntries)
   else:
-    add(s, "Traceback (most recent call last)")
-    add(s, stackTraceNewLine)
-    auxWriteStackTrace(framePtr, s)
+    let s = stackTraceEntries
+
+  result = newStringOfCap(2000)
+  for i in 0 .. s.len-1:
+    if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
+    elif s[i].line == reraisedFromEnd: result.add "]]\n"
+    else: addFrameEntry(result, s[i])
+
+when hasSomeStackTrace:
+
+  proc auxWriteStackTrace(f: PFrame, s: var string) =
+    when hasThreadSupport:
+      var
+        tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar
+    const
+      firstCalls = 32
+    var
+      it = f
+      i = 0
+      total = 0
+    # setup long head:
+    while it != nil and i <= high(tempFrames)-firstCalls:
+      tempFrames[i] = it
+      inc(i)
+      inc(total)
+      it = it.prev
+    # go up the stack to count 'total':
+    var b = it
+    while it != nil:
+      inc(total)
+      it = it.prev
+    var skipped = 0
+    if total > len(tempFrames):
+      # skip N
+      skipped = total-i-firstCalls+1
+      for j in 1..skipped:
+        if b != nil: b = b.prev
+      # create '...' entry:
+      tempFrames[i] = nil
+      inc(i)
+    # setup short tail:
+    while b != nil and i <= high(tempFrames):
+      tempFrames[i] = b
+      inc(i)
+      b = b.prev
+    for j in countdown(i-1, 0):
+      if tempFrames[j] == nil:
+        add(s, "(")
+        add(s, $skipped)
+        add(s, " calls omitted) ...\n")
+      else:
+        addFrameEntry(s, tempFrames[j])
 
-proc quitOrDebug() {.inline.} =
-  when not defined(endb):
-    quit(1)
-  else:
-    endbStep() # call the debugger
-
-proc raiseException(e: ref E_Base, ename: CString) =
-  GC_disable() # a bad thing is an error in the GC while raising an exception
-  e.name = ename
-  if excHandler != nil:
-    excHandler.exc = e
-    c_longjmp(excHandler.context, 1)
-  else:
-    if not isNil(buf):
-      setLen(buf, 0)
-      rawWriteStackTrace(buf)
-      if e.msg != nil and e.msg[0] != '\0':
-        add(buf, "Error: unhandled exception: ")
-        add(buf, $e.msg)
+  proc stackTraceAvailable*(): bool
+
+  proc rawWriteStackTrace(s: var string) =
+    when defined(nimStackTraceOverride):
+      add(s, "Traceback (most recent call last, using override)\n")
+      auxWriteStackTraceWithOverride(s)
+    elif NimStackTrace:
+      if framePtr == nil:
+        add(s, noStacktraceAvailable)
       else:
-        add(buf, "Error: unhandled exception")
-      add(buf, " [")
-      add(buf, $ename)
-      add(buf, "]\n")
-      writeToStdErr(buf)
+        add(s, "Traceback (most recent call last)\n")
+        auxWriteStackTrace(framePtr, s)
+    elif defined(nativeStackTrace) and nativeStackTraceSupported:
+      add(s, "Traceback from system (most recent call last)\n")
+      auxWriteStackTraceWithBacktrace(s)
     else:
-      writeToStdErr(ename)
-    quitOrDebug()
-  GC_enable()
+      add(s, noStacktraceAvailable)
 
-var
-  gAssertionFailed: ref EAssertionFailed
-
-proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
-  if not cond:
-    #c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
-    #quit(1)
-    GC_disable() # BUGFIX: `$` allocates a new string object!
-    if not isNil(assertBuf):
-      # BUGFIX: when debugging the GC, assertBuf may be nil
-      setLen(assertBuf, 0)
-      add(assertBuf, "[Assertion failure] file: ")
-      add(assertBuf, file)
-      add(assertBuf, " line: ")
-      add(assertBuf, $line)
-      add(assertBuf, "\n")
-      gAssertionFailed.msg = assertBuf
-    GC_enable()
-    if gAssertionFailed != nil:
-      raise gAssertionFailed
+  proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
+    when defined(nimStackTraceOverride):
+      auxWriteStackTraceWithOverride(s)
+    elif NimStackTrace:
+      auxWriteStackTrace(framePtr, s)
     else:
-      c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
-      quit(1)
-
-proc WriteStackTrace() =
-  var s = ""
-  rawWriteStackTrace(s)
-  writeToStdErr(s)
-
-#proc stackTraceWrapper {.noconv.} =
-#  writeStackTrace()
+      s = @[]
+
+  proc stackTraceAvailable(): bool =
+    when defined(nimStackTraceOverride):
+      result = true
+    elif NimStackTrace:
+      if framePtr == nil:
+        result = false
+      else:
+        result = true
+    elif defined(nativeStackTrace) and nativeStackTraceSupported:
+      result = true
+    else:
+      result = false
+else:
+  proc stackTraceAvailable*(): bool = result = false
+
+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)`.
+  ## Unstable API.
+
+proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
+  when hasSomeStackTrace:
+    var buf = newStringOfCap(2000)
+    if e.trace.len == 0:
+      rawWriteStackTrace(buf)
+    else:
+      var trace = $e.trace
+      add(buf, trace)
+      {.gcsafe.}:
+        `=destroy`(trace)
+    add(buf, "Error: unhandled exception: ")
+    add(buf, e.msg)
+    add(buf, " [")
+    add(buf, $e.name)
+    add(buf, "]\n")
+
+    if onUnhandledException != nil:
+      onUnhandledException(buf)
+    else:
+      showErrorMessage2(buf)
+    {.gcsafe.}:
+      `=destroy`(buf)
+  else:
+    # ugly, but avoids heap allocations :-)
+    template xadd(buf, s, slen) =
+      if L + slen < high(buf):
+        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)
+    var buf: array[0..2000, char]
+    var L = 0
+    if e.trace.len != 0:
+      var trace = $e.trace
+      add(buf, 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")
+    if onUnhandledException != nil:
+      onUnhandledException($cast[cstring](buf.addr))
+    else:
+      showErrorMessage(cast[cstring](buf.addr), L)
+
+proc reportUnhandledError(e: ref Exception) {.nodestroy, gcsafe.} =
+  if unhandledExceptionHook != nil:
+    unhandledExceptionHook(e)
+  when hostOS != "any":
+    reportUnhandledErrorAux(e)
+
+when not gotoBasedExceptions:
+  proc nimLeaveFinally() {.compilerRtl.} =
+    when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
+      {.emit: "throw;".}
+    else:
+      if excHandler != nil:
+        c_longjmp(excHandler.context, 1)
+      else:
+        reportUnhandledError(currException)
+        rawQuit(1)
+
+when gotoBasedExceptions:
+  var nimInErrorMode {.threadvar.}: bool
+
+  proc nimErrorFlag(): ptr bool {.compilerRtl, inl.} =
+    result = addr(nimInErrorMode)
+
+  proc nimTestErrorFlag() {.compilerRtl.} =
+    ## 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
+      rawQuit(1)
+
+proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
+  when defined(nimPanics):
+    if e of Defect:
+      reportUnhandledError(e)
+      rawQuit(1)
+
+  if localRaiseHook != nil:
+    if not localRaiseHook(e): return
+  if globalRaiseHook != nil:
+    if not globalRaiseHook(e): return
+  when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
+    if e == currException:
+      {.emit: "throw;".}
+    else:
+      pushCurrentException(e)
+      {.emit: "throw `e`;".}
+  elif quirkyExceptions or gotoBasedExceptions:
+    pushCurrentException(e)
+    when gotoBasedExceptions:
+      inc nimInErrorMode
+  else:
+    if excHandler != nil:
+      pushCurrentException(e)
+      c_longjmp(excHandler.context, 1)
+    else:
+      reportUnhandledError(e)
+      rawQuit(1)
+
+proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
+                      line: int) {.compilerRtl, nodestroy.} =
+  if e.name.isNil: e.name = ename
+  when hasSomeStackTrace:
+    when defined(nimStackTraceOverride):
+      if e.trace.len == 0:
+        rawWriteStackTrace(e.trace)
+      else:
+        e.trace.add reraisedFrom(reraisedFromBegin)
+        auxWriteStackTraceWithOverride(e.trace)
+        e.trace.add reraisedFrom(reraisedFromEnd)
+    elif NimStackTrace:
+      if e.trace.len == 0:
+        rawWriteStackTrace(e.trace)
+      elif framePtr != nil:
+        e.trace.add reraisedFrom(reraisedFromBegin)
+        auxWriteStackTrace(framePtr, e.trace)
+        e.trace.add reraisedFrom(reraisedFromEnd)
+  else:
+    if procname != nil and filename != nil:
+      e.trace.add StackTraceEntry(procname: procname, filename: filename, line: line)
+  raiseExceptionAux(e)
 
-#addQuitProc(stackTraceWrapper)
+proc raiseException(e: sink(ref Exception), ename: cstring) {.compilerRtl.} =
+  raiseExceptionEx(e, ename, nil, nil, 0)
 
-var
-  dbgAborting: bool # whether the debugger wants to abort
-
-proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} =
-  # print stack trace and quit
-  var s = sig
-  GC_disable()
-  setLen(buf, 0)
-  rawWriteStackTrace(buf)
-
-  if s == SIGINT: add(buf, "SIGINT: Interrupted by Ctrl-C.\n")
-  elif s == SIGSEGV: 
-    add(buf, "SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
-  elif s == SIGABRT:
-    if dbgAborting: return # the debugger wants to abort
-    add(buf, "SIGABRT: Abnormal termination.\n")
-  elif s == SIGFPE: add(buf, "SIGFPE: Arithmetic error.\n")
-  elif s == SIGILL: add(buf, "SIGILL: Illegal operation.\n")
-  elif s == SIGBUS: 
-    add(buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
-  else: add(buf, "unknown signal\n")
-  writeToStdErr(buf)
-  dbgAborting = True # play safe here...
-  GC_enable()
-  quit(1) # always quit when SIGABRT
-
-proc registerSignalHandler() =
-  c_signal(SIGINT, signalHandler)
-  c_signal(SIGSEGV, signalHandler)
-  c_signal(SIGABRT, signalHandler)
-  c_signal(SIGFPE, signalHandler)
-  c_signal(SIGILL, signalHandler)
-  c_signal(SIGBUS, signalHandler)
-
-when not defined(noSignalHandler):
-  registerSignalHandler() # call it in initialization section
-# for easier debugging of the GC, this memory is only allocated after the
-# signal handlers have been registered
-new(gAssertionFailed)
-buf = newString(2048)
-assertBuf = newString(2048)
-setLen(buf, 0)
-setLen(assertBuf, 0)
-
-proc raiseRangeError(val: biggestInt) {.compilerproc, noreturn, noinline.} =
-  raise newException(EOutOfRange, "value " & $val & " out of range")
-
-proc raiseIndexError() {.compilerproc, noreturn, noinline.} =
-  raise newException(EInvalidIndex, "index out of bounds")
-
-proc raiseFieldError(f: string) {.compilerproc, noreturn, noinline.} =
-  raise newException(EInvalidField, f & " is not accessible")
-
-proc chckIndx(i, a, b: int): int =
-  if i >= a and i <= b:
-    return i
+proc reraiseException() {.compilerRtl.} =
+  if currException == nil:
+    sysFatal(ReraiseDefect, "no exception to reraise")
   else:
-    raiseIndexError()
-
-proc chckRange(i, a, b: int): int =
-  if i >= a and i <= b:
-    return i
+    when gotoBasedExceptions:
+      inc nimInErrorMode
+    else:
+      raiseExceptionAux(currException)
+
+proc threadTrouble() =
+  # also forward declared, it is 'raises: []' hence the try-except.
+  try:
+    if currException != nil: reportUnhandledError(currException)
+  except:
+    discard
+  rawQuit 1
+
+proc writeStackTrace() =
+  when hasSomeStackTrace:
+    var s = ""
+    rawWriteStackTrace(s)
   else:
-    raiseRangeError(i)
+    let s = noStacktraceAvailable
+  cast[proc (s: string) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage2)(s)
 
-proc chckRange64(i, a, b: int64): int64 {.compilerproc.} =
-  if i >= a and i <= b:
-    return i
+proc getStackTrace(): string =
+  when hasSomeStackTrace:
+    result = ""
+    rawWriteStackTrace(result)
   else:
-    raiseRangeError(i)
+    result = noStacktraceAvailable
 
-proc chckRangeF(x, a, b: float): float =
-  if x >= a and x <= b:
-    return x
+proc getStackTrace(e: ref Exception): string =
+  if not isNil(e):
+    result = $e.trace
   else:
-    raise newException(EOutOfRange, "value " & $x & " out of range")
-
-proc chckNil(p: pointer) =
-  if p == nil: c_raise(SIGSEGV)
-
-proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
-  # checks if obj is of type subclass:
-  var x = obj
-  if x == subclass: return # optimized fast path
-  while x != subclass:
-    if x == nil:
-      raise newException(EInvalidObjectConversion, "invalid object conversion")
-    x = x.base
-
-proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
-  if a != b:
-    raise newException(EInvalidObjectAssignment, "invalid object assignment")
-
-proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
-  # checks if obj is of type subclass:
-  var x = obj
-  if x == subclass: return true # optimized fast path
-  while x != subclass:
-    if x == nil: return false
-    x = x.base
-  return true
+    result = ""
+
+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.
+  ## This is not yet available for the JS backend.
+  when hasSomeStackTrace:
+    rawWriteStackTrace(result)
+
+const nimCallDepthLimit {.intdefine.} = 2000
+
+proc callDepthLimitReached() {.noinline.} =
+  writeStackTrace()
+  let msg = "Error: call depth limit reached in a debug build (" &
+      $nimCallDepthLimit & " function calls). You can change it with " &
+      "-d:nimCallDepthLimit=<int> but really try to avoid deep " &
+      "recursions instead.\n"
+  showErrorMessage2(msg)
+  rawQuit(1)
+
+proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
+  if framePtr == nil:
+    s.calldepth = 0
+    when NimStackTraceMsgs: s.frameMsgLen = 0
+  else:
+    s.calldepth = framePtr.calldepth+1
+    when NimStackTraceMsgs: s.frameMsgLen = framePtr.frameMsgLen
+  s.prev = framePtr
+  framePtr = s
+  if s.calldepth == nimCallDepthLimit: callDepthLimitReached()
+
+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 quirkyExceptions:
+
+  type
+    StdException {.importcpp: "std::exception", header: "<exception>".} = object
+
+  proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())", nodecl.}
+
+  proc setTerminate(handler: proc() {.noconv.})
+    {.importc: "std::set_terminate", header: "<exception>".}
+
+  setTerminate proc() {.noconv.} =
+    # Remove ourself as a handler, reinstalling the default handler.
+    setTerminate(nil)
+
+    var msg = "Unknown error in unexpected exception handler"
+    try:
+      {.emit: "#if !defined(_MSC_VER) || (_MSC_VER >= 1923)".}
+      raise
+      {.emit: "#endif".}
+    except Exception:
+      msg = currException.getStackTrace() & "Error: unhandled exception: " &
+        currException.msg & " [" & $currException.name & "]"
+    except StdException as e:
+      msg = "Error: unhandled cpp exception: " & $e.what()
+    except:
+      msg = "Error: unhandled unknown cpp exception"
+
+    {.emit: "#if defined(_MSC_VER) && (_MSC_VER < 1923)".}
+    msg = "Error: unhandled unknown cpp exception"
+    {.emit: "#endif".}
+
+    when defined(genode):
+      # stderr not available by default, use the LOG session
+      echo msg
+    else:
+      writeToStdErr msg & "\n"
+
+    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")
+      elif s == SIGSEGV:
+        action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
+      elif s == SIGABRT:
+        action("SIGABRT: Abnormal termination.\n")
+      elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n")
+      elif s == SIGILL: action("SIGILL: Illegal operation.\n")
+      elif (when declared(SIGBUS): s == SIGBUS else: false):
+        action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
+      else:
+        block platformSpecificSignal:
+          when declared(SIGPIPE):
+            if s == SIGPIPE:
+              action("SIGPIPE: Pipe closed.\n")
+              break platformSpecificSignal
+          action("unknown signal\n")
+
+    # print stack trace and quit
+    when defined(memtracker):
+      logPendingOps()
+    when hasSomeStackTrace:
+      when not usesDestructors: GC_disable()
+      var buf = newStringOfCap(2000)
+      rawWriteStackTrace(buf)
+      processSignal(sign, buf.add) # nice hu? currying a la Nim :-)
+      showErrorMessage2(buf)
+      when not usesDestructors: GC_enable()
+    else:
+      var msg: cstring
+      template asgn(y) =
+        msg = y
+      processSignal(sign, asgn)
+      # xxx use string for msg instead of cstring, and here use showErrorMessage2(msg)
+      # unless there's a good reason to use cstring in signal handler to avoid
+      # using gc?
+      showErrorMessage(msg, msg.len)
+
+    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)
+    c_signal(SIGFPE, signalHandler)
+    c_signal(SIGILL, signalHandler)
+    when declared(SIGBUS):
+      c_signal(SIGBUS, signalHandler)
+    when declared(SIGPIPE):
+      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:
+  when declared(Sighandler):
+    c_signal(SIGINT, cast[Sighandler](hook))
+
+when not defined(noSignalHandler) and not defined(useNimRtl):
+  proc unsetControlCHook() =
+    # proc to unset a hook set by setControlCHook
+    c_signal(SIGINT, signalHandler)