Merge branch 'devel' into araq-quirky-exceptions

This commit is contained in:
Andreas Rumpf 2019-02-08 17:18:17 +01:00
commit 0841c64a32
347 changed files with 18361 additions and 16065 deletions

View file

@ -20,7 +20,7 @@ template track(op, address, size) =
# Each chunk starts at an address that is divisible by the page size.
const
InitialMemoryRequest = 128 * PageSize # 0.5 MB
nimMinHeapPages {.intdefine.} = 128 # 0.5 MB
SmallChunkSize = PageSize
MaxFli = 30
MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32'
@ -588,8 +588,8 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
result = findSuitableBlock(a, fl, sl)
if result == nil:
if size < InitialMemoryRequest:
result = requestOsChunks(a, InitialMemoryRequest)
if size < nimMinHeapPages * PageSize:
result = requestOsChunks(a, nimMinHeapPages * PageSize)
splitChunk(a, result, size)
else:
result = requestOsChunks(a, size)

View file

@ -111,22 +111,27 @@ type c_sighandler_t = proc (a: cint) {.noconv.}
proc c_signal(sign: cint, handler: proc (a: cint) {.noconv.}): c_sighandler_t {.
importc: "signal", header: "<signal.h>", discardable.}
proc c_fprintf(f: File, frmt: cstring): cint {.
type
CFile {.importc: "FILE", header: "<stdio.h>",
incompletestruct.} = object
CFilePtr* = ptr CFile ## The type representing a file handle.
var
cstderr* {.importc: "stderr", header: "<stdio.h>".}: CFilePtr
cstdout* {.importc: "stdout", header: "<stdio.h>".}: CFilePtr
proc c_fprintf(f: CFilePtr, frmt: cstring): cint {.
importc: "fprintf", header: "<stdio.h>", varargs, discardable.}
proc c_printf(frmt: cstring): cint {.
importc: "printf", header: "<stdio.h>", varargs, discardable.}
proc c_fputs(c: cstring, f: CFilePtr): cint {.
importc: "fputs", header: "<stdio.h>", discardable.}
proc c_sprintf(buf, frmt: cstring): cint {.
importc: "sprintf", header: "<stdio.h>", varargs, noSideEffect.}
# we use it only in a way that cannot lead to security issues
when defined(windows):
proc c_fileno(f: File): cint {.
importc: "_fileno", header: "<stdio.h>".}
else:
proc c_fileno(f: File): cint {.
importc: "fileno", header: "<fcntl.h>".}
proc c_malloc(size: csize): pointer {.
importc: "malloc", header: "<stdlib.h>".}
proc c_free(p: pointer) {.

View file

@ -197,26 +197,40 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
proc divInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """
mov eax, ecx
mov ecx, edx
xor edx, edx
idiv ecx
jno theEnd
call `raiseOverflow`
theEnd:
test edx, edx
jne L_NOT_ZERO
call `raiseDivByZero`
L_NOT_ZERO:
cmp ecx, 0x80000000
jne L_DO_DIV
cmp edx, -1
jne L_DO_DIV
call `raiseOverflow`
L_DO_DIV:
mov eax, ecx
mov ecx, edx
cdq
idiv ecx
ret
"""
proc modInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """
mov eax, ecx
mov ecx, edx
xor edx, edx
idiv ecx
jno theEnd
call `raiseOverflow`
theEnd:
mov eax, edx
test edx, edx
jne L_NOT_ZERO
call `raiseDivByZero`
L_NOT_ZERO:
cmp ecx, 0x80000000
jne L_DO_DIV
cmp edx, -1
jne L_DO_DIV
call `raiseOverflow`
L_DO_DIV:
mov eax, ecx
mov ecx, edx
cdq
idiv ecx
mov eax, edx
ret
"""

View file

@ -8,7 +8,7 @@
#
# Implementation of some runtime checks.
import system/helpers2
import system/indexerrors
proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} =
when hostOS == "standalone":

View file

@ -19,11 +19,11 @@ const
proc nimLoadLibraryError(path: string) =
# carefully written to avoid memory allocation:
stderr.rawWrite("could not load: ")
stderr.rawWrite(path)
stderr.rawWrite("\n")
cstderr.rawWrite("could not load: ")
cstderr.rawWrite(path)
cstderr.rawWrite("\n")
when not defined(nimDebugDlOpen) and not defined(windows):
stderr.rawWrite("compile with -d:nimDebugDlOpen for more information\n")
cstderr.rawWrite("compile with -d:nimDebugDlOpen for more information\n")
when defined(windows) and defined(guiapp):
# Because console output is not shown in GUI apps, display error as message box:
const prefix = "could not load: "
@ -35,9 +35,9 @@ proc nimLoadLibraryError(path: string) =
proc procAddrError(name: cstring) {.noinline.} =
# carefully written to avoid memory allocation:
stderr.rawWrite("could not import: ")
stderr.rawWrite(name)
stderr.rawWrite("\n")
cstderr.rawWrite("could not import: ")
cstderr.rawWrite(name)
cstderr.rawWrite("\n")
quit(1)
# this code was inspired from Lua's source code:
@ -79,8 +79,8 @@ when defined(posix):
when defined(nimDebugDlOpen):
let error = dlerror()
if error != nil:
stderr.rawWrite(error)
stderr.rawWrite("\n")
cstderr.rawWrite(error)
cstderr.rawWrite("\n")
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
result = dlsym(lib, name)
@ -162,20 +162,20 @@ elif defined(genode):
elif defined(nintendoswitch):
proc nimUnloadLibrary(lib: LibHandle) =
stderr.rawWrite("nimUnLoadLibrary not implemented")
stderr.rawWrite("\n")
cstderr.rawWrite("nimUnLoadLibrary not implemented")
cstderr.rawWrite("\n")
quit(1)
proc nimLoadLibrary(path: string): LibHandle =
stderr.rawWrite("nimLoadLibrary not implemented")
stderr.rawWrite("\n")
cstderr.rawWrite("nimLoadLibrary not implemented")
cstderr.rawWrite("\n")
quit(1)
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
stderr.rawWrite("nimGetProAddr not implemented")
stderr.rawWrite(name)
stderr.rawWrite("\n")
cstderr.rawWrite("nimGetProAddr not implemented")
cstderr.rawWrite(name)
cstderr.rawWrite("\n")
quit(1)
else:

View file

@ -40,6 +40,7 @@ proc reraiseException() {.compilerRtl.} =
proc writeStackTrace() = discard
proc unsetControlCHook() = discard
proc setControlCHook(hook: proc () {.noconv.}) = discard
proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =

View file

@ -76,29 +76,52 @@ proc `==`(a, b: StaticStr): bool =
proc `==`(a: StaticStr, b: cstring): bool =
result = c_strcmp(unsafeAddr a.data, b) == 0
proc write(f: File, s: StaticStr) =
proc write(f: CFilePtr, s: cstring) = c_fputs(s, f)
proc writeLine(f: CFilePtr, s: cstring) =
c_fputs(s, f)
c_fputs("\n", f)
proc write(f: CFilePtr, s: StaticStr) =
write(f, cstring(unsafeAddr s.data))
proc listBreakPoints() =
write(stdout, EndbBeg)
write(stdout, "| Breakpoints:\n")
for b in listBreakpoints():
write(stdout, abs(b.low))
if b.high != b.low:
write(stdout, "..")
write(stdout, abs(b.high))
write(stdout, " ")
write(stdout, b.filename)
if b.isActive:
write(stdout, " [disabled]\n")
else:
write(stdout, "\n")
write(stdout, EndbEnd)
proc write(f: CFilePtr, i: int) =
when sizeof(int) == 8:
discard c_fprintf(f, "%lld", i)
else:
discard c_fprintf(f, "%ld", i)
proc openAppend(filename: cstring): File =
var p: pointer = fopen(filename, "ab")
if p != nil:
result = cast[File](p)
proc close(f: CFilePtr): cint {.
importc: "fclose", header: "<stdio.h>", discardable.}
proc c_fgetc(stream: CFilePtr): cint {.
importc: "fgetc", header: "<stdio.h>".}
proc c_ungetc(c: cint, f: CFilePtr): cint {.
importc: "ungetc", header: "<stdio.h>", discardable.}
var
cstdin* {.importc: "stdin", header: "<stdio.h>".}: CFilePtr
proc listBreakPoints() =
write(cstdout, EndbBeg)
write(cstdout, "| Breakpoints:\n")
for b in listBreakpoints():
write(cstdout, abs(b.low))
if b.high != b.low:
write(cstdout, "..")
write(cstdout, abs(b.high))
write(cstdout, " ")
write(cstdout, b.filename)
if b.isActive:
write(cstdout, " [disabled]\n")
else:
write(cstdout, "\n")
write(cstdout, EndbEnd)
proc openAppend(filename: cstring): CFilePtr =
proc fopen(filename, mode: cstring): CFilePtr {.importc: "fopen", header: "<stdio.h>".}
result = fopen(filename, "ab")
if result != nil:
write(result, "----------------------------------------\n")
proc dbgRepr(p: pointer, typ: PNimType): string =
@ -112,12 +135,12 @@ proc dbgRepr(p: pointer, typ: PNimType): string =
# dec(recGcLock)
deinitReprClosure(cl)
proc writeVariable(stream: File, slot: VarSlot) =
proc writeVariable(stream: CFilePtr, slot: VarSlot) =
write(stream, slot.name)
write(stream, " = ")
writeLine(stream, dbgRepr(slot.address, slot.typ))
proc listFrame(stream: File, f: PFrame) =
proc listFrame(stream: CFilePtr, f: PFrame) =
write(stream, EndbBeg)
write(stream, "| Frame (")
write(stream, f.len)
@ -126,7 +149,7 @@ proc listFrame(stream: File, f: PFrame) =
writeLine(stream, getLocal(f, i).name)
write(stream, EndbEnd)
proc listLocals(stream: File, f: PFrame) =
proc listLocals(stream: CFilePtr, f: PFrame) =
write(stream, EndbBeg)
write(stream, "| Frame (")
write(stream, f.len)
@ -135,7 +158,7 @@ proc listLocals(stream: File, f: PFrame) =
writeVariable(stream, getLocal(f, i))
write(stream, EndbEnd)
proc listGlobals(stream: File) =
proc listGlobals(stream: CFilePtr) =
write(stream, EndbBeg)
write(stream, "| Globals:\n")
for i in 0 .. getGlobalLen()-1:
@ -145,10 +168,10 @@ proc listGlobals(stream: File) =
proc debugOut(msg: cstring) =
# the *** *** markers are for easy recognition of debugger
# output for external frontends.
write(stdout, EndbBeg)
write(stdout, "| ")
write(stdout, msg)
write(stdout, EndbEnd)
write(cstdout, EndbBeg)
write(cstdout, "| ")
write(cstdout, msg)
write(cstdout, EndbEnd)
proc dbgFatal(msg: cstring) =
debugOut(msg)
@ -157,20 +180,20 @@ proc dbgFatal(msg: cstring) =
proc dbgShowCurrentProc(dbgFramePointer: PFrame) =
if dbgFramePointer != nil:
write(stdout, "*** endb| now in proc: ")
write(stdout, dbgFramePointer.procname)
write(stdout, " ***\n")
write(cstdout, "*** endb| now in proc: ")
write(cstdout, dbgFramePointer.procname)
write(cstdout, " ***\n")
else:
write(stdout, "*** endb| (proc name not available) ***\n")
write(cstdout, "*** endb| (proc name not available) ***\n")
proc dbgShowExecutionPoint() =
write(stdout, "*** endb| ")
write(stdout, framePtr.filename)
write(stdout, "(")
write(stdout, framePtr.line)
write(stdout, ") ")
write(stdout, framePtr.procname)
write(stdout, " ***\n")
write(cstdout, "*** endb| ")
write(cstdout, framePtr.filename)
write(cstdout, "(")
write(cstdout, framePtr.line)
write(cstdout, ") ")
write(cstdout, framePtr.procname)
write(cstdout, " ***\n")
proc scanAndAppendWord(src: cstring, a: var StaticStr, start: int): int =
result = start
@ -279,7 +302,7 @@ proc breakpointToggle(s: cstring, start: int) =
if not b.isNil: b.flip
else: debugOut("[Warning] unknown breakpoint ")
proc dbgEvaluate(stream: File, s: cstring, start: int, f: PFrame) =
proc dbgEvaluate(stream: CFilePtr, s: cstring, start: int, f: PFrame) =
var dbgTemp: StaticStr
var i = scanWord(s, dbgTemp, start)
while s[i] in {' ', '\t'}: inc(i)
@ -315,8 +338,8 @@ proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
var dbgTemp: StaticStr
var i = scanFilename(s, dbgTemp, start)
if dbgTemp.len == 0:
# just write it to stdout:
listFrame(stdout, currFrame)
# just write it to cstdout:
listFrame(cstdout, currFrame)
else:
var stream = openAppend(addr dbgTemp.data)
if stream == nil:
@ -325,7 +348,7 @@ proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
listFrame(stream, currFrame)
close(stream)
proc readLine(f: File, line: var StaticStr): bool =
proc readLine(f: CFilePtr, line: var StaticStr): bool =
while true:
var c = c_fgetc(f)
if c < 0'i32:
@ -340,16 +363,16 @@ proc readLine(f: File, line: var StaticStr): bool =
result = true
proc listFilenames() =
write(stdout, EndbBeg)
write(stdout, "| Files:\n")
write(cstdout, EndbBeg)
write(cstdout, "| Files:\n")
var i = 0
while true:
let x = dbgFilenames[i]
if x.isNil: break
write(stdout, x)
write(stdout, "\n")
write(cstdout, x)
write(cstdout, "\n")
inc i
write(stdout, EndbEnd)
write(cstdout, EndbEnd)
proc dbgWriteStackTrace(f: PFrame)
proc commandPrompt() =
@ -361,10 +384,10 @@ proc commandPrompt() =
dbgTemp: StaticStr
while again:
write(stdout, "*** endb| >>")
write(cstdout, "*** endb| >>")
let oldLen = dbgUser.len
dbgUser.len = 0
if not readLine(stdin, dbgUser): break
if not readLine(cstdin, dbgUser): break
if dbgUser.len == 0: dbgUser.len = oldLen
# now look what we have to do:
var i = scanWord(addr dbgUser.data, dbgTemp, 0)
@ -398,7 +421,7 @@ proc commandPrompt() =
prevState = dbgState
prevSkipFrame = dbgSkipToFrame
dbgState = dbSkipCurrent
dbgEvaluate(stdout, addr dbgUser.data, i, dbgFramePtr)
dbgEvaluate(cstdout, addr dbgUser.data, i, dbgFramePtr)
dbgState = prevState
dbgSkipToFrame = prevSkipFrame
elif ?"o" or ?"out":
@ -412,7 +435,7 @@ proc commandPrompt() =
prevState = dbgState
prevSkipFrame = dbgSkipToFrame
dbgState = dbSkipCurrent
listLocals(stdout, dbgFramePtr)
listLocals(cstdout, dbgFramePtr)
dbgState = prevState
dbgSkipToFrame = prevSkipFrame
elif ?"g" or ?"globals":
@ -420,7 +443,7 @@ proc commandPrompt() =
prevState = dbgState
prevSkipFrame = dbgSkipToFrame
dbgState = dbSkipCurrent
listGlobals(stdout)
listGlobals(cstdout)
dbgState = prevState
dbgSkipToFrame = prevSkipFrame
elif ?"u" or ?"up":
@ -501,29 +524,29 @@ proc dbgWriteStackTrace(f: PFrame) =
b = b.prev
for j in countdown(i-1, 0):
if tempFrames[j] == nil:
write(stdout, "(")
write(stdout, skipped)
write(stdout, " calls omitted) ...")
write(cstdout, "(")
write(cstdout, skipped)
write(cstdout, " calls omitted) ...")
else:
write(stdout, tempFrames[j].filename)
write(cstdout, tempFrames[j].filename)
if tempFrames[j].line > 0:
write(stdout, '(')
write(stdout, tempFrames[j].line)
write(stdout, ')')
write(stdout, ' ')
write(stdout, tempFrames[j].procname)
write(stdout, "\n")
write(cstdout, "(")
write(cstdout, tempFrames[j].line)
write(cstdout, ")")
write(cstdout, " ")
write(cstdout, tempFrames[j].procname)
write(cstdout, "\n")
proc checkForBreakpoint =
let b = checkBreakpoints(framePtr.filename, framePtr.line)
if b != nil:
write(stdout, "*** endb| reached ")
write(stdout, framePtr.filename)
write(stdout, "(")
write(stdout, framePtr.line)
write(stdout, ") ")
write(stdout, framePtr.procname)
write(stdout, " ***\n")
write(cstdout, "*** endb| reached ")
write(cstdout, framePtr.filename)
write(cstdout, "(")
write(cstdout, framePtr.line)
write(cstdout, ") ")
write(cstdout, framePtr.procname)
write(cstdout, " ***\n")
commandPrompt()
proc lineHookImpl() {.nimcall.} =

View file

@ -17,15 +17,15 @@ var
## instead of stdmsg.write when printing stacktrace.
## Unstable API.
proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {.
proc c_fwrite(buf: pointer, size, n: csize, f: CFilePtr): cint {.
importc: "fwrite", header: "<stdio.h>".}
proc rawWrite(f: File, s: string|cstring) =
proc rawWrite(f: CFilePtr, s: string|cstring) =
# we cannot throw an exception here!
discard c_fwrite(cstring(s), 1, s.len, f)
when not defined(windows) or not defined(guiapp):
proc writeToStdErr(msg: cstring) = rawWrite(stdmsg, msg)
proc writeToStdErr(msg: cstring) = rawWrite(cstderr, msg)
else:
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
@ -220,11 +220,12 @@ proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
inc(i)
it = it.prev
var last = i-1
if s.len == 0:
s = newSeq[StackTraceEntry](i)
else:
last = s.len + i - 1
s.setLen(last+1)
when true: # not defined(gcDestructors):
if s.len == 0:
s = newSeq[StackTraceEntry](i)
else:
last = s.len + i - 1
s.setLen(last+1)
it = f
while it != nil:
s[last] = StackTraceEntry(procname: it.procname,
@ -347,26 +348,19 @@ proc raiseExceptionAux(e: ref Exception) =
if globalRaiseHook != nil:
if not globalRaiseHook(e): return
when defined(cpp) and not defined(noCppExceptions):
if e[] of OutOfMemError:
showErrorMessage(e.name)
quitOrDebug()
else:
pushCurrentException(e)
raiseCounter.inc
if raiseCounter == 0:
raiseCounter.inc # skip zero at overflow
e.raiseId = raiseCounter
{.emit: "`e`->raise();".}
pushCurrentException(e)
raiseCounter.inc
if raiseCounter == 0:
raiseCounter.inc # skip zero at overflow
e.raiseId = raiseCounter
{.emit: "`e`->raise();".}
elif defined(nimQuirky):
if currException == nil: currException = e
pushCurrentException(e)
else:
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)
@ -449,33 +443,29 @@ proc getStackTrace(e: ref Exception): string =
else:
result = ""
when not defined(gcDestructors):
proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
## Returns the attached stack trace to the exception ``e`` as
## a ``seq``. This is not yet available for the JS backend.
proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
## Returns the attached stack trace to the exception ``e`` as
## a ``seq``. This is not yet available for the JS backend.
when not defined(gcDestructors):
shallowCopy(result, e.trace)
else:
result = move(e.trace)
when defined(nimRequiresNimFrame):
const nimCallDepthLimit {.intdefine.} = 2000
const nimCallDepthLimit {.intdefine.} = 2000
proc callDepthLimitReached() {.noinline.} =
writeStackTrace()
showErrorMessage("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")
quitOrDebug()
proc callDepthLimitReached() {.noinline.} =
writeStackTrace()
showErrorMessage("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")
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 == nimCallDepthLimit: callDepthLimitReached()
else:
proc pushFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} =
# XXX only for backwards compatibility
s.prev = framePtr
framePtr = s
proc nimFrame(s: PFrame) {.compilerRtl, inl.} =
s.calldepth = if framePtr == nil: 0 else: framePtr.calldepth+1
s.prev = framePtr
framePtr = s
if s.calldepth == nimCallDepthLimit: callDepthLimitReached()
when defined(endb):
var
@ -538,3 +528,8 @@ proc setControlCHook(hook: proc () {.noconv.}) =
# ugly cast, but should work on all architectures:
type SignalHandler = proc (sign: cint) {.noconv, benign.}
c_signal(SIGINT, cast[SignalHandler](hook))
when not defined(noSignalHandler) and not defined(useNimRtl):
proc unsetControlCHook() =
# proc to unset a hook set by setControlCHook
c_signal(SIGINT, signalHandler)

View file

@ -104,7 +104,11 @@ when not defined(useNimRtl):
template gcAssert(cond: bool, msg: string) =
when defined(useGcAssert):
if not cond:
echo "[GCASSERT] ", msg
cstderr.rawWrite "[GCASSERT] "
cstderr.rawWrite msg
when defined(logGC):
cstderr.rawWrite "[GCASSERT] statistics:\L"
cstderr.rawWrite GC_getStatistics()
GC_disable()
writeStackTrace()
#var x: ptr int
@ -159,6 +163,10 @@ when defined(logGC):
c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld; thread=%ld\n",
msg, c, kind, typName, c.refcount shr rcShift, gch.gcThreadId)
template logCell(msg: cstring, c: PCell) =
when defined(logGC):
writeCell(msg, c)
template gcTrace(cell, state: untyped) =
when traceGC: traceCell(cell, state)
@ -174,7 +182,7 @@ proc incRef(c: PCell) {.inline.} =
gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
c.refcount = c.refcount +% rcIncrement
# and not colorMask
#writeCell("incRef", c)
logCell("incRef", c)
proc nimGCref(p: pointer) {.compilerProc.} =
# we keep it from being collected by pretending it's not even allocated:
@ -192,6 +200,7 @@ proc decRef(c: PCell) {.inline.} =
c.refcount = c.refcount -% rcIncrement
if c.refcount <% rcIncrement:
rtlAddZCT(c)
logCell("decRef", c)
proc nimGCunref(p: pointer) {.compilerProc.} =
let cell = usrToCell(p)
@ -410,7 +419,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
# its refcount is zero, so add it to the ZCT:
addNewObjToZCT(res, gch)
when logGC: writeCell("new cell", res)
logCell("new cell", res)
track("rawNewObj", res, size)
gcTrace(res, csAllocated)
when useCellIds:
@ -455,7 +464,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
setFrameInfo(res)
res.refcount = rcIncrement # refcount is 1
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
when logGC: writeCell("new cell", res)
logCell("new cell", res)
track("newObjRC1", res, size)
gcTrace(res, csAllocated)
when useCellIds:
@ -493,9 +502,8 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
# This can be wrong for intermediate temps that are nevertheless on the
# heap because of lambda lifting:
#gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
when logGC:
writeCell("growObj old cell", ol)
writeCell("growObj new cell", res)
logCell("growObj old cell", ol)
logCell("growObj new cell", res)
gcTrace(ol, csZctFreed)
gcTrace(res, csAllocated)
track("growObj old", ol, 0)
@ -547,7 +555,7 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) =
prepareDealloc(c)
gcTrace(c, csCycFreed)
track("cycle collector dealloc cell", c, 0)
when logGC: writeCell("cycle collector dealloc cell", c)
logCell("cycle collector dealloc cell", c)
when reallyDealloc:
sysAssert(allocInv(gch.region), "free cyclic cell")
beforeDealloc(gch, c, "freeCyclicCell: stack trash")
@ -616,7 +624,7 @@ proc doOperation(p: pointer, op: WalkOp) =
# c_fprintf(stdout, "[GC] decref bug: %p", c)
gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
when logGC: writeCell("decref (from doOperation)", c)
logCell("decref (from doOperation)", c)
track("waZctDecref", p, 0)
decRef(c)
of waPush:
@ -704,7 +712,7 @@ proc collectZCT(gch: var GcHeap): bool =
# as this might be too slow.
# In any case, it should be removed from the ZCT. But not
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
when logGC: writeCell("zct dealloc cell", c)
logCell("zct dealloc cell", c)
track("zct dealloc cell", c, 0)
gcTrace(c, csZctFreed)
# We are about to free the object, call the finalizer BEFORE its
@ -858,6 +866,10 @@ when not defined(useNimRtl):
for stack in items(gch.stack):
result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
else:
# this caused memory leaks, see #10488 ; find a way without `repr`
# maybe using a local copy of strutils.toHex or snprintf
when defined(logGC):
result.add "[GC] stack bottom: " & gch.stack.bottom.repr
result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
{.pop.} # profiler: off, stackTrace: off

View file

@ -418,7 +418,7 @@ proc prepareDealloc(cell: PCell) =
decTypeSize(cell, t)
proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
## Frees the thread local heap. Runs every finalizer if ``runFinalizers```
## Frees the thread local heap. Runs every finalizer if ``runFinalizers``
## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
## happens to ensure the GC can continue to work after the call
## to ``deallocHeap``.
@ -457,7 +457,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
globalMarkers[globalMarkersLen] = markerProc
inc globalMarkersLen
else:
echo "[GC] cannot register global variable; too many global variables"
cstderr.rawWrite("[GC] cannot register global variable; too many global variables")
quit 1
proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
@ -465,5 +465,5 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerProc.}
threadLocalMarkers[threadLocalMarkersLen] = markerProc
inc threadLocalMarkersLen
else:
echo "[GC] cannot register thread local variable; too many thread local variables"
cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables")
quit 1

View file

@ -88,7 +88,8 @@ when not defined(useNimRtl):
template gcAssert(cond: bool, msg: string) =
when defined(useGcAssert):
if not cond:
echo "[GCASSERT] ", msg
cstderr.rawWrite "[GCASSERT] "
cstderr.rawWrite msg
quit 1
proc cellToUsr(cell: PCell): pointer {.inline.} =
@ -485,8 +486,9 @@ proc collectCTBody(gch: var GcHeap) =
sysAssert(allocInv(gch.region), "collectCT: end")
proc collectCT(gch: var GcHeap; size: int) =
let fmem = getFreeMem(gch.region)
if (getOccupiedMem(gch.region) >= gch.cycleThreshold or
size > getFreeMem(gch.region)) and gch.recGcLock == 0:
size > fmem and fmem > InitialThreshold) and gch.recGcLock == 0:
collectCTBody(gch)
when not defined(useNimRtl):

View file

@ -23,6 +23,21 @@ when defined(nimphpext):
proc osDeallocPages(p: pointer, size: int) {.inline.} =
efree(p)
elif defined(useMalloc):
proc roundup(x, v: int): int {.inline.} =
result = (x + (v-1)) and not (v-1)
proc emalloc(size: int): pointer {.importc: "malloc", header: "<stdlib.h>".}
proc efree(mem: pointer) {.importc: "free", header: "<stdlib.h>".}
proc osAllocPages(size: int): pointer {.inline.} =
emalloc(size)
proc osTryAllocPages(size: int): pointer {.inline.} =
emalloc(size)
proc osDeallocPages(p: pointer, size: int) {.inline.} =
efree(p)
else:
include osalloc
@ -108,6 +123,8 @@ template `+!`(p: pointer, s: int): pointer =
template `-!`(p: pointer, s: int): pointer =
cast[pointer](cast[int](p) -% s)
const nimMinHeapPages {.intdefine.} = 4
proc allocSlowPath(r: var MemRegion; size: int) =
# we need to ensure that the underlying linked list
# stays small. Say we want to grab 16GB of RAM with some
@ -116,9 +133,8 @@ proc allocSlowPath(r: var MemRegion; size: int) =
# 8MB, 16MB, 32MB, 64MB, 128MB, 512MB, 1GB, 2GB, 4GB, 8GB,
# 16GB --> list contains only 20 elements! That's reasonable.
if (r.totalSize and 1) == 0:
r.nextChunkSize =
if r.totalSize < 64 * 1024: PageSize*4
else: r.nextChunkSize*2
r.nextChunkSize = if r.totalSize < 64 * 1024: PageSize*nimMinHeapPages
else: r.nextChunkSize*2
var s = roundup(size+sizeof(BaseChunk), PageSize)
var fresh: Chunk
if s > r.nextChunkSize:
@ -179,6 +195,19 @@ proc runFinalizers(c: Chunk) =
(cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader))
it = it.nextFinal
proc runFinalizers(c: Chunk; newbump: pointer) =
var it = c.head
var prev: ptr ObjHeader = nil
while it != nil:
let nxt = it.nextFinal
if it >= newbump:
if it.typ != nil and it.typ.finalizer != nil:
(cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader))
elif prev != nil:
prev.nextFinal = nil
prev = it
it = nxt
proc dealloc(r: var MemRegion; p: pointer; size: int) =
let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader))
if it.typ != nil and it.typ.finalizer != nil:
@ -221,16 +250,15 @@ template computeRemaining(r): untyped =
proc setObstackPtr*(r: var MemRegion; sp: StackPtr) =
# free everything after 'sp':
if sp.current.next != nil:
if sp.current != nil and sp.current.next != nil:
deallocAll(r, sp.current.next)
sp.current.next = nil
when false:
# better leak this memory than be sorry:
for i in 0..high(r.freeLists): r.freeLists[i] = nil
r.holes = nil
#else:
# deallocAll(r, r.head)
# r.head = nil
if r.tail != nil: runFinalizers(r.tail, sp.bump)
r.bump = sp.bump
r.tail = sp.current
r.remaining = sp.remaining
@ -241,6 +269,13 @@ proc deallocAll*() = tlRegion.deallocAll()
proc deallocOsPages(r: var MemRegion) = r.deallocAll()
when false:
let obs = obstackPtr()
try:
body
finally:
setObstackPtr(obs)
template withScratchRegion*(body: untyped) =
var scratch: MemRegion
let oldRegion = tlRegion

View file

@ -1,11 +1,28 @@
## helpers used system.nim and other modules, avoids code duplication while
## also minimizing symbols exposed in system.nim
# helpers used system.nim and other modules, avoids code duplication while
# also minimizing symbols exposed in system.nim
#
# TODO: move other things here that should not be exposed in system.nim
proc lineInfoToString(file: string, line, column: int): string =
file & "(" & $line & ", " & $column & ")"
proc `$`(info: type(instantiationInfo(0))): string =
type InstantiationInfo = tuple[filename: string, line: int, column: int]
proc `$`(info: InstantiationInfo): string =
# The +1 is needed here
# instead of overriding `$` (and changing its meaning), consider explicit name.
lineInfoToString(info.fileName, info.line, info.column+1)
proc isNamedTuple(T: type): bool =
## return true for named tuples, false for any other type.
when T isnot tuple: result = false
else:
var t: T
for name, _ in t.fieldPairs:
when name == "Field0":
return compiles(t.Field0)
else:
return true
# empty tuple should be un-named,
# see https://github.com/nim-lang/Nim/issues/8861#issue-356631191
return false

View file

@ -1,5 +0,0 @@
template formatErrorIndexBound*[T](i, a, b: T): string =
"index out of bounds: (a:" & $a & ") <= (i:" & $i & ") <= (b:" & $b & ") "
template formatErrorIndexBound*[T](i, n: T): string =
"index out of bounds: (i:" & $i & ") <= (n:" & $n & ") "

View file

@ -0,0 +1,7 @@
# imported by other modules, unlike helpers.nim which is included
template formatErrorIndexBound*[T](i, a, b: T): string =
"index out of bounds: (a: " & $a & ") <= (i: " & $i & ") <= (b: " & $b & ") "
template formatErrorIndexBound*[T](i, n: T): string =
"index out of bounds: (i: " & $i & ") <= (n: " & $n & ") "

View file

@ -1,19 +1,60 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2019 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
include inclrtl
# Nim's standard IO library. It contains high-performance
# routines for reading and writing data to (buffered) files or
# TTYs.
# ----------------- IO Part ------------------------------------------------
type
CFile {.importc: "FILE", header: "<stdio.h>",
incompletestruct.} = object
File* = ptr CFile ## The type representing a file handle.
{.push debugger:off .} # the user does not want to trace a part
# of the standard library!
FileMode* = enum ## The file mode when opening a file.
fmRead, ## Open the file for read access only.
fmWrite, ## Open the file for write access only.
## If the file does not exist, it will be
## created. Existing files will be cleared!
fmReadWrite, ## Open the file for read and write access.
## If the file does not exist, it will be
## created. Existing files will be cleared!
fmReadWriteExisting, ## Open the file for read and write access.
## If the file does not exist, it will not be
## created. The existing file will not be cleared.
fmAppend ## Open the file for writing only; append data
## at the end.
FileHandle* = cint ## type that represents an OS file handle; this is
## useful for low-level file access
# text file handling:
when not defined(nimscript) and not defined(js):
var
stdin* {.importc: "stdin", header: "<stdio.h>".}: File
## The standard input stream.
stdout* {.importc: "stdout", header: "<stdio.h>".}: File
## The standard output stream.
stderr* {.importc: "stderr", header: "<stdio.h>".}: File
## The standard error stream.
when defined(useStdoutAsStdmsg):
template stdmsg*: File = stdout
else:
template stdmsg*: File = stderr
## Template which expands to either stdout or stderr depending on
## `useStdoutAsStdmsg` compile-time switch.
when defined(windows):
proc c_fileno(f: File): cint {.
importc: "_fileno", header: "<stdio.h>".}
else:
proc c_fileno(f: File): cint {.
importc: "fileno", header: "<fcntl.h>".}
when defined(windows):
proc c_fdopen(filehandle: cint, mode: cstring): File {.
@ -68,6 +109,12 @@ proc c_ferror(f: File): cint {.
proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize): cint {.
importc: "setvbuf", header: "<stdio.h>", tags: [].}
proc c_fprintf(f: File, frmt: cstring): cint {.
importc: "fprintf", header: "<stdio.h>", varargs, discardable.}
template sysFatal(exc, msg) =
raise newException(exc, msg)
proc raiseEIO(msg: string) {.noinline, noreturn.} =
sysFatal(IOError, msg)
@ -91,34 +138,63 @@ proc checkErr(f: File) =
quit(1)
{.push stackTrace:off, profiler:off.}
proc readBuffer(f: File, buffer: pointer, len: Natural): int =
proc readBuffer*(f: File, buffer: pointer, len: Natural): int {.
tags: [ReadIOEffect], benign.} =
## reads `len` bytes into the buffer pointed to by `buffer`. Returns
## the actual number of bytes that have been read which may be less than
## `len` (if not as many bytes are remaining), but not greater.
result = c_fread(buffer, 1, len, f)
if result != len: checkErr(f)
proc readBytes(f: File, a: var openArray[int8|uint8], start, len: Natural): int =
proc readBytes*(f: File, a: var openArray[int8|uint8], start, len: Natural): int {.
tags: [ReadIOEffect], benign.} =
## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
## the actual number of bytes that have been read which may be less than
## `len` (if not as many bytes are remaining), but not greater.
result = readBuffer(f, addr(a[start]), len)
proc readChars(f: File, a: var openArray[char], start, len: Natural): int =
proc readChars*(f: File, a: var openArray[char], start, len: Natural): int {.
tags: [ReadIOEffect], benign.} =
## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
## the actual number of bytes that have been read which may be less than
## `len` (if not as many bytes are remaining), but not greater.
##
## **Warning:** The buffer `a` must be pre-allocated. This can be done
## using, for example, ``newString``.
if (start + len) > len(a):
raiseEIO("buffer overflow: (start+len) > length of openarray buffer")
result = readBuffer(f, addr(a[start]), len)
proc write(f: File, c: cstring) =
proc write*(f: File, c: cstring) {.tags: [WriteIOEffect], benign.} =
## Writes a value to the file `f`. May throw an IO exception.
discard c_fputs(c, f)
checkErr(f)
proc writeBuffer(f: File, buffer: pointer, len: Natural): int =
proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {.
tags: [WriteIOEffect], benign.} =
## writes the bytes of buffer pointed to by the parameter `buffer` to the
## file `f`. Returns the number of actual written bytes, which may be less
## than `len` in case of an error.
result = c_fwrite(buffer, 1, len, f)
checkErr(f)
proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int =
var x = cast[ptr UncheckedArray[int8]](a)
result = writeBuffer(f, addr(x[int(start)]), len)
proc writeChars(f: File, a: openArray[char], start, len: Natural): int =
proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {.
tags: [WriteIOEffect], benign.} =
## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
## the number of actual written bytes, which may be less than `len` in case
## of an error.
var x = cast[ptr UncheckedArray[int8]](a)
result = writeBuffer(f, addr(x[int(start)]), len)
proc write(f: File, s: string) =
proc writeChars*(f: File, a: openArray[char], start, len: Natural): int {.
tags: [WriteIOEffect], benign.} =
## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
## the number of actual written bytes, which may be less than `len` in case
## of an error.
var x = cast[ptr UncheckedArray[int8]](a)
result = writeBuffer(f, addr(x[int(start)]), len)
proc write*(f: File, s: string) {.tags: [WriteIOEffect], benign.} =
if writeBuffer(f, cstring(s), s.len) != s.len:
raiseEIO("cannot write string to file")
{.pop.}
@ -141,21 +217,40 @@ else:
const
BufSize = 4000
proc close*(f: File) =
proc close*(f: File) {.tags: [], gcsafe.} =
## Closes the file.
if not f.isNil:
discard c_fclose(f)
proc readChar(f: File): char =
proc readChar*(f: File): char {.tags: [ReadIOEffect].} =
## Reads a single character from the stream `f`. Should not be used in
## performance sensitive code.
let x = c_fgetc(f)
if x < 0:
checkErr(f)
raiseEOF()
result = char(x)
proc flushFile*(f: File) = discard c_fflush(f)
proc getFileHandle*(f: File): FileHandle = c_fileno(f)
proc flushFile*(f: File) {.tags: [WriteIOEffect].} =
## Flushes `f`'s buffer.
discard c_fflush(f)
proc getFileHandle*(f: File): FileHandle =
## returns the OS file handle of the file ``f``. This is only useful for
## platform specific programming.
c_fileno(f)
proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect],
benign.} =
## reads a line of text from the file `f` into `line`. May throw an IO
## exception.
## A line of text may be delimited by ``LF`` or ``CRLF``. The newline
## character(s) are not part of the returned string. Returns ``false``
## if the end of the file has been reached, ``true`` otherwise. If
## ``false`` is returned `line` contains no new data.
proc c_memchr(s: pointer, c: cint, n: csize): pointer {.
importc: "memchr", header: "<string.h>".}
proc readLine(f: File, line: var TaintedString): bool =
var pos = 0
# Use the currently reserved space for a first try
@ -165,7 +260,8 @@ proc readLine(f: File, line: var TaintedString): bool =
while true:
# memset to \L so that we can tell how far fgets wrote, even on EOF, where
# fgets doesn't append an \L
nimSetMem(addr line.string[pos], '\L'.ord, sp)
for i in 0..<sp: line.string[pos+i] = '\L'
var fgetsSuccess = c_fgets(addr line.string[pos], sp.cint, f) != nil
if not fgetsSuccess: checkErr(f)
let m = c_memchr(addr line.string[pos], '\L'.ord, sp)
@ -191,32 +287,37 @@ proc readLine(f: File, line: var TaintedString): bool =
sp = 128 # read in 128 bytes at a time
line.string.setLen(pos+sp)
proc readLine(f: File): TaintedString =
proc readLine*(f: File): TaintedString {.tags: [ReadIOEffect], benign.} =
## reads a line of text from the file `f`. May throw an IO exception.
## A line of text may be delimited by ``LF`` or ``CRLF``. The newline
## character(s) are not part of the returned string.
result = TaintedString(newStringOfCap(80))
if not readLine(f, result): raiseEOF()
proc write(f: File, i: int) =
proc write*(f: File, i: int) {.tags: [WriteIOEffect], benign.} =
when sizeof(int) == 8:
if c_fprintf(f, "%lld", i) < 0: checkErr(f)
else:
if c_fprintf(f, "%ld", i) < 0: checkErr(f)
proc write(f: File, i: BiggestInt) =
proc write*(f: File, i: BiggestInt) {.tags: [WriteIOEffect], benign.} =
when sizeof(BiggestInt) == 8:
if c_fprintf(f, "%lld", i) < 0: checkErr(f)
else:
if c_fprintf(f, "%ld", i) < 0: checkErr(f)
proc write(f: File, b: bool) =
proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} =
if b: write(f, "true")
else: write(f, "false")
proc write(f: File, r: float32) =
proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} =
if c_fprintf(f, "%.16g", r) < 0: checkErr(f)
proc write(f: File, r: BiggestFloat) =
proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.} =
if c_fprintf(f, "%.16g", r) < 0: checkErr(f)
proc write(f: File, c: char) = discard c_putc(cint(c), f)
proc write(f: File, a: varargs[string, `$`]) =
proc write*(f: File, c: char) {.tags: [WriteIOEffect], benign.} =
discard c_putc(cint(c), f)
proc write*(f: File, a: varargs[string, `$`]) {.tags: [WriteIOEffect], benign.} =
for x in items(a): write(f, x)
proc readAllBuffer(file: File): string =
@ -240,7 +341,8 @@ proc rawFileSize(file: File): int64 =
result = c_ftell(file)
discard c_fseek(file, oldPos, 0)
proc endOfFile(f: File): bool =
proc endOfFile*(f: File): bool {.tags: [], benign.} =
## Returns true iff `f` is at the end.
var c = c_fgetc(f)
discard c_ungetc(c, f)
return c < 0'i32
@ -263,7 +365,12 @@ proc readAllFile(file: File): string =
var len = rawFileSize(file)
result = readAllFile(file, len)
proc readAll(file: File): TaintedString =
proc readAll*(file: File): TaintedString {.tags: [ReadIOEffect], benign.} =
## Reads all data from the stream `file`.
##
## Raises an IO exception in case of an error. It is an error if the
## current file position is not at the beginning of the file.
# Separate handling needed because we need to buffer when we
# don't know the overall length of the File.
when declared(stdin):
@ -280,19 +387,16 @@ proc writeLn[Ty](f: File, x: varargs[Ty, `$`]) =
write(f, i)
write(f, "\n")
proc writeLine[Ty](f: File, x: varargs[Ty, `$`]) =
proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
tags: [WriteIOEffect], benign.} =
## writes the values `x` to `f` and then writes "\\n".
## May throw an IO exception.
for i in items(x):
write(f, i)
write(f, "\n")
when declared(stdout):
proc rawEcho(x: string) {.inline, compilerproc.} = write(stdout, x)
proc rawEchoNL() {.inline, compilerproc.} = write(stdout, "\n")
# interface to the C procs:
include "system/widestrs"
when defined(windows) and not defined(useWinAnsi):
when defined(cpp):
proc wfopen(filename, mode: WideCString): pointer {.
@ -356,9 +460,14 @@ when defined(posix) and not defined(nimscript):
proc c_fstat(a1: cint, a2: var Stat): cint {.
importc: "fstat", header: "<sys/stat.h>".}
proc open(f: var File, filename: string,
proc open*(f: var File, filename: string,
mode: FileMode = fmRead,
bufSize: int = -1): bool =
bufSize: int = -1): bool {.tags: [], raises: [], benign.} =
## Opens a file named `filename` with given `mode`.
##
## Default mode is readonly. Returns true iff the file could be opened.
## This throws no exception if the file could not be opened.
var p: pointer = fopen(filename, FormatOpen[mode])
if p != nil:
when defined(posix) and not defined(nimscript):
@ -377,49 +486,55 @@ proc open(f: var File, filename: string,
elif bufSize == 0:
discard c_setvbuf(f, nil, IONBF, 0)
proc reopen(f: File, filename: string, mode: FileMode = fmRead): bool =
proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {.
tags: [], benign.} =
## reopens the file `f` with given `filename` and `mode`. This
## is often used to redirect the `stdin`, `stdout` or `stderr`
## file variables.
##
## Default mode is readonly. Returns true iff the file could be reopened.
var p: pointer = freopen(filename, FormatOpen[mode], f)
result = p != nil
proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool =
proc open*(f: var File, filehandle: FileHandle,
mode: FileMode = fmRead): bool {.tags: [], raises: [], benign.} =
## Creates a ``File`` from a `filehandle` with given `mode`.
##
## Default mode is readonly. Returns true iff the file could be opened.
f = c_fdopen(filehandle, FormatOpen[mode])
result = f != nil
proc setFilePos(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) =
proc open*(filename: string,
mode: FileMode = fmRead, bufSize: int = -1): File =
## Opens a file named `filename` with given `mode`.
##
## Default mode is readonly. Raises an ``IOError`` if the file
## could not be opened.
if not open(result, filename, mode, bufSize):
sysFatal(IOError, "cannot open: " & filename)
proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.} =
## sets the position of the file pointer that is used for read/write
## operations. The file's first byte has the index zero.
if c_fseek(f, pos, cint(relativeTo)) != 0:
raiseEIO("cannot set file position")
proc getFilePos(f: File): int64 =
proc getFilePos*(f: File): int64 {.benign.} =
## retrieves the current position of the file pointer that is used to
## read from the file `f`. The file's first byte has the index zero.
result = c_ftell(f)
if result < 0: raiseEIO("cannot retrieve file position")
proc getFileSize(f: File): int64 =
proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} =
## retrieves the file size (in bytes) of `f`.
var oldPos = getFilePos(f)
discard c_fseek(f, 0, 2) # seek the end of the file
result = getFilePos(f)
setFilePos(f, oldPos)
proc readFile(filename: string): TaintedString =
var f: File
if open(f, filename):
try:
result = readAll(f).TaintedString
finally:
close(f)
else:
sysFatal(IOError, "cannot open: ", filename)
proc writeFile(filename, content: string) =
var f: File
if open(f, filename, fmWrite):
try:
f.write(content)
finally:
close(f)
else:
sysFatal(IOError, "cannot open: ", filename)
proc setStdIoUnbuffered() =
proc setStdIoUnbuffered*() {.tags: [], benign.} =
## Configures `stdin`, `stdout` and `stderr` to be unbuffered.
when declared(stdout):
discard c_setvbuf(stdout, nil, IONBF, 0)
when declared(stderr):
@ -429,6 +544,9 @@ proc setStdIoUnbuffered() =
when declared(stdout):
when defined(windows) and compileOption("threads"):
const insideRLocksModule = false
include "system/syslocks"
var echoLock: SysLock
initSysLock echoLock
@ -450,4 +568,81 @@ when declared(stdout):
when defined(windows) and compileOption("threads"):
releaseSys echoLock
{.pop.}
when defined(windows) and not defined(nimscript):
# work-around C's sucking abstraction:
# BUGFIX: stdin and stdout should be binary files!
proc c_setmode(handle, mode: cint) {.
importc: when defined(bcc): "setmode" else: "_setmode",
header: "<io.h>".}
var
O_BINARY {.importc: "_O_BINARY", header:"<fcntl.h>".}: cint
# we use binary mode on Windows:
c_setmode(c_fileno(stdin), O_BINARY)
c_setmode(c_fileno(stdout), O_BINARY)
c_setmode(c_fileno(stderr), O_BINARY)
proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} =
## Opens a file named `filename` for reading, calls `readAll
## <#readAll>`_ and closes the file afterwards. Returns the string.
## Raises an IO exception in case of an error. If # you need to call
## this inside a compile time macro you can use `staticRead
## <#staticRead>`_.
var f: File
if open(f, filename):
try:
result = readAll(f).TaintedString
finally:
close(f)
else:
sysFatal(IOError, "cannot open: " & filename)
proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} =
## Opens a file named `filename` for writing. Then writes the
## `content` completely to the file and closes the file afterwards.
## Raises an IO exception in case of an error.
var f: File
if open(f, filename, fmWrite):
try:
f.write(content)
finally:
close(f)
else:
sysFatal(IOError, "cannot open: " & filename)
iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} =
## Iterates over any line in the file named `filename`.
##
## If the file does not exist `IOError` is raised. The trailing newline
## character(s) are removed from the iterated lines. Example:
##
## .. code-block:: nim
## import strutils
##
## proc transformLetters(filename: string) =
## var buffer = ""
## for line in filename.lines:
## buffer.add(line.replace("a", "0") & '\x0A')
## writeFile(filename, buffer)
var f = open(filename, bufSize=8000)
defer: close(f)
var res = TaintedString(newStringOfCap(80))
while f.readLine(res): yield res
iterator lines*(f: File): TaintedString {.tags: [ReadIOEffect].} =
## Iterate over any line in the file `f`.
##
## The trailing newline character(s) are removed from the iterated lines.
## Example:
##
## .. code-block:: nim
## proc countZeros(filename: File): tuple[lines, zeros: int] =
## for line in filename.lines:
## for letter in line:
## if letter == '0':
## result.zeros += 1
## result.lines += 1
var res = TaintedString(newStringOfCap(80))
while f.readLine(res): yield res

View file

@ -46,7 +46,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
result[0] = x
proc isNimException(): bool {.asmNoStackFrame.} =
asm "return `lastJSError`.m_type;"
asm "return `lastJSError` && `lastJSError`.m_type;"
proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
if isNimException(): result = cast[ref Exception](lastJSError)

View file

@ -62,7 +62,7 @@ const
proc raiseOutOfMem() {.noinline.} =
if outOfMemHook != nil: outOfMemHook()
echo("out of memory")
cstderr.rawWrite("out of memory")
quit(1)
when defined(boehmgc):
@ -72,6 +72,8 @@ when defined(boehmgc):
proc boehmGCincremental {.
importc: "GC_enable_incremental", boehmGC.}
proc boehmGCfullCollect {.importc: "GC_gcollect", boehmGC.}
proc boehmGC_set_all_interior_pointers(flag: cint) {.
importc: "GC_set_all_interior_pointers", boehmGC.}
proc boehmAlloc(size: int): pointer {.importc: "GC_malloc", boehmGC.}
proc boehmAllocAtomic(size: int): pointer {.
importc: "GC_malloc_atomic", boehmGC.}
@ -148,6 +150,7 @@ when defined(boehmgc):
proc nimGC_setStackBottom(theStackBottom: pointer) = discard
proc initGC() =
boehmGC_set_all_interior_pointers(0)
boehmGCinit()
when hasThreadSupport:
boehmGC_allow_register_threads()

View file

@ -7,6 +7,7 @@
# distribution, for details about the copyright.
#
## To learn about scripting in Nim see `NimScript<nims.html>`_
# Nim's configuration system now uses Nim for scripting. This module provides
# a few things that are required for this to work.
@ -46,7 +47,7 @@ proc copyDir(src, dest: string) {.
tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
proc createDir(dir: string) {.tags: [WriteIOEffect], raises: [OSError].} =
builtin
proc getOsError: string = builtin
proc getError: string = builtin
proc setCurrentDir(dir: string) = builtin
proc getCurrentDir*(): string =
## Retrieves the current working directory.
@ -178,9 +179,12 @@ var
mode*: ScriptMode ## Set this to influence how mkDir, rmDir, rmFile etc.
## behave
template checkError(exc: untyped): untyped =
let err = getError()
if err.len > 0: raise newException(exc, err)
template checkOsError =
let err = getOsError()
if err.len > 0: raise newException(OSError, err)
checkError(OSError)
template log(msg: string, body: untyped) =
if mode in {ScriptMode.Verbose, ScriptMode.Whatif}:
@ -332,6 +336,26 @@ proc cppDefine*(define: string) =
## needs to be mangled.
builtin
proc stdinReadLine(): TaintedString {.
tags: [ReadIOEffect], raises: [IOError].} =
builtin
proc stdinReadAll(): TaintedString {.
tags: [ReadIOEffect], raises: [IOError].} =
builtin
proc readLineFromStdin*(): TaintedString {.raises: [IOError].} =
## Reads a line of data from stdin - blocks until \n or EOF which happens when stdin is closed
log "readLineFromStdin":
result = stdinReadLine()
checkError(EOFError)
proc readAllFromStdin*(): TaintedString {.raises: [IOError].} =
## Reads all data from stdin - blocks until EOF which happens when stdin is closed
log "readAllFromStdin":
result = stdinReadAll()
checkError(EOFError)
when not defined(nimble):
template `==?`(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
template task*(name: untyped; description: string; body: untyped): untyped =
@ -341,14 +365,15 @@ when not defined(nimble):
## .. code-block:: nim
## task build, "default build is via the C backend":
## setCommand "c"
proc `name Task`*() = body
proc `name Task`*() =
setCommand "nop"
body
let cmd = getCommand()
if cmd.len == 0 or cmd ==? "help":
setCommand "help"
writeTask(astToStr(name), description)
elif cmd ==? astToStr(name):
setCommand "nop"
`name Task`()
# nimble has its own implementation for these things.

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## Compilerprocs for strings that do not depend on the string implementation.
# Compilerprocs for strings that do not depend on the string implementation.
proc cmpStrings(a, b: string): int {.inline, compilerProc.} =
let alen = a.len

View file

@ -325,11 +325,8 @@ when not defined(useNimRtl):
when emulatedThreadVars:
if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
echo "too large thread local storage size requested ",
"(", nimThreadVarsSize(), "/", sizeof(ThreadLocalStorage), "). ",
"Use -d:\"nimTlsSize=", nimThreadVarsSize(),
"\" to preallocate sufficient storage."
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
when hasSharedHeap and not defined(boehmgc) and not defined(gogc) and not defined(nogc):

View file

@ -10,8 +10,8 @@
# Nim support for C/C++'s `wide strings`:idx:. This is part of the system
# module! Do not import it directly!
when not declared(ThisIsSystem):
{.error: "You must not import this module explicitly".}
#when not declared(ThisIsSystem):
# {.error: "You must not import this module explicitly".}
type
Utf16Char* = distinct int16