diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2009-06-08 08:06:25 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2009-06-08 08:06:25 +0200 |
commit | 4d4b3b1c04d41868ebb58bd9ccba7b303007e900 (patch) | |
tree | 909ed0aad0b145733521f4ac2bfb938dd4b43785 /lib/system/excpt.nim | |
parent | ce88dc3e67436939b03f97e624c11ca6058fedce (diff) | |
download | Nim-4d4b3b1c04d41868ebb58bd9ccba7b303007e900.tar.gz |
version0.7.10
Diffstat (limited to 'lib/system/excpt.nim')
-rw-r--r-- | lib/system/excpt.nim | 285 |
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 |