Merge branch 'devel' into araq-quirky-exceptions
This commit is contained in:
commit
0841c64a32
347 changed files with 18361 additions and 16065 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#
|
||||
|
||||
# Implementation of some runtime checks.
|
||||
import system/helpers2
|
||||
import system/indexerrors
|
||||
|
||||
proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} =
|
||||
when hostOS == "standalone":
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.} =
|
||||
|
|
|
|||
|
|
@ -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.} =
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 & ") "
|
||||
7
lib/system/indexerrors.nim
Normal file
7
lib/system/indexerrors.nim
Normal 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 & ") "
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue