summary refs log tree commit diff stats
path: root/lib/system/excpt.nim
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2009-06-08 08:06:25 +0200
committerAndreas Rumpf <rumpf_a@web.de>2009-06-08 08:06:25 +0200
commit4d4b3b1c04d41868ebb58bd9ccba7b303007e900 (patch)
tree909ed0aad0b145733521f4ac2bfb938dd4b43785 /lib/system/excpt.nim
parentce88dc3e67436939b03f97e624c11ca6058fedce (diff)
downloadNim-4d4b3b1c04d41868ebb58bd9ccba7b303007e900.tar.gz
version0.7.10
Diffstat (limited to 'lib/system/excpt.nim')
-rw-r--r--lib/system/excpt.nim285
1 files changed, 285 insertions, 0 deletions
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
new file mode 100644
index 000000000..293491fe9
--- /dev/null
+++ b/lib/system/excpt.nim
@@ -0,0 +1,285 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+# Exception handling code. This is difficult because it has
+# to work if there is no more memory. Thus we have to use
+# a static string. Do not use ``sprintf``, etc. as they are
+# unsafe!
+
+when not defined(windows) or not defined(guiapp):
+  proc writeToStdErr(msg: CString) = write(stdout, msg)
+
+else:
+  proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
+    header: "<windows.h>", nodecl.}
+
+  proc writeToStdErr(msg: CString) =
+    discard MessageBoxA(0, msg, nil, 0)
+
+proc raiseException(e: ref E_Base, ename: CString) {.compilerproc.}
+proc reraiseException() {.compilerproc.}
+
+proc registerSignalHandler() {.compilerproc.}
+
+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.}
+
+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
+
+var
+  excHandler {.compilerproc.}: PSafePoint = nil
+    # list of exception handlers
+    # a global variable for the root of all try blocks
+
+proc reraiseException() =
+  if excHandler == nil:
+    raise newException(ENoExceptionToReraise, "no exception to reraise")
+  else:
+    c_longjmp(excHandler.context, 1)
+
+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)
+
+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
+
+  framePtr {.exportc.}: PFrame
+
+  tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
+  
+  stackTraceNewLine* = "\n" ## undocumented feature
+
+proc auxWriteStackTrace(f: PFrame, s: var string) =
+  const 
+    firstCalls = 32
+  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
+    inc(i)
+    inc(total)
+    it = it.prev
+  var b = it
+  while it != nil:
+    inc(total)
+    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) ...")
+    else:
+      add(s, $tempFrames[j].procname)
+      if tempFrames[j].line > 0:
+        add(s, ", line: ")
+        add(s, $tempFrames[j].line)
+    add(s, stackTraceNewLine)
+
+proc rawWriteStackTrace(s: var string) =
+  if framePtr == nil:
+    add(s, "No stack traceback available")
+    add(s, stackTraceNewLine)
+  else:
+    add(s, "Traceback (most recent call last)")
+    add(s, stackTraceNewLine)
+    auxWriteStackTrace(framePtr, s)
+
+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)
+      else:
+        add(buf, "Error: unhandled exception")
+      add(buf, " [")
+      add(buf, $ename)
+      add(buf, "]\n")
+      writeToStdErr(buf)
+    else:
+      writeToStdErr(ename)
+    quitOrDebug()
+  GC_enable()
+
+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
+    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()
+
+#addQuitProc(stackTraceWrapper)
+
+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.\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.\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
+  else:
+    raiseIndexError()
+
+proc chckRange(i, a, b: int): int =
+  if i >= a and i <= b:
+    return i
+  else:
+    raiseRangeError(i)
+
+proc chckRange64(i, a, b: int64): int64 {.compilerproc.} =
+  if i >= a and i <= b:
+    return i
+  else:
+    raiseRangeError(i)
+
+proc chckRangeF(x, a, b: float): float =
+  if x >= a and x <= b:
+    return x
+  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