From cdebcf23d94939690ebdd08151984d926d9b566e Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 20 Jan 2017 17:08:11 +0100 Subject: [PATCH] new segfaults.nim stdlib module works on Windows --- lib/pure/segfaults.nim | 42 +++++++++++++++++++++++++++++++++---- lib/system/excpt.nim | 7 ++++++- tests/stdlib/tsegfaults.nim | 4 ++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim index 3c3b594492..c53abb4aa4 100644 --- a/lib/pure/segfaults.nim +++ b/lib/pure/segfaults.nim @@ -10,6 +10,8 @@ ## This modules registers a signal handler that turns access violations / ## segfaults into a ``NilAccessError`` exception. To be able to catch ## a NilAccessError all you have to do is to import this module. +## +## Tested on these OSes: Linux, Windows, OSX type NilAccessError* = object of SystemError ## \ @@ -18,17 +20,49 @@ type # do allocate memory upfront: var se: ref NilAccessError new(se) +se.name = "NilAccessError" se.msg = "" when defined(windows): include "$lib/system/ansi_c" + import winlean + + const + EXCEPTION_ACCESS_VIOLATION = DWORD(0xc0000005) + EXCEPTION_CONTINUE_SEARCH = Long(0) + + type + PEXCEPTION_RECORD = ptr object + exceptionCode: DWORD # other fields left out + + PEXCEPTION_POINTERS = ptr object + exceptionRecord: PEXCEPTION_RECORD + contextRecord: pointer + + VectoredHandler = proc (p: PEXCEPTION_POINTERS): LONG {.stdcall.} + proc addVectoredExceptionHandler(firstHandler: ULONG, + handler: VectoredHandler): pointer {. + importc: "AddVectoredExceptionHandler", stdcall, dynlib: "kernel32.dll"} + {.push stackTrace: off.} - proc segfaultHandler(sig: cint) {.noconv.} = - {.gcsafe.}: - raise se + proc segfaultHandler(p: PEXCEPTION_POINTERS): LONG {.stdcall.} = + if p.exceptionRecord.exceptionCode == EXCEPTION_ACCESS_VIOLATION: + {.gcsafe.}: + raise se + else: + result = EXCEPTION_CONTINUE_SEARCH {.pop.} - c_signal(SIGSEGV, segfaultHandler) + + discard addVectoredExceptionHandler(0, segfaultHandler) + + when false: + {.push stackTrace: off.} + proc segfaultHandler(sig: cint) {.noconv.} = + {.gcsafe.}: + rawRaise se + {.pop.} + c_signal(SIGSEGV, segfaultHandler) else: import posix diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index d00ab64b13..b16590c924 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -212,6 +212,12 @@ proc quitOrDebug() {.inline.} = else: endbStep() # call the debugger +when false: + proc rawRaise*(e: ref Exception) = + ## undocumented. Do not use. + pushCurrentException(e) + c_longjmp(excHandler.context, 1) + proc raiseExceptionAux(e: ref Exception) = if localRaiseHook != nil: if not localRaiseHook(e): return @@ -371,5 +377,4 @@ when not defined(noSignalHandler): proc setControlCHook(hook: proc () {.noconv.} not nil) = # ugly cast, but should work on all architectures: type SignalHandler = proc (sign: cint) {.noconv, benign.} - {.deprecated: [TSignalHandler: SignalHandler].} c_signal(SIGINT, cast[SignalHandler](hook)) diff --git a/tests/stdlib/tsegfaults.nim b/tests/stdlib/tsegfaults.nim index 1e63ba6600..1d8508c52b 100644 --- a/tests/stdlib/tsegfaults.nim +++ b/tests/stdlib/tsegfaults.nim @@ -18,6 +18,10 @@ proc main = try: var x: ptr int echo x[] + try: + raise newException(ValueError, "not a crash") + except ValueError: + discard except NilAccessError: echo "caught a crash!"