summary refs log tree commit diff stats
path: root/lib/system
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system')
-rw-r--r--lib/system/alloc.nim6
-rw-r--r--lib/system/ansi_c.nim21
-rw-r--r--lib/system/arithm.nim44
-rw-r--r--lib/system/chcks.nim2
-rw-r--r--lib/system/dyncalls.nim32
-rw-r--r--lib/system/embedded.nim1
-rw-r--r--lib/system/endb.nim153
-rw-r--r--lib/system/excpt.nim89
-rw-r--r--lib/system/gc.nim32
-rw-r--r--lib/system/gc_common.nim6
-rw-r--r--lib/system/gc_ms.nim6
-rw-r--r--lib/system/gc_regions.nim49
-rw-r--r--lib/system/helpers.nim23
-rw-r--r--lib/system/helpers2.nim5
-rw-r--r--lib/system/indexerrors.nim7
-rw-r--r--lib/system/io.nim (renamed from lib/system/sysio.nim)329
-rw-r--r--lib/system/jssys.nim2
-rw-r--r--lib/system/mmdisp.nim5
-rw-r--r--lib/system/nimscript.nim35
-rw-r--r--lib/system/strmantle.nim2
-rw-r--r--lib/system/threads.nim7
-rw-r--r--lib/system/widestrs.nim4
22 files changed, 593 insertions, 267 deletions
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index b090117a9..e938dc475 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -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)
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index af34060d8..552962775 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -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) {.
diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim
index 69c558799..a875e95a7 100644
--- a/lib/system/arithm.nim
+++ b/lib/system/arithm.nim
@@ -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
     """
 
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 6f4e8ce37..0840d863a 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -8,7 +8,7 @@
 #
 
 # Implementation of some runtime checks.
-import system/helpers2
+import system/indexerrors
 
 proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} =
   when hostOS == "standalone":
diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim
index 70bdc429b..528587d05 100644
--- a/lib/system/dyncalls.nim
+++ b/lib/system/dyncalls.nim
@@ -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:
diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim
index 4d453fcca..fb89e7f0f 100644
--- a/lib/system/embedded.nim
+++ b/lib/system/embedded.nim
@@ -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.} =
diff --git a/lib/system/endb.nim b/lib/system/endb.nim
index 257ee3fea..6c99f8d12 100644
--- a/lib/system/endb.nim
+++ b/lib/system/endb.nim
@@ -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 write(f: CFilePtr, i: int) =
+  when sizeof(int) == 8:
+    discard c_fprintf(f, "%lld", i)
+  else:
+    discard c_fprintf(f, "%ld", i)
+
+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(stdout, EndbBeg)
-  write(stdout, "| Breakpoints:\n")
+  write(cstdout, EndbBeg)
+  write(cstdout, "| Breakpoints:\n")
   for b in listBreakpoints():
-    write(stdout, abs(b.low))
+    write(cstdout, abs(b.low))
     if b.high != b.low:
-      write(stdout, "..")
-      write(stdout, abs(b.high))
-    write(stdout, " ")
-    write(stdout, b.filename)
+      write(cstdout, "..")
+      write(cstdout, abs(b.high))
+    write(cstdout, " ")
+    write(cstdout, b.filename)
     if b.isActive:
-      write(stdout, " [disabled]\n")
+      write(cstdout, " [disabled]\n")
     else:
-      write(stdout, "\n")
-  write(stdout, EndbEnd)
+      write(cstdout, "\n")
+  write(cstdout, EndbEnd)
+
+proc openAppend(filename: cstring): CFilePtr =
+  proc fopen(filename, mode: cstring): CFilePtr {.importc: "fopen", header: "<stdio.h>".}
 
-proc openAppend(filename: cstring): File =
-  var p: pointer = fopen(filename, "ab")
-  if p != nil:
-    result = cast[File](p)
+  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.} =
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 3c0e42c6e..fbfdc2532 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -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
-
-  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
+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 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)
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 9ae388aa9..d6d7da66e 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -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
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 565453298..5af64ae20 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -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
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index d8cb3e2d0..bd08eedf0 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -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):
diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim
index 8a1446944..797eeeebf 100644
--- a/lib/system/gc_regions.nim
+++ b/lib/system/gc_regions.nim
@@ -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
diff --git a/lib/system/helpers.nim b/lib/system/helpers.nim
index fb1218684..a7e47915e 100644
--- a/lib/system/helpers.nim
+++ b/lib/system/helpers.nim
@@ -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
diff --git a/lib/system/helpers2.nim b/lib/system/helpers2.nim
deleted file mode 100644
index 1c9e7c068..000000000
--- a/lib/system/helpers2.nim
+++ /dev/null
@@ -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 & ") "
diff --git a/lib/system/indexerrors.nim b/lib/system/indexerrors.nim
new file mode 100644
index 000000000..8bd69ad71
--- /dev/null
+++ b/lib/system/indexerrors.nim
@@ -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 & ") "
diff --git a/lib/system/sysio.nim b/lib/system/io.nim
index 5b0278d74..e93f602ae 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/io.nim
@@ -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
+
+# ----------------- IO Part ------------------------------------------------
+type
+  CFile {.importc: "FILE", header: "<stdio.h>",
+          incompletestruct.} = object
+  File* = ptr CFile ## The type representing a file handle.
+
+  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.
 
-# Nim's standard IO library. It contains high-performance
-# routines for reading and writing data to (buffered) files or
-# TTYs.
-
-{.push debugger:off .} # the user does not want to trace a part
-                       # of the standard library!
+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 =
+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 writeChars(f: File, a: openArray[char], start, len: Natural): int =
+
+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) =
+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
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 8be19e5b8..d7718e4f4 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -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)
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 89bc11d2c..9284f07d2 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -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()
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index 34daf30a9..1c4986aa4 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -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.
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index ceaecb4f9..3c681fc53 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -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
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index f89de4376..bbe170376 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -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):
diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim
index 85e5e1462..a52e58ac3 100644
--- a/lib/system/widestrs.nim
+++ b/lib/system/widestrs.nim
@@ -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