summary refs log tree commit diff stats
path: root/lib/pure/json.nim
diff options
context:
space:
mode:
authornarimiran <narimiran@disroot.org>2020-08-17 09:11:33 +0200
committernarimiran <narimiran@disroot.org>2020-08-17 09:11:33 +0200
commit39906c52c4fd075b813bc1e0f65a9e25a8e9e95a (patch)
treed8cc90f37699cfe3632b2c3a4a80ebbd3516edd6 /lib/pure/json.nim
parentc9fdad2c21a6394daec70e9d0e215176b69783ac (diff)
downloadNim-39906c52c4fd075b813bc1e0f65a9e25a8e9e95a.tar.gz
stale bot is now active only for pull requests
Diffstat (limited to 'lib/pure/json.nim')
0 files changed, 0 insertions, 0 deletions
> 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
#
#
#            Nim's Runtime Library
#        (c) Copyright 2015 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# Exception handling code. Carefully coded so that tiny programs which do not
# use the heap (and nor exceptions) do not include the GC or memory allocator.

var
  errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign.})
    ## Function that will be called
    ## instead of stdmsg.write when printing stacktrace.
    ## Unstable API.

when not defined(windows) or not defined(guiapp):
  proc writeToStdErr(msg: cstring) = write(stdmsg, 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 showErrorMessage(data: cstring) =
  if errorMessageWriter != nil:
    errorMessageWriter($data)
  else:
    writeToStdErr(data)

proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
proc chckNil(p: pointer) {.noinline, compilerproc, benign.}

var
  framePtr {.threadvar.}: PFrame
  excHandler {.threadvar.}: PSafePoint
    # list of exception handlers
    # a global variable for the root of all try blocks
  currException {.threadvar.}: ref Exception

proc popFrame {.compilerRtl, inl.} =
  framePtr = framePtr.prev

proc setFrame(s: PFrame) {.compilerRtl, inl.} =
  framePtr = s

proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
  s.hasRaiseAction = false
  s.prev = excHandler
  excHandler = s

proc popSafePoint {.compilerRtl, inl.} =
  excHandler = excHandler.prev

proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = 
  e.parent = currException
  currException = e

proc popCurrentException {.compilerRtl, inl.} =
  currException = currException.parent

# some platforms have native support for stack traces:
const
  nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and
                              not NimStackTrace
  hasSomeStackTrace = NimStackTrace or 
    defined(nativeStackTrace) and nativeStackTraceSupported

when defined(nativeStacktrace) and nativeStackTraceSupported:
  type
    TDl_info {.importc: "Dl_info", header: "<dlfcn.h>", 
               final, pure.} = object
      dli_fname: cstring
      dli_fbase: pointer
      dli_sname: cstring
      dli_saddr: pointer

  proc backtrace(symbols: ptr pointer, size: int): int {.
    importc: "backtrace", header: "<execinfo.h>".}
  proc dladdr(addr1: pointer, info: ptr TDl_info): int {.
    importc: "dladdr", header: "<dlfcn.h>".}

  when not hasThreadSupport:
    var
      tempAddresses: array [0..127, pointer] # should not be alloc'd on stack
      tempDlInfo: TDl_info

  proc auxWriteStackTraceWithBacktrace(s: var string) =
    when hasThreadSupport:
      var
        tempAddresses: array [0..127, pointer] # but better than a threadvar
        tempDlInfo: TDl_info
    # This is allowed to be expensive since it only happens during crashes
    # (but this way you don't need manual stack tracing)
    var size = backtrace(cast[ptr pointer](addr(tempAddresses)), 
                         len(tempAddresses))
    var enabled = false
    for i in 0..size-1:
      var dlresult = dladdr(tempAddresses[i], addr(tempDlInfo))
      if enabled:
        if dlresult != 0:
          var oldLen = s.len
          add(s, tempDlInfo.dli_fname)
          if tempDlInfo.dli_sname != nil:
            for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
            add(s, tempDlInfo.dli_sname)
        else:
          add(s, '?')
        add(s, "\n")
      else:
        if dlresult != 0 and tempDlInfo.dli_sname != nil and
            c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
          # Once we're past signalHandler, we're at what the user is
          # interested in
          enabled = true

when not hasThreadSupport:
  var
    tempFrames: array [0..127, PFrame] # should not be alloc'd on stack
  
proc auxWriteStackTrace(f: PFrame, s: var string) =
  when hasThreadSupport:
    var
      tempFrames: array [0..127, PFrame] # but better than a threadvar
  const
    firstCalls = 32
  var
    it = f
    i = 0
    total = 0
  # setup long head:
  while it != nil and i <= high(tempFrames)-firstCalls:
    tempFrames[i] = it
    inc(i)
    inc(total)
    it = it.prev
  # go up the stack to count 'total':
  var b = it
  while it != nil:
    inc(total)
    it = it.prev
  var skipped = 0
  if total > len(tempFrames):
    # skip N
    skipped = total-i-firstCalls+1
    for j in 1..skipped:
      if b != nil: b = b.prev
    # create '...' entry:
    tempFrames[i] = nil
    inc(i)
  # setup short tail:
  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, $skipped)
      add(s, " calls omitted) ...")
    else:
      var oldLen = s.len
      add(s, tempFrames[j].filename)
      if tempFrames[j].line > 0:
        add(s, '(')
        add(s, $tempFrames[j].line)
        add(s, ')')
      for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
      add(s, tempFrames[j].procname)
    add(s, "\n")

proc stackTraceAvailable*(): bool

when hasSomeStackTrace:
  proc rawWriteStackTrace(s: var string) =
    when NimStackTrace:
      if framePtr == nil:
        add(s, "No stack traceback available\n")
      else:
        add(s, "Traceback (most recent call last)\n")
        auxWriteStackTrace(framePtr, s)
    elif defined(nativeStackTrace) and nativeStackTraceSupported:
      add(s, "Traceback from system (most recent call last)\n")
      auxWriteStackTraceWithBacktrace(s)
    else:
      add(s, "No stack traceback available\n")
  proc stackTraceAvailable(): bool =
    when NimStackTrace:
      if framePtr == nil:
        result = false
      else:
        result = true
    elif defined(nativeStackTrace) and nativeStackTraceSupported:
      result = true
    else:
      result = false
else:
  proc stackTraceAvailable*(): bool = result = false

proc quitOrDebug() {.inline.} =
  when not defined(endb):
    quit(1)
  else:
    endbStep() # call the debugger

proc raiseExceptionAux(e: ref Exception) =
  if localRaiseHook != nil:
    if not localRaiseHook(e): return
  if globalRaiseHook != nil:
    if not globalRaiseHook(e): return
  if excHandler != nil:
    if not excHandler.hasRaiseAction or excHandler.raiseAction(e):
      pushCurrentException(e)
      c_longjmp(excHandler.context, 1)
  elif e[] of OutOfMemError:
    showErrorMessage(e.name)
    quitOrDebug()
  else:
    when hasSomeStackTrace:
      var buf = newStringOfCap(2000)
      if isNil(e.trace): rawWriteStackTrace(buf)
      else: add(buf, e.trace)
      add(buf, "Error: unhandled exception: ")
      if not isNil(e.msg): add(buf, e.msg)
      add(buf, " [")
      add(buf, $e.name)
      add(buf, "]\n")
      showErrorMessage(buf)
    else:
      # ugly, but avoids heap allocations :-)
      template xadd(buf, s, slen: expr) =
        if L + slen < high(buf):
          copyMem(addr(buf[L]), cstring(s), slen)
          inc L, slen
      template add(buf, s: expr) =
        xadd(buf, s, s.len)
      var buf: array [0..2000, char]
      var L = 0
      add(buf, "Error: unhandled exception: ")
      if not isNil(e.msg): add(buf, e.msg)
      add(buf, " [")
      xadd(buf, e.name, c_strlen(e.name))
      add(buf, "]\n")
      showErrorMessage(buf)
    quitOrDebug()

proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
  e.name = ename
  when hasSomeStackTrace:
    e.trace = ""
    rawWriteStackTrace(e.trace)
  raiseExceptionAux(e)

proc reraiseException() {.compilerRtl.} =
  if currException == nil:
    sysFatal(ReraiseError, "no exception to reraise")
  else:
    raiseExceptionAux(currException)

proc writeStackTrace() =
  when hasSomeStackTrace:
    var s = ""
    rawWriteStackTrace(s)
    showErrorMessage(s)
  else:
    showErrorMessage("No stack traceback available\n")

proc getStackTrace(): string =
  when hasSomeStackTrace:
    result = ""
    rawWriteStackTrace(result)
  else:
    result = "No stack traceback available\n"

proc getStackTrace(e: ref Exception): string =
  if not isNil(e) and not isNil(e.trace):
    result = e.trace
  else:
    result = ""

when defined(nimRequiresNimFrame):
  proc stackOverflow() {.noinline.} =
    writeStackTrace()
    showErrorMessage("Stack overflow\n")
    quitOrDebug()

  proc nimFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} =
    s.calldepth = if framePtr == nil: 0 else: framePtr.calldepth+1
    s.prev = framePtr
    framePtr = s
    if s.calldepth == 2000: stackOverflow()
else:
  proc pushFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} =
    # XXX only for backwards compatibility
    s.prev = framePtr
    framePtr = s

when defined(endb):
  var
    dbgAborting: bool # whether the debugger wants to abort

when not defined(noSignalHandler):
  proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} =
    template processSignal(s, action: expr) {.immediate,  dirty.} =
      if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n")
      elif s == SIGSEGV: 
        action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
      elif s == SIGABRT:
        when defined(endb):
          if dbgAborting: return # the debugger wants to abort
        action("SIGABRT: Abnormal termination.\n")
      elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n")
      elif s == SIGILL: action("SIGILL: Illegal operation.\n")
      elif s == SIGBUS:
        action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
      else:
        block platformSpecificSignal:
          when declared(SIGPIPE):
            if s == SIGPIPE:
              action("SIGPIPE: Pipe closed.\n")
              break platformSpecificSignal
          action("unknown signal\n")

    # print stack trace and quit
    when hasSomeStackTrace:
      GC_disable()
      var buf = newStringOfCap(2000)
      rawWriteStackTrace(buf)
      processSignal(sig, buf.add) # nice hu? currying a la Nim :-)
      showErrorMessage(buf)
      GC_enable()
    else:
      var msg: cstring
      template asgn(y: expr) = msg = y
      processSignal(sig, asgn)
      showErrorMessage(msg)
    when defined(endb): dbgAborting = true
    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 declared(SIGPIPE):
      c_signal(SIGPIPE, signalHandler)

  registerSignalHandler() # call it in initialization section

proc setControlCHook(hook: proc () {.noconv.} not nil) =
  # ugly cast, but should work on all architectures:
  type TSignalHandler = proc (sig: cint) {.noconv, benign.}
  c_signal(SIGINT, cast[TSignalHandler](hook))