mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-09 08:40:59 +00:00
system.nim: refactoring that removes cyclic dependencies in prep for IC
This commit is contained in:
@@ -19,12 +19,16 @@ import std/private/miscdollars
|
||||
|
||||
type InstantiationInfo = tuple[filename: string, line: int, column: int]
|
||||
|
||||
{.push overflowChecks: off, rangeChecks: off.}
|
||||
|
||||
proc `$`(info: InstantiationInfo): string =
|
||||
# The +1 is needed here
|
||||
# instead of overriding `$` (and changing its meaning), consider explicit name.
|
||||
result = ""
|
||||
result.toLocation(info.filename, info.line, info.column + 1)
|
||||
|
||||
{.pop.}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,13 @@ template toLocation*(result: var string, file: string | cstring, line: int, col:
|
||||
## avoids spurious allocations
|
||||
# Hopefully this can be re-used everywhere so that if a user needs to customize,
|
||||
# it can be done in a single place.
|
||||
result.add file
|
||||
when file is cstring:
|
||||
var i = 0
|
||||
while file[i] != '\0':
|
||||
add(result, file[i])
|
||||
inc i
|
||||
else:
|
||||
result.add file
|
||||
if line > 0:
|
||||
result.add "("
|
||||
addInt(result, line)
|
||||
|
||||
230
lib/system.nim
230
lib/system.nim
@@ -1766,6 +1766,111 @@ template newException*(exceptn: typedesc, message: string;
|
||||
## to `message`. Returns the new exception object.
|
||||
(ref exceptn)(msg: message, parent: parentException)
|
||||
|
||||
# we have to compute this here before turning it off in except.nim anyway ...
|
||||
const NimStackTrace = compileOption("stacktrace")
|
||||
const
|
||||
usesDestructors = defined(gcDestructors) or defined(gcHooks)
|
||||
|
||||
when notJSnotNims:
|
||||
proc setControlCHook*(hook: proc () {.noconv.}) {.raises: [], gcsafe.}
|
||||
## Allows you to override the behaviour of your application when CTRL+C
|
||||
## is pressed. Only one such hook is supported.
|
||||
##
|
||||
## The handler runs inside a C signal handler and comes with similar
|
||||
## limitations.
|
||||
##
|
||||
## Allocating memory and interacting with most system calls, including using
|
||||
## `echo`, `string`, `seq`, raising or catching exceptions etc is undefined
|
||||
## behavior and will likely lead to application crashes.
|
||||
##
|
||||
## The OS may call the ctrl-c handler from any thread, including threads
|
||||
## that were not created by Nim, such as happens on Windows.
|
||||
##
|
||||
## ## Example:
|
||||
##
|
||||
## ```nim
|
||||
## var stop: Atomic[bool]
|
||||
## proc ctrlc() {.noconv.} =
|
||||
## # Using atomics types is safe!
|
||||
## stop.store(true)
|
||||
##
|
||||
## setControlCHook(ctrlc)
|
||||
##
|
||||
## while not stop.load():
|
||||
## echo "Still running.."
|
||||
## sleep(1000)
|
||||
## ```
|
||||
|
||||
when not defined(noSignalHandler) and not defined(useNimRtl):
|
||||
proc unsetControlCHook*()
|
||||
## Reverts a call to setControlCHook.
|
||||
|
||||
when hostOS != "standalone":
|
||||
proc getStackTrace*(): string {.gcsafe.}
|
||||
## Gets the current stack trace. This only works for debug builds.
|
||||
|
||||
proc getStackTrace*(e: ref Exception): string {.gcsafe.}
|
||||
## Gets the stack trace associated with `e`, which is the stack that
|
||||
## lead to the `raise` statement. This only works for debug builds.
|
||||
|
||||
var
|
||||
globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.}
|
||||
## With this hook you can influence exception handling on a global level.
|
||||
## If not nil, every 'raise' statement ends up calling this hook.
|
||||
##
|
||||
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
|
||||
##
|
||||
## If `globalRaiseHook` returns false, the exception is caught and does
|
||||
## not propagate further through the call stack.
|
||||
|
||||
localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.}
|
||||
## With this hook you can influence exception handling on a
|
||||
## thread local level.
|
||||
## If not nil, every 'raise' statement ends up calling this hook.
|
||||
##
|
||||
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
|
||||
##
|
||||
## If `localRaiseHook` returns false, the exception
|
||||
## is caught and does not propagate further through the call stack.
|
||||
|
||||
outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].}
|
||||
## Set this variable to provide a procedure that should be called
|
||||
## in case of an `out of memory`:idx: event. The standard handler
|
||||
## writes an error message and terminates the program.
|
||||
##
|
||||
## `outOfMemHook` can be used to raise an exception in case of OOM like so:
|
||||
##
|
||||
## ```nim
|
||||
## var gOutOfMem: ref EOutOfMemory
|
||||
## new(gOutOfMem) # need to be allocated *before* OOM really happened!
|
||||
## gOutOfMem.msg = "out of memory"
|
||||
##
|
||||
## proc handleOOM() =
|
||||
## raise gOutOfMem
|
||||
##
|
||||
## system.outOfMemHook = handleOOM
|
||||
## ```
|
||||
##
|
||||
## If the handler does not raise an exception, ordinary control flow
|
||||
## continues and the program is terminated.
|
||||
|
||||
unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].}
|
||||
## Set this variable to provide a procedure that should be called
|
||||
## in case of an `unhandle exception` event. The standard handler
|
||||
## writes an error message and terminates the program, except when
|
||||
## using `--os:any`
|
||||
|
||||
{.push stackTrace: off, profiler: off.}
|
||||
when defined(memtracker):
|
||||
include "system/memtracker"
|
||||
|
||||
when hostOS == "standalone":
|
||||
include "system/embedded"
|
||||
else:
|
||||
include "system/excpt"
|
||||
{.pop.}
|
||||
|
||||
|
||||
when not defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
export assertions
|
||||
@@ -1844,9 +1949,6 @@ proc `<`*[T: tuple](x, y: T): bool =
|
||||
|
||||
include "system/gc_interface"
|
||||
|
||||
# we have to compute this here before turning it off in except.nim anyway ...
|
||||
const NimStackTrace = compileOption("stacktrace")
|
||||
|
||||
import system/coro_detection
|
||||
|
||||
{.push checks: off.}
|
||||
@@ -1855,53 +1957,6 @@ import system/coro_detection
|
||||
# however, stack-traces are available for most parts
|
||||
# of the code
|
||||
|
||||
when notJSnotNims:
|
||||
var
|
||||
globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.}
|
||||
## With this hook you can influence exception handling on a global level.
|
||||
## If not nil, every 'raise' statement ends up calling this hook.
|
||||
##
|
||||
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
|
||||
##
|
||||
## If `globalRaiseHook` returns false, the exception is caught and does
|
||||
## not propagate further through the call stack.
|
||||
|
||||
localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.}
|
||||
## With this hook you can influence exception handling on a
|
||||
## thread local level.
|
||||
## If not nil, every 'raise' statement ends up calling this hook.
|
||||
##
|
||||
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
|
||||
##
|
||||
## If `localRaiseHook` returns false, the exception
|
||||
## is caught and does not propagate further through the call stack.
|
||||
|
||||
outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].}
|
||||
## Set this variable to provide a procedure that should be called
|
||||
## in case of an `out of memory`:idx: event. The standard handler
|
||||
## writes an error message and terminates the program.
|
||||
##
|
||||
## `outOfMemHook` can be used to raise an exception in case of OOM like so:
|
||||
##
|
||||
## ```nim
|
||||
## var gOutOfMem: ref EOutOfMemory
|
||||
## new(gOutOfMem) # need to be allocated *before* OOM really happened!
|
||||
## gOutOfMem.msg = "out of memory"
|
||||
##
|
||||
## proc handleOOM() =
|
||||
## raise gOutOfMem
|
||||
##
|
||||
## system.outOfMemHook = handleOOM
|
||||
## ```
|
||||
##
|
||||
## If the handler does not raise an exception, ordinary control flow
|
||||
## continues and the program is terminated.
|
||||
unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].}
|
||||
## Set this variable to provide a procedure that should be called
|
||||
## in case of an `unhandle exception` event. The standard handler
|
||||
## writes an error message and terminates the program, except when
|
||||
## using `--os:any`
|
||||
|
||||
when defined(js) or defined(nimdoc):
|
||||
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
|
||||
## Appends `y` to `x` in place.
|
||||
@@ -2028,6 +2083,16 @@ template unlikely*(val: bool): bool =
|
||||
import system/dollars
|
||||
export dollars
|
||||
|
||||
when notJSnotNims:
|
||||
{.push stackTrace: off, profiler: off.}
|
||||
|
||||
include "system/chcks"
|
||||
|
||||
# we cannot compile this with stack tracing on
|
||||
# as it would recurse endlessly!
|
||||
include "system/integerops"
|
||||
{.pop.}
|
||||
|
||||
when defined(nimAuditDelete):
|
||||
{.pragma: auditDelete, deprecated: "review this call for out of bounds behavior".}
|
||||
else:
|
||||
@@ -2110,17 +2175,17 @@ when notJSnotNims:
|
||||
nimZeroMem(p, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("zeroMem", p, size)
|
||||
proc copyMem(dest, source: pointer, size: Natural) =
|
||||
proc copyMem(dest, source: pointer, size: Natural) {.enforceNoRaises.} =
|
||||
nimCopyMem(dest, source, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("copyMem", dest, size)
|
||||
proc moveMem(dest, source: pointer, size: Natural) =
|
||||
proc moveMem(dest, source: pointer, size: Natural) {.enforceNoRaises.} =
|
||||
c_memmove(dest, source, csize_t(size))
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("moveMem", dest, size)
|
||||
proc equalMem(a, b: pointer, size: Natural): bool =
|
||||
proc equalMem(a, b: pointer, size: Natural): bool {.enforceNoRaises.} =
|
||||
nimCmpMem(a, b, size) == 0
|
||||
proc cmpMem(a, b: pointer, size: Natural): int =
|
||||
proc cmpMem(a, b: pointer, size: Natural): int {.enforceNoRaises.} =
|
||||
nimCmpMem(a, b, size).int
|
||||
|
||||
when not defined(js) or defined(nimscript):
|
||||
@@ -2194,63 +2259,6 @@ when not defined(js):
|
||||
when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
|
||||
when declared(initGC): initGC()
|
||||
|
||||
when notJSnotNims:
|
||||
proc setControlCHook*(hook: proc () {.noconv.}) {.raises: [], gcsafe.}
|
||||
## Allows you to override the behaviour of your application when CTRL+C
|
||||
## is pressed. Only one such hook is supported.
|
||||
##
|
||||
## The handler runs inside a C signal handler and comes with similar
|
||||
## limitations.
|
||||
##
|
||||
## Allocating memory and interacting with most system calls, including using
|
||||
## `echo`, `string`, `seq`, raising or catching exceptions etc is undefined
|
||||
## behavior and will likely lead to application crashes.
|
||||
##
|
||||
## The OS may call the ctrl-c handler from any thread, including threads
|
||||
## that were not created by Nim, such as happens on Windows.
|
||||
##
|
||||
## ## Example:
|
||||
##
|
||||
## ```nim
|
||||
## var stop: Atomic[bool]
|
||||
## proc ctrlc() {.noconv.} =
|
||||
## # Using atomics types is safe!
|
||||
## stop.store(true)
|
||||
##
|
||||
## setControlCHook(ctrlc)
|
||||
##
|
||||
## while not stop.load():
|
||||
## echo "Still running.."
|
||||
## sleep(1000)
|
||||
## ```
|
||||
|
||||
when not defined(noSignalHandler) and not defined(useNimRtl):
|
||||
proc unsetControlCHook*()
|
||||
## Reverts a call to setControlCHook.
|
||||
|
||||
when hostOS != "standalone":
|
||||
proc getStackTrace*(): string {.gcsafe.}
|
||||
## Gets the current stack trace. This only works for debug builds.
|
||||
|
||||
proc getStackTrace*(e: ref Exception): string {.gcsafe.}
|
||||
## Gets the stack trace associated with `e`, which is the stack that
|
||||
## lead to the `raise` statement. This only works for debug builds.
|
||||
|
||||
{.push stackTrace: off, profiler: off.}
|
||||
when defined(memtracker):
|
||||
include "system/memtracker"
|
||||
|
||||
when hostOS == "standalone":
|
||||
include "system/embedded"
|
||||
else:
|
||||
include "system/excpt"
|
||||
include "system/chcks"
|
||||
|
||||
# we cannot compile this with stack tracing on
|
||||
# as it would recurse endlessly!
|
||||
include "system/integerops"
|
||||
{.pop.}
|
||||
|
||||
|
||||
when not defined(js):
|
||||
# this is a hack: without this when statement, you would get:
|
||||
|
||||
@@ -42,11 +42,13 @@ proc writeToStdErr(msg: string) {.inline.} =
|
||||
# fix bug #13115: handles correctly '\0' unlike default implicit conversion to cstring
|
||||
writeToStdErr(msg.cstring, msg.len)
|
||||
|
||||
proc cstrToStrBuiltin(x: cstring): string {.magic: "CStrToStr", noSideEffect.}
|
||||
|
||||
proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} =
|
||||
var toWrite = true
|
||||
if errorMessageWriter != nil:
|
||||
try:
|
||||
errorMessageWriter($data)
|
||||
errorMessageWriter(cstrToStrBuiltin data)
|
||||
toWrite = false
|
||||
except:
|
||||
discard
|
||||
@@ -261,7 +263,10 @@ 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)
|
||||
var i = 0
|
||||
while f.procname[i] != '\0':
|
||||
add(s, f.procname[i])
|
||||
inc i
|
||||
when NimStackTraceMsgs:
|
||||
when typeof(f) is StackTraceEntry:
|
||||
add(s, f.frameMsg)
|
||||
@@ -283,8 +288,34 @@ proc `$`(stackTraceEntries: seq[StackTraceEntry]): string =
|
||||
else: addFrameEntry(result, s[i])
|
||||
|
||||
when hasSomeStackTrace:
|
||||
const
|
||||
Ten = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||
|
||||
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||
proc i2s(x: int64): string =
|
||||
# quick reimplementation; optimized for code size, no dependencies
|
||||
if x < 0:
|
||||
if x == -9223372036854775808:
|
||||
result = "-9223372036854775808"
|
||||
else:
|
||||
result = "-" & i2s(0-x)
|
||||
elif x < 10:
|
||||
result = Ten[int x] # saves allocations
|
||||
else:
|
||||
var y = x
|
||||
while true:
|
||||
result.add char((y mod 10) + int('0'))
|
||||
y = y div 10
|
||||
if y == 0: break
|
||||
let last = result.len-1
|
||||
var i = 0
|
||||
let b = result.len div 2
|
||||
while i < b:
|
||||
let ch = result[i]
|
||||
result[i] = result[last-i]
|
||||
result[last-i] = ch
|
||||
inc i
|
||||
|
||||
proc auxWriteStackTrace(f: PFrame, s: var string) {.raises: [].} =
|
||||
when hasThreadSupport:
|
||||
var
|
||||
tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar
|
||||
@@ -322,14 +353,14 @@ when hasSomeStackTrace:
|
||||
for j in countdown(i-1, 0):
|
||||
if tempFrames[j] == nil:
|
||||
add(s, "(")
|
||||
add(s, $skipped)
|
||||
s.add(i2s(skipped))
|
||||
add(s, " calls omitted) ...\n")
|
||||
else:
|
||||
addFrameEntry(s, tempFrames[j])
|
||||
|
||||
proc stackTraceAvailable*(): bool
|
||||
|
||||
proc rawWriteStackTrace(s: var string) =
|
||||
proc rawWriteStackTrace(s: var string) {.raises: [].} =
|
||||
when defined(nimStackTraceOverride):
|
||||
add(s, "Traceback (most recent call last, using override)\n")
|
||||
auxWriteStackTraceWithOverride(s)
|
||||
@@ -388,7 +419,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
|
||||
add(buf, "Error: unhandled exception: ")
|
||||
add(buf, e.msg)
|
||||
add(buf, " [")
|
||||
add(buf, $e.name)
|
||||
add(buf, cstrToStrBuiltin(e.name))
|
||||
add(buf, "]\n")
|
||||
|
||||
if onUnhandledException != nil:
|
||||
@@ -408,7 +439,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
|
||||
var buf: array[0..2000, char]
|
||||
var L = 0
|
||||
if e.trace.len != 0:
|
||||
var trace = $e.trace
|
||||
var trace = cstrToStrBuiltin(e.trace)
|
||||
add(buf, trace)
|
||||
{.gcsafe.}:
|
||||
`=destroy`(trace)
|
||||
@@ -559,10 +590,11 @@ 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"
|
||||
var msg = "Error: call depth limit reached in a debug build ("
|
||||
msg.add(i2s(nimCallDepthLimit))
|
||||
msg.add(" function calls). You can change it with " &
|
||||
"-d:nimCallDepthLimit=<int> but really try to avoid deep " &
|
||||
"recursions instead.\n")
|
||||
showErrorMessage2(msg)
|
||||
rawQuit(1)
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# ----------------- GC interface ---------------------------------------------
|
||||
const
|
||||
usesDestructors = defined(gcDestructors) or defined(gcHooks)
|
||||
|
||||
when not usesDestructors:
|
||||
{.pragma: nodestroy.}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
when notJSnotNims:
|
||||
proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Overwrites the contents of the memory at `p` with the value 0.
|
||||
##
|
||||
## Exactly `size` bytes will be overwritten. Like any procedure
|
||||
## dealing with raw memory this is **unsafe**.
|
||||
|
||||
proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
||||
tags: [], raises: [].}
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Copies the contents from the memory at `source` to the memory
|
||||
## at `dest`.
|
||||
## Exactly `size` bytes will be copied. The memory
|
||||
@@ -15,7 +15,7 @@ when notJSnotNims:
|
||||
## memory this is **unsafe**.
|
||||
|
||||
proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
||||
tags: [], raises: [].}
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Copies the contents from the memory at `source` to the memory
|
||||
## at `dest`.
|
||||
##
|
||||
@@ -25,7 +25,7 @@ when notJSnotNims:
|
||||
## dealing with raw memory this is still **unsafe**, though.
|
||||
|
||||
proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Compares the memory blocks `a` and `b`. `size` bytes will
|
||||
## be compared.
|
||||
##
|
||||
@@ -34,7 +34,7 @@ when notJSnotNims:
|
||||
## **unsafe**.
|
||||
|
||||
proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Compares the memory blocks `a` and `b`. `size` bytes will
|
||||
## be compared.
|
||||
##
|
||||
|
||||
@@ -23,6 +23,8 @@ type
|
||||
|
||||
const nimStrVersion {.core.} = 2
|
||||
|
||||
{.push overflowChecks: off, rangeChecks: off.}
|
||||
|
||||
template isLiteral(s): bool = (s.p == nil) or (s.p.cap and strlitFlag) == strlitFlag
|
||||
|
||||
template contentSize(cap): int = cap + 1 + sizeof(NimStrPayloadBase)
|
||||
@@ -227,3 +229,5 @@ func capacity*(self: string): int {.inline.} =
|
||||
|
||||
let str = cast[ptr NimStringV2](unsafeAddr self)
|
||||
result = if str.p != nil: str.p.cap and not strlitFlag else: 0
|
||||
|
||||
{.pop.}
|
||||
|
||||
Reference in New Issue
Block a user