From c277b1baa202dd5728fae5b3965be0abdf9838cc Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 5 Nov 2022 17:58:57 +0800 Subject: [PATCH] ref #20694; quit value gets saturated to ranges (#20753) * quit value gets saturated to ranges * add documentation * minimal changes * refactor * small fix * add documentation * fixes * Update lib/system.nim Co-authored-by: Juan Carlos Co-authored-by: Juan Carlos (cherry picked from commit d5cc2085eae85ad922dfafc1dca81860d94d1731) --- lib/genode/alloc.nim | 2 +- lib/system.nim | 99 ++++++++++++++++++++----------- lib/system/arc.nim | 2 +- lib/system/dyncalls.nim | 10 ++-- lib/system/excpt.nim | 16 ++--- lib/system/fatal.nim | 2 +- lib/system/gc.nim | 4 +- lib/system/gc2.nim | 2 +- lib/system/gc_common.nim | 4 +- lib/system/gc_hooks.nim | 4 +- lib/system/gc_ms.nim | 2 +- lib/system/memtracker.nim | 2 +- lib/system/mmdisp.nim | 2 +- lib/system/orc.nim | 4 +- lib/system/osalloc.nim | 2 +- lib/system/threadlocalstorage.nim | 2 +- 16 files changed, 95 insertions(+), 64 deletions(-) diff --git a/lib/genode/alloc.nim b/lib/genode/alloc.nim index 3ddd3074be8e5..8e6b0a5040353 100644 --- a/lib/genode/alloc.nim +++ b/lib/genode/alloc.nim @@ -111,7 +111,7 @@ proc osDeallocPages(p: pointer; size: int) = if m.attachment == p: if m.size != size: echo "cannot partially detach dataspace" - quit -1 + rawQuit -1 runtimeEnv.detachAddress m.attachment runtimeEnv.freeDataspace m.ds m[] = Map() diff --git a/lib/system.nim b/lib/system.nim index 98855adc8bd63..c7f49d5947f2d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1190,32 +1190,7 @@ proc align(address, alignment: int): int = result = (address + (alignment - 1)) and not (alignment - 1) when defined(nimNoQuit): - proc quit*(errorcode: int = QuitSuccess) = discard "ignoring quit" - ## Stops the program immediately with an exit code. - ## - ## Before stopping the program the "exit procedures" are called in the - ## opposite order they were added with `addExitProc `_. - ## - ## The proc `quit(QuitSuccess)` is called implicitly when your nim - ## program finishes without incident for platforms where this is the - ## expected behavior. A raised unhandled exception is - ## equivalent to calling `quit(QuitFailure)`. - ## - ## Note that this is a *runtime* call and using `quit` inside a macro won't - ## have any compile time effect. If you need to stop the compiler inside a - ## macro, use the `error `_ or `fatal - ## `_ pragmas. - ## - ## .. danger:: In almost all cases, in particular in library code, prefer - ## alternatives, e.g. `doAssert false` or raise a `Defect`. - ## `quit` bypasses regular control flow in particular `defer`, - ## `try`, `catch`, `finally` and `destructors`, and exceptions that may have been - ## raised by an `addExitProc` proc, as well as cleanup code in other threads. - ## It does *not* call the garbage collector to free all the memory, - ## unless an `addExitProc` proc calls `GC_fullCollect <#GC_fullCollect>`_. - -elif defined(nimdoc): - proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.} + proc rawQuit(errorcode: int = QuitSuccess) = discard "ignoring quit" elif defined(genode): include genode/env @@ -1225,28 +1200,28 @@ elif defined(genode): type GenodeEnv* = GenodeEnvPtr ## Opaque type representing Genode environment. - proc quit*(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn, + proc rawQuit(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn, importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "".} - proc quit*(errorcode: int = QuitSuccess) = - systemEnv.quit(errorcode) + proc rawQuit(errorcode: int = QuitSuccess) {.inline, noreturn.} = + systemEnv.rawQuit(errorcode) + elif defined(js) and defined(nodejs) and not defined(nimscript): - proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", + proc rawQuit(errorcode: int = QuitSuccess) {.magic: "Exit", importc: "process.exit", noreturn.} else: - proc quit*(errorcode: int = QuitSuccess) {. + proc rawQuit(errorcode: int = QuitSuccess) {. magic: "Exit", importc: "exit", header: "", noreturn.} - template sysAssert(cond: bool, msg: string) = when defined(useSysAssert): if not cond: cstderr.rawWrite "[SYSASSERT] " cstderr.rawWrite msg cstderr.rawWrite "\n" - quit 1 + rawQuit 1 const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript) @@ -2462,6 +2437,62 @@ when defined(js): include "system/jssys" include "system/reprjs" + +when defined(nimNoQuit): + proc quit*(errorcode: int = QuitSuccess) = discard "ignoring quit" + ## Stops the program immediately with an exit code. + ## + ## Before stopping the program the "exit procedures" are called in the + ## opposite order they were added with `addExitProc `_. + ## + ## The proc `quit(QuitSuccess)` is called implicitly when your nim + ## program finishes without incident for platforms where this is the + ## expected behavior. A raised unhandled exception is + ## equivalent to calling `quit(QuitFailure)`. + ## + ## Note that this is a *runtime* call and using `quit` inside a macro won't + ## have any compile time effect. If you need to stop the compiler inside a + ## macro, use the `error `_ or `fatal + ## `_ pragmas. + ## + ## .. warning:: `errorcode` gets saturated when it exceeds the valid range + ## on the specific platform. On Posix, the valid range is `low(int8)..high(int8)`. + ## On Windows, the valid range is `low(int32)..high(int32)`. For instance, + ## `quit(int(0x100000000))` is equal to `quit(127)` on Linux. + ## + ## .. danger:: In almost all cases, in particular in library code, prefer + ## alternatives, e.g. `doAssert false` or raise a `Defect`. + ## `quit` bypasses regular control flow in particular `defer`, + ## `try`, `catch`, `finally` and `destructors`, and exceptions that may have been + ## raised by an `addExitProc` proc, as well as cleanup code in other threads. + ## It does *not* call the garbage collector to free all the memory, + ## unless an `addExitProc` proc calls `GC_fullCollect <#GC_fullCollect>`_. + +elif defined(nimdoc): + proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.} + +elif defined(genode): + proc quit*(errorcode: int = QuitSuccess) {.inline, noreturn.} = + rawQuit(errorcode) + +elif defined(js) and defined(nodejs) and not defined(nimscript): + proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", + importc: "process.exit", noreturn.} + +else: + proc quit*(errorcode: int = QuitSuccess) {.inline, noreturn.} = + when defined(posix): # posix uses low 8 bits + type ExitCodeRange = int8 + else: # win32 uses low 32 bits + type ExitCodeRange = int32 + + if errorcode < low(ExitCodeRange): + rawQuit(low(ExitCodeRange).int) + elif errorcode > high(ExitCodeRange): + rawQuit(high(ExitCodeRange).int) + else: + rawQuit(errorcode) + proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = ## A shorthand for `echo(errormsg); quit(errorcode)`. when defined(nimscript) or defined(js) or (hostOS == "standalone"): @@ -3106,7 +3137,7 @@ when defined(genode): proc nim_component_construct(env: GenodeEnv) {.exportc.} = ## Procedure called during `Component::construct` by the loader. if componentConstructHook.isNil: - env.quit(programResult) + env.rawQuit(programResult) # No native Genode application initialization, # exit as would POSIX. else: diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 0a41b294c517b..0c6b3cff64d2b 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -154,7 +154,7 @@ proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} = when defined(nimOwnedEnabled): if head(p).rc >= rcIncrement: cstderr.rawWrite "[FATAL] dangling references exist\n" - quit 1 + rawQuit 1 when defined(nimArcDebug): # we do NOT really free the memory here in order to reliably detect use-after-frees if freedCells.data == nil: init(freedCells) diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index 027955c73585f..de22f7cbb9173 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -47,14 +47,14 @@ proc nimLoadLibraryError(path: string) = copyMem(msg[msgIdx].addr, badExe.cstring, badExe.len) discard MessageBoxA(nil, msg[0].addr, nil, 0) cstderr.rawWrite("\n") - quit(1) + rawQuit(1) proc procAddrError(name: cstring) {.compilerproc, nonReloadable, hcrInline.} = # carefully written to avoid memory allocation: cstderr.rawWrite("could not import: ") cstderr.rawWrite(name) cstderr.rawWrite("\n") - quit(1) + rawQuit(1) # this code was inspired from Lua's source code: # Lua - An Extensible Extension Language @@ -180,19 +180,19 @@ elif defined(nintendoswitch) or defined(freertos) or defined(zephyr): proc nimUnloadLibrary(lib: LibHandle) = cstderr.rawWrite("nimUnLoadLibrary not implemented") cstderr.rawWrite("\n") - quit(1) + rawQuit(1) proc nimLoadLibrary(path: string): LibHandle = cstderr.rawWrite("nimLoadLibrary not implemented") cstderr.rawWrite("\n") - quit(1) + rawQuit(1) proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = cstderr.rawWrite("nimGetProAddr not implemented") cstderr.rawWrite(name) cstderr.rawWrite("\n") - quit(1) + rawQuit(1) else: {.error: "no implementation for dyncalls".} diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 10f8a2ba77cec..86cfff9cd349e 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -415,7 +415,7 @@ proc nimLeaveFinally() {.compilerRtl.} = c_longjmp(excHandler.context, 1) else: reportUnhandledError(currException) - quit(1) + rawQuit(1) when gotoBasedExceptions: var nimInErrorMode {.threadvar.}: bool @@ -430,13 +430,13 @@ when gotoBasedExceptions: 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 @@ -458,7 +458,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.} = @@ -501,7 +501,7 @@ proc threadTrouble() = if currException != nil: reportUnhandledError(currException) except: discard - quit 1 + rawQuit 1 proc writeStackTrace() = when hasSomeStackTrace: @@ -544,7 +544,7 @@ proc callDepthLimitReached() {.noinline.} = "-d:nimCallDepthLimit= 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: @@ -597,7 +597,7 @@ 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.} @@ -651,7 +651,7 @@ when not defined(noSignalHandler) and not defined(useNimRtl): # also return the correct exit code to the shell. discard c_raise(sign) else: - quit(1) + rawQuit(1) var SIG_IGN {.importc: "SIG_IGN", header: "".}: Sighandler diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim index c01787a326612..a55af2dc3989b 100644 --- a/lib/system/fatal.nim +++ b/lib/system/fatal.nim @@ -44,7 +44,7 @@ elif (defined(nimQuirky) or defined(nimPanics)) and not defined(nimscript): add(buf, name exceptn) add(buf, "]\n") cstderr.rawWrite buf - quit 1 + rawQuit 1 func sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = sysFatal(exceptn, message, "") diff --git a/lib/system/gc.nim b/lib/system/gc.nim index e50e80f11d1c2..99e478a2528ea 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -161,7 +161,7 @@ template gcAssert(cond: bool, msg: string) = writeStackTrace() #var x: ptr int #echo x[] - quit 1 + rawQuit 1 proc addZCT(s: var CellSeq, c: PCell) {.noinline.} = if (c.refcount and ZctFlag) == 0: @@ -653,7 +653,7 @@ when logGC: if cycleCheckA[i] == c: return true if cycleCheckALen == len(cycleCheckA): gcAssert(false, "cycle detection overflow") - quit 1 + rawQuit 1 cycleCheckA[cycleCheckALen] = c inc cycleCheckALen diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 45d467051ef3c..0593b396e3ca5 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -129,7 +129,7 @@ template gcAssert(cond: bool, msg: string) = echo "[GCASSERT] ", msg GC_disable() writeStackTrace() - quit 1 + rawQuit 1 proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index d7483419ac258..e7d97e0d89f1d 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -471,7 +471,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = inc globalMarkersLen else: cstderr.rawWrite("[GC] cannot register global variable; too many global variables") - quit 1 + rawQuit 1 proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = if threadLocalMarkersLen <= high(threadLocalMarkers): @@ -479,4 +479,4 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} inc threadLocalMarkersLen else: cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables") - quit 1 + rawQuit 1 diff --git a/lib/system/gc_hooks.nim b/lib/system/gc_hooks.nim index 70f02e6574a44..ace62eea0a13b 100644 --- a/lib/system/gc_hooks.nim +++ b/lib/system/gc_hooks.nim @@ -24,7 +24,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = inc globalMarkersLen else: cstderr.rawWrite("[GC] cannot register global variable; too many global variables") - quit 1 + rawQuit 1 proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = if threadLocalMarkersLen <= high(threadLocalMarkers): @@ -32,7 +32,7 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} inc threadLocalMarkersLen else: cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables") - quit 1 + rawQuit 1 proc traverseGlobals*() = for i in 0..globalMarkersLen-1: diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 0675b9f2eef6a..f91b37b941753 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -90,7 +90,7 @@ template gcAssert(cond: bool, msg: string) = if not cond: cstderr.rawWrite "[GCASSERT] " cstderr.rawWrite msg - quit 1 + rawQuit 1 proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim index f0c83f1fa645e..f769bb997df83 100644 --- a/lib/system/memtracker.nim +++ b/lib/system/memtracker.nim @@ -72,7 +72,7 @@ proc addEntry(entry: LogEntry) = cprintf("interesting %s:%ld %s\n", entry.file, entry.line, entry.op) let x = cast[proc() {.nimcall, tags: [], gcsafe, locks: 0, raises: [].}](writeStackTrace) x() - quit 1 + rawQuit 1 #if gLog.count > high(gLog.data): # gLogger(gLog) # gLog.count = 0 diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 3317ba627051f..881a00d08ae78 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -46,7 +46,7 @@ else: proc raiseOutOfMem() {.noinline.} = if outOfMemHook != nil: outOfMemHook() cstderr.rawWrite("out of memory\n") - quit(1) + rawQuit(1) when defined(boehmgc): include system / mm / boehm diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 6273e1a216ebe..ef2d4a8ffb565 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -112,7 +112,7 @@ template orcAssert(cond, msg) = when logOrc: if not cond: cfprintf(cstderr, "[Bug!] %s\n", msg) - quit 1 + rawQuit 1 when logOrc: proc strstr(s, sub: cstring): cstring {.header: "", importc.} @@ -141,7 +141,7 @@ proc unregisterCycle(s: Cell) = when false: if idx >= roots.len or idx < 0: cprintf("[Bug!] %ld\n", idx) - quit 1 + rawQuit 1 roots.d[idx] = roots.d[roots.len-1] roots.d[idx][0].rootIdx = idx+1 dec roots.len diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 39bf65d6c9485..4817059bee02a 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -189,7 +189,7 @@ elif defined(windows) and not defined(StandaloneHeapSize): when reallyOsDealloc: if virtualFree(p, 0, MEM_RELEASE) == 0: cprintf "virtualFree failing!" - quit 1 + rawQuit 1 #VirtualFree(p, size, MEM_DECOMMIT) elif hostOS == "standalone" or defined(StandaloneHeapSize): diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim index e7457ae8754c2..45403c68b321b 100644 --- a/lib/system/threadlocalstorage.nim +++ b/lib/system/threadlocalstorage.nim @@ -265,4 +265,4 @@ when not defined(useNimRtl): if nimThreadVarsSize() > sizeof(ThreadLocalStorage): c_fprintf(cstderr, """too large thread local storage size requested, use -d:\"nimTlsSize=X\" to setup even more or stop using unittest.nim""") - quit 1 + rawQuit 1