summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgexprs.nim21
-rw-r--r--compiler/ccgstmts.nim2
-rw-r--r--compiler/jstypes.nim2
-rw-r--r--compiler/lambdalifting.nim40
-rw-r--r--compiler/parser.nim9
-rw-r--r--compiler/plugins.nim13
-rw-r--r--compiler/plugins/active.nim2
-rw-r--r--compiler/plugins/itersgen.nim49
-rw-r--r--compiler/semexprs.nim41
-rw-r--r--compiler/sempass2.nim16
-rw-r--r--compiler/semstmts.nim8
-rw-r--r--compiler/semtypes.nim51
-rw-r--r--compiler/semtypinst.nim4
-rw-r--r--compiler/sigmatch.nim11
-rw-r--r--compiler/transf.nim6
-rw-r--r--doc/astspec.txt8
-rw-r--r--doc/grammar.txt15
-rw-r--r--doc/lib.txt3
-rw-r--r--doc/manual/generics.txt2
-rw-r--r--doc/tut1.txt23
-rw-r--r--lib/js/dom.nim306
-rw-r--r--lib/pure/collections/sequtils.nim9
-rw-r--r--lib/pure/fsmonitor.nim45
-rw-r--r--lib/pure/options.nim4
-rw-r--r--lib/pure/os.nim2
-rw-r--r--lib/pure/strutils.nim63
-rw-r--r--lib/pure/times.nim121
-rw-r--r--lib/pure/unicode.nim1
-rw-r--r--lib/system.nim10
-rw-r--r--lib/system/alloc.nim5
-rw-r--r--lib/system/cellsets.nim35
-rw-r--r--lib/system/dyncalls.nim4
-rw-r--r--lib/system/gc.nim2
-rw-r--r--lib/system/gc2.nim1521
-rw-r--r--lib/system/jssys.nim94
-rw-r--r--lib/system/sysstr.nim2
-rw-r--r--tests/concepts/tmanual.nim2
-rw-r--r--tests/iter/titerable.nim3
-rw-r--r--tests/metatype/ttypeclasses.nim8
-rw-r--r--tests/misc/tfsmonitor.nim12
-rw-r--r--tests/stdlib/ttime.nim123
-rw-r--r--tests/testament/categories.nim3
-rw-r--r--tests/types/tisop.nim2
-rw-r--r--tests/types/tisopr.nim23
-rw-r--r--web/news.txt5
-rw-r--r--web/website.ini6
46 files changed, 1208 insertions, 1529 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index c9ff9d4f0..df3b655e3 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -959,6 +959,7 @@ proc genEcho(p: BProc, n: PNode) =
       addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
   linefmt(p, cpsStmts, "printf($1$2);$n",
           makeCString(repeat("%s", n.len) & tnl), args)
+  linefmt(p, cpsStmts, "fflush(stdout);$n")
 
 proc gcUsage(n: PNode) =
   if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)
@@ -1415,11 +1416,11 @@ proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
   case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
-  of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1<<(($2)&7)))!=0)")
-  of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1<<(($2)&15)))!=0)")
-  of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1<<(($2)&31)))!=0)")
-  of 8: binaryExprIn(p, e, a, b, d, "(($1 &(IL64(1)<<(($2)&IL64(63))))!=0)")
-  else: binaryExprIn(p, e, a, b, d, "(($1[$2/8] &(1<<($2%8)))!=0)")
+  of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)")
+  of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)")
+  of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)")
+  of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
+  else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
 
 proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a, b: TLoc
@@ -1500,8 +1501,8 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     else: internalError(e.info, "genSetOp()")
   else:
     case op
-    of mIncl: binaryStmtInExcl(p, e, d, "$1[$2/8] |=(1<<($2%8));$n")
-    of mExcl: binaryStmtInExcl(p, e, d, "$1[$2/8] &= ~(1<<($2%8));$n")
+    of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n")
+    of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3]] &= ~(1U<<($2&7U));$n")
     of mCard: unaryExprChar(p, e, d, "#cardSet($1, " & $size & ')')
     of mLtSet, mLeSet:
       getTemp(p, getSysType(tyInt), i) # our counter
@@ -1733,8 +1734,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mEcho: genEcho(p, e[1].skipConv)
   of mArrToSeq: genArrToSeq(p, e, d)
   of mNLen..mNError, mSlurp..mQuoteAst:
-    echo "from here ", p.prc.name.s, " ", p.prc.info
-    writestacktrace()
     localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
   of mSpawn:
     let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil)
@@ -1788,11 +1787,11 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
           initLocExpr(p, e.sons[i].sons[0], a)
           initLocExpr(p, e.sons[i].sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
-              "$2[$1/8] |=(1<<($1%8));$n", [rdLoc(idx), rdLoc(d),
+              "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d),
               rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)])
         else:
           initLocExpr(p, e.sons[i], a)
-          lineF(p, cpsStmts, "$1[$2/8] |=(1<<($2%8));$n",
+          lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n",
                [rdLoc(d), rdSetElemLoc(a, e.typ)])
     else:
       # small set
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index f4a7c4400..73497bded 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -16,7 +16,7 @@ const
     # above X strings a hash-switch for strings is generated
 
 proc registerGcRoot(p: BProc, v: PSym) =
-  if gSelectedGC in {gcMarkAndSweep, gcGenerational} and
+  if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2} and
       containsGarbageCollectedRef(v.loc.t):
     # we register a specialized marked proc here; this has the advantage
     # that it works out of the box for thread local storage then :-)
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index 367c173ea..611f50eaf 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -116,7 +116,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
          [name, genTypeInfo(p, typ.sons[0])])
 
 proc genTypeInfo(p: PProc, typ: PType): Rope =
-  let t = typ.skipTypes({tyGenericInst})
+  let t = typ.skipTypes({tyGenericInst, tyDistinct})
   result = "NTI$1" % [rope(t.id)]
   if containsOrIncl(p.g.typeInfoGenerated, t.id): return
   case t.kind
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index cccc94756..be1631af0 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -126,6 +126,7 @@ type
     fn, closureParam, state, resultSym: PSym # most are only valid if
                                              # fn.kind == skClosureIterator
     obj: PType
+    isIterator: bool
 
   PEnv = ref TEnv
   TEnv {.final.} = object of RootObj
@@ -197,24 +198,29 @@ proc getHiddenParam(routine: PSym): PSym =
   result = hidden.sym
   assert sfFromGeneric in result.flags
 
-proc getEnvParam(routine: PSym): PSym =
+proc getEnvParam*(routine: PSym): PSym =
   let params = routine.ast.sons[paramsPos]
   let hidden = lastSon(params)
   if hidden.kind == nkSym and hidden.sym.name.s == paramName:
     result = hidden.sym
     assert sfFromGeneric in result.flags
 
-proc initIter(iter: PSym): TIter =
+proc initIter(iter: PSym; ptrType: PType = nil): TIter =
   result.fn = iter
-  if iter.kind == skClosureIterator:
+  result.isIterator = ptrType != nil or iter.kind == skClosureIterator
+  #echo "fuck you ", ptrType != nil
+  if result.isIterator:
     var cp = getEnvParam(iter)
     if cp == nil:
-      result.obj = createEnvObj(iter)
+      result.obj = if ptrType != nil: ptrType.lastSon else: createEnvObj(iter)
 
       cp = newSym(skParam, getIdent(paramName), iter, iter.info)
       incl(cp.flags, sfFromGeneric)
-      cp.typ = newType(tyRef, iter)
-      rawAddSon(cp.typ, result.obj)
+      if ptrType != nil:
+        cp.typ = ptrType
+      else:
+        cp.typ = newType(tyRef, iter)
+        rawAddSon(cp.typ, result.obj)
       addHiddenParam(iter, cp)
     else:
       result.obj = cp.typ.sons[0]
@@ -534,6 +540,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
     let fn = n.sym
     if isInnerProc(fn, o.fn) and not containsOrIncl(o.processed, fn.id):
       let body = fn.getBody
+      if nfLL in body.flags: return
 
       # handle deeply nested captures:
       let ex = closureCreationPoint(body)
@@ -794,7 +801,7 @@ proc finishEnvironments(o: POuterContext) =
 proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
   if nfLL in n.flags:
     result = nil
-  elif it.fn.kind == skClosureIterator:
+  elif it.isIterator:
     # unfortunately control flow is still convoluted and we can end up
     # multiple times here for the very same iterator. We shield against this
     # with some rather primitive check for now:
@@ -843,7 +850,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
       if newBody != nil:
         local.ast.sons[bodyPos] = newBody
 
-    if it.fn.kind == skClosureIterator and interestingIterVar(local) and
+    if it.isIterator and interestingIterVar(local) and
         it.fn == local.owner:
       # every local goes through the closure:
       #if not containsOrIncl(o.capturedVars, local.id):
@@ -931,7 +938,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
     when false:
       if n.sons[1].kind == nkSym:
         var local = n.sons[1].sym
-        if it.fn.kind == skClosureIterator and interestingIterVar(local) and
+        if it.isIterator and interestingIterVar(local) and
             it.fn == local.owner:
           # every local goes through the closure:
           addUniqueField(it.obj, local)
@@ -941,14 +948,25 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
     if x != nil: n.sons[1] = x
     result = transformOuterConv(n)
   of nkYieldStmt:
-    if it.fn.kind == skClosureIterator: result = transformYield(o, n, it)
+    if it.isIterator: result = transformYield(o, n, it)
     else: outerProcSons(o, n, it)
   of nkReturnStmt:
-    if it.fn.kind == skClosureIterator: result = transformReturn(o, n, it)
+    if it.isIterator: result = transformReturn(o, n, it)
     else: outerProcSons(o, n, it)
   else:
     outerProcSons(o, n, it)
 
+proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
+  var o = newOuterContext(fn)
+  let ex = closureCreationPoint(body)
+  let env = newEnv(o, nil, ex, fn)
+  addParamsToEnv(fn, env)
+  searchForInnerProcs(o, body, env)
+  createEnvironments(o)
+  let it = initIter(fn, ptrType)
+  result = transformOuterProcBody(o, body, it)
+  finishEnvironments(o)
+
 proc liftLambdas*(fn: PSym, body: PNode): PNode =
   # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
   # the transformation even when compiling to JS ...
diff --git a/compiler/parser.nim b/compiler/parser.nim
index dbf9706ea..11dd6788a 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -991,7 +991,7 @@ proc isExprStart(p: TParser): bool =
   of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
      tkProc, tkIterator, tkBind, tkAddr,
      tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
-     tkTuple, tkObject, tkType, tkWhen, tkCase:
+     tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
     result = true
   else: result = false
 
@@ -1038,7 +1038,7 @@ proc parseObject(p: var TParser): PNode
 proc parseTypeClass(p: var TParser): PNode
 
 proc primary(p: var TParser, mode: TPrimaryMode): PNode =
-  #| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'tuple'
+  #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
   #|          | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
   #| primary = typeKeyw typeDescK
   #|         /  prefixOperator* identOrLiteral primarySuffix*
@@ -1112,6 +1112,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
     optInd(p, result)
     addSon(result, primary(p, pmNormal))
   of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
+  of tkOut: result = parseTypeDescKAux(p, nkVarTy, mode)
   of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
   of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
   of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
@@ -1763,7 +1764,7 @@ proc parseObject(p: var TParser): PNode =
   addSon(result, parseObjectPart(p))
 
 proc parseTypeClassParam(p: var TParser): PNode =
-  if p.tok.tokType == tkVar:
+  if p.tok.tokType in {tkOut, tkVar}:
     result = newNodeP(nkVarTy, p)
     getTok(p)
     result.addSon(p.parseSymbol)
@@ -1771,7 +1772,7 @@ proc parseTypeClassParam(p: var TParser): PNode =
     result = p.parseSymbol
 
 proc parseTypeClass(p: var TParser): PNode =
-  #| typeClassParam = ('var')? symbol
+  #| typeClassParam = ('var' | 'out')? symbol
   #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
   #|               &IND{>} stmt
   result = newNodeP(nkTypeClassTy, p)
diff --git a/compiler/plugins.nim b/compiler/plugins.nim
index 1c9b7b77b..19a0bc84d 100644
--- a/compiler/plugins.nim
+++ b/compiler/plugins.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## Plugin support for the Nim compiler. Right now there are no plugins and they
+## Plugin support for the Nim compiler. Right now they
 ## need to be build with the compiler, no DLL support.
 
 import ast, semdata, idents
@@ -20,13 +20,16 @@ type
     next: Plugin
 
 proc pluginMatches(p: Plugin; s: PSym): bool =
-  if s.name.id != p.fn.id: return false
-  let module = s.owner
+  if s.name.id != p.fn.id:
+    return false
+  let module = s.skipGenericOwner
   if module == nil or module.kind != skModule or
-      module.name.id != p.module.id: return false
+      module.name.id != p.module.id:
+    return false
   let package = module.owner
   if package == nil or package.kind != skPackage or
-      package.name.id != p.package.id: return false
+      package.name.id != p.package.id:
+    return false
   return true
 
 var head: Plugin
diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim
index e9c11c2ea..7b6411178 100644
--- a/compiler/plugins/active.nim
+++ b/compiler/plugins/active.nim
@@ -10,4 +10,4 @@
 ## Include file that imports all plugins that are active.
 
 import
-  locals.locals
+  locals.locals, itersgen
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
new file mode 100644
index 000000000..91e1d2783
--- /dev/null
+++ b/compiler/plugins/itersgen.nim
@@ -0,0 +1,49 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Plugin to transform an inline iterator into a data structure.
+
+import plugins, ast, astalgo, magicsys, lookups, semdata,
+  lambdalifting, msgs, rodread
+
+proc iterToProcImpl(c: PContext, n: PNode): PNode =
+  result = newNodeI(nkStmtList, n.info)
+  let iter = n[1]
+  if iter.kind != nkSym or iter.sym.kind != skIterator:
+    localError(iter.info, "first argument needs to be an iterator")
+    return
+  if n[2].typ.isNil:
+    localError(n[2].info, "second argument needs to be a type")
+    return
+  if n[3].kind != nkIdent:
+    localError(n[3].info, "third argument needs to be an identifier")
+    return
+
+  let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst})
+  if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject:
+    localError(n[2].info,
+        "type must be a non-generic ref|ptr to object with state field")
+    return
+  let body = liftIterToProc(iter.sym, iter.sym.getBody, t)
+
+  let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info)
+  prc.typ = copyType(iter.sym.typ, prc, false)
+  excl prc.typ.flags, tfCapturesEnv
+  prc.typ.n.add newSymNode(getEnvParam(iter.sym))
+  prc.typ.rawAddSon t
+  let orig = iter.sym.ast
+  prc.ast = newProcNode(nkProcDef, n.info,
+                        name = newSymNode(prc),
+                        params = orig[paramsPos],
+                        pragmas = orig[pragmasPos],
+                        body = body)
+  prc.ast.add iter.sym.ast.sons[resultPos]
+  addInterfaceDecl(c, prc)
+
+registerPlugin("stdlib", "system", "iterToProc", iterToProcImpl)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 58c42d410..95a90463c 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -385,7 +385,8 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
       result = newIntNode(nkIntLit, ord(t.kind == tyProc and
                                         t.callConv == ccClosure and
                                         tfIterator notin t.flags))
-    else: discard
+    else:
+      result = newIntNode(nkIntLit, 0)
   else:
     var t2 = n[2].typ.skipTypes({tyTypeDesc})
     maybeLiftType(t2, c, n.info)
@@ -1434,20 +1435,15 @@ proc semYield(c: PContext, n: PNode): PNode =
     var iterType = c.p.owner.typ
     let restype = iterType.sons[0]
     if restype != nil:
-      let adjustedRes = if restype.kind == tyIter: restype.base
-                        else: restype
-      if adjustedRes.kind != tyExpr:
-        n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
+      if restype.kind != tyExpr:
+        n.sons[0] = fitNode(c, restype, n.sons[0])
       if n.sons[0].typ == nil: internalError(n.info, "semYield")
 
-      if resultTypeIsInferrable(adjustedRes):
+      if resultTypeIsInferrable(restype):
         let inferred = n.sons[0].typ
-        if restype.kind == tyIter:
-          restype.sons[0] = inferred
-        else:
-          iterType.sons[0] = inferred
+        iterType.sons[0] = inferred
 
-      semYieldVarResult(c, n, adjustedRes)
+      semYieldVarResult(c, n, restype)
     else:
       localError(n.info, errCannotReturnExpr)
   elif c.p.owner.typ.sons[0] != nil:
@@ -1780,7 +1776,24 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     result = setMs(n, s)
     result.sons[1] = semExpr(c, n.sons[1])
     result.typ = n[1].typ
-  else: result = semDirectOp(c, n, flags)
+  of mPlugin:
+    # semDirectOp with conditional 'afterCallActions':
+    let nOrig = n.copyTree
+    #semLazyOpAux(c, n)
+    result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
+    if result == nil:
+      result = errorNode(c, n)
+    else:
+      let callee = result.sons[0].sym
+      if callee.magic == mNone:
+        semFinishOperands(c, result)
+      activate(c, result)
+      fixAbstractType(c, result)
+      analyseIfAddressTakenInCall(c, result)
+      if callee.magic != mNone:
+        result = magicsAfterOverloadResolution(c, result, flags)
+  else:
+    result = semDirectOp(c, n, flags)
 
 proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   # If semCheck is set to false, ``when`` will return the verbatim AST of
@@ -2167,7 +2180,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     message(n.info, warnDeprecated, "bind")
     result = semExpr(c, n.sons[0], flags)
   of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
-    var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
+    var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
     result.typ = makeTypeDesc(c, typ)
     #result = symNodeFromType(c, typ, n.info)
   of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
@@ -2240,7 +2253,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       var tupexp = semTuplePositionsConstr(c, n, flags)
       if isTupleType(tupexp):
         # reinterpret as type
-        var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
+        var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
         result.typ = makeTypeDesc(c, typ)
       else:
         result = tupexp
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 29cce9247..c3a9e01a0 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -504,7 +504,8 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
     if n.kind == nkAddr:
       # addr(x[]) can't be proven, but addr(x) can:
       if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
-    elif (n.kind == nkSym and n.sym.kind in routineKinds) or n.kind in procDefs:
+    elif (n.kind == nkSym and n.sym.kind in routineKinds) or
+         n.kind in procDefs+{nkObjConstr}:
       # 'p' is not nil obviously:
       return
     case impliesNotNil(tracked.guards, n)
@@ -704,7 +705,15 @@ proc track(tracked: PEffects, n: PNode) =
     for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
     if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
       # may not look like an assignment, but it is:
-      initVarViaNew(tracked, n.sons[1])
+      let arg = n.sons[1]
+      initVarViaNew(tracked, arg)
+      if {tfNeedsInit} * arg.typ.lastSon.flags != {}:
+        if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
+            n[2].intVal == 0:
+          # var s: seq[notnil];  newSeq(s, 0)  is a special case!
+          discard
+        else:
+          message(arg.info, warnProveInit, $arg)
     for i in 0 .. <safeLen(n):
       track(tracked, n.sons[i])
   of nkDotExpr:
@@ -875,7 +884,8 @@ proc trackProc*(s: PSym, body: PNode) =
   var t: TEffects
   initEffects(effects, s, t)
   track(t, body)
-  if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
+  if not isEmptyType(s.typ.sons[0]) and
+      {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
       s.kind in {skProc, skConverter, skMethod}:
     var res = s.ast.sons[resultPos].sym # get result symbol
     if res.id notin t.init:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index adb1c81c1..e80f1cfda 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -330,7 +330,8 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
   styleCheckDef(result)
 
 proc checkNilable(v: PSym) =
-  if sfGlobal in v.flags and {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
+  if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
+      {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
     if v.ast.isNil:
       message(v.info, warnProveInit, v.name.s)
     elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
@@ -539,7 +540,7 @@ proc symForVar(c: PContext, n: PNode): PSym =
 proc semForVars(c: PContext, n: PNode): PNode =
   result = n
   var length = sonsLen(n)
-  let iterBase = n.sons[length-2].typ.skipTypes({tyIter})
+  let iterBase = n.sons[length-2].typ
   var iter = skipTypes(iterBase, {tyGenericInst})
   # length == 3 means that there is one for loop variable
   # and thus no tuple unpacking:
@@ -593,8 +594,7 @@ proc semFor(c: PContext, n: PNode): PNode =
       result.kind = nkParForStmt
     else:
       result = semForFields(c, n, call.sons[0].sym.magic)
-  elif (isCallExpr and call.sons[0].typ.callConv == ccClosure) or
-      call.typ.kind == tyIter:
+  elif isCallExpr and call.sons[0].typ.callConv == ccClosure:
     # first class iterator:
     result = semForVars(c, n)
   elif not isCallExpr or call.sons[0].kind != nkSym or
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 65cb9421b..65edb756f 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -135,13 +135,19 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
     checkMinSonsLen(n, 1)
     var base = semTypeNode(c, n.lastSon, nil)
     result = newOrPrevType(kind, prev, c)
+    var isNilable = false
     # check every except the last is an object:
     for i in isCall .. n.len-2:
-      let region = semTypeNode(c, n[i], nil)
-      if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}:
-        message n[i].info, errGenerated, "region needs to be an object type"
-      addSonSkipIntLit(result, region)
+      let ni = n[i]
+      if ni.kind == nkNilLit:
+        isNilable = true
+      else:
+        let region = semTypeNode(c, ni, nil)
+        if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}:
+          message n[i].info, errGenerated, "region needs to be an object type"
+        addSonSkipIntLit(result, region)
     addSonSkipIntLit(result, base)
+    #if not isNilable: result.flags.incl tfNotNil
 
 proc semVarType(c: PContext, n: PNode, prev: PType): PType =
   if sonsLen(n) == 1:
@@ -826,15 +832,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
     result = addImplicitGeneric(result)
 
-  of tyIter:
-    if paramType.callConv == ccInline:
-      if procKind notin {skTemplate, skMacro, skIterator}:
-        localError(info, errInlineIteratorsAsProcParams)
-      if paramType.len == 1:
-        let lifted = liftingWalk(paramType.base)
-        if lifted != nil: paramType.sons[0] = lifted
-      result = addImplicitGeneric(paramType)
-
   of tyGenericInst:
     if paramType.lastSon.kind == tyUserTypeClass:
       var cp = copyType(paramType, getCurrOwner(), false)
@@ -865,11 +862,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
   of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
     result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
 
-  of tyExpr:
-    if procKind notin {skMacro, skTemplate}:
-      result = addImplicitGeneric(newTypeS(tyAnything, c))
-      #result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
-
   of tyGenericParam:
     markUsed(info, paramType.sym)
     styleCheckUse(info, paramType.sym)
@@ -996,7 +988,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           # see tchainediterators
           # in cases like iterator foo(it: iterator): type(it)
           # we don't need to change the return type to iter[T]
-          if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
+          result.flags.incl tfIterator
+          # XXX Would be nice if we could get rid of this
       result.sons[0] = r
       result.n.typ = r
 
@@ -1151,7 +1144,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     # for ``type(countup(1,3))``, see ``tests/ttoseq``.
     checkSonsLen(n, 1)
     let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
-    result = typExpr.typ.skipTypes({tyIter})
+    result = typExpr.typ
   of nkPar:
     if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
     else:
@@ -1169,6 +1162,14 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       result = semTypeNode(c, b, prev)
     elif ident != nil and ident.id == ord(wDotDot):
       result = semRangeAux(c, n, prev)
+    elif n[0].kind == nkNilLit and n.len == 2:
+      result = semTypeNode(c, n.sons[1], prev)
+      if result.skipTypes({tyGenericInst}).kind in NilableTypes+GenericTypes:
+        if tfNotNil in result.flags:
+          result = freshType(result, prev)
+          result.flags.excl(tfNotNil)
+      else:
+        localError(n.info, errGenerated, "invalid type")
     elif n[0].kind notin nkIdentKinds:
       result = semTypeExpr(c, n)
     else:
@@ -1209,7 +1210,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       elif op.id == ord(wType):
         checkSonsLen(n, 2)
         let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
-        result = typExpr.typ.skipTypes({tyIter})
+        result = typExpr.typ
       else:
         result = semTypeExpr(c, n)
   of nkWhenStmt:
@@ -1290,14 +1291,16 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     result.flags.incl tfHasStatic
   of nkIteratorTy:
     if n.sonsLen == 0:
-      result = newConstraint(c, tyIter)
+      result = newTypeS(tyBuiltInTypeClass, c)
+      let child = newTypeS(tyProc, c)
+      child.flags.incl tfIterator
+      result.addSonSkipIntLit(child)
     else:
       result = semProcTypeWithScope(c, n, prev, skClosureIterator)
+      result.flags.incl(tfIterator)
       if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
-        result.kind = tyIter
         result.callConv = ccInline
       else:
-        result.flags.incl(tfIterator)
         result.callConv = ccClosure
   of nkProcTy:
     if n.sonsLen == 0:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 7957ac50a..20b60a88d 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -77,7 +77,7 @@ proc cacheTypeInst*(inst: PType) =
   #      update the refcount
   let gt = inst.sons[0]
   let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
-  if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
+  if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
     return
   gt.sym.typeInstCache.safeAdd(inst)
 
@@ -390,7 +390,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
   result = t
   if t == nil: return
 
-  if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
+  if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
     let lookup = PType(idTableGet(cl.typeMap, t))
     if lookup != nil: return lookup
 
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 9fda8c860..aee42e021 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -1256,10 +1256,6 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
       result.typ = getInstantiatedType(c, arg, m, base(f))
     m.baseTypeMatch = true
 
-proc isInlineIterator*(t: PType): bool =
-  result = t.kind == tyIter or
-          (t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
-
 proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   case r
   of isConvertible, isIntConv: inc(m.convMatches, convMatch)
@@ -1323,13 +1319,6 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     else:
       return argSemantized # argOrig
 
-  if r != isNone and f.isInlineIterator:
-    var inlined = newTypeS(tyStatic, c)
-    inlined.sons = @[argType]
-    inlined.n = argSemantized
-    put(m.bindings, f, inlined)
-    return argSemantized
-
   # If r == isBothMetaConvertible then we rerun typeRel.
   # bothMetaCounter is for safety to avoid any infinite loop,
   #  I don't have any example when it is needed.
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 3d78a6b92..3a5ff982e 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -478,9 +478,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
     result[1] = newNode(nkEmpty).PTransNode
     return result
   c.breakSyms.add(labl)
-  if call.typ.kind != tyIter and
-    (call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
-      call.sons[0].sym.kind != skIterator):
+  if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
+      call.sons[0].sym.kind != skIterator:
     n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
     result[1] = lambdalifting.liftForLoop(n).PTransNode
     discard c.breakSyms.pop
@@ -512,7 +511,6 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   for i in countup(1, sonsLen(call) - 1):
     var arg = transform(c, call.sons[i]).PNode
     var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
-    if arg.typ.kind == tyIter: continue
     case putArgInto(arg, formal.typ)
     of paDirectMapping:
       idNodeTablePut(newC.mapping, formal, arg)
diff --git a/doc/astspec.txt b/doc/astspec.txt
index c84fad8e8..f235e2984 100644
--- a/doc/astspec.txt
+++ b/doc/astspec.txt
@@ -924,9 +924,11 @@ AST:
 
 .. code-block:: nim
   nnkLetSection(
-    nnkIdentDefs(!"v"),
-    nnkEmpty(), # for the type
-    nnkIntLit(3)
+    nnkIdentDefs(
+      nnkIdent(!"a"),
+      nnkEmpty(), # or nnkIdent(...) for the type
+      nnkIntLit(3),
+    )
   )
 
 Const section
diff --git a/doc/grammar.txt b/doc/grammar.txt
index 72dc6c974..d967bf938 100644
--- a/doc/grammar.txt
+++ b/doc/grammar.txt
@@ -35,10 +35,13 @@ castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
 parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
         | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
         | 'when' | 'var' | 'mixin'
-par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';'
-                 | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
-                            | (':' expr)? (',' (exprColonEqExpr comma?)*)?  )?
-        optPar ')'
+par = '(' optInd
+          ( &parKeyw complexOrSimpleStmt ^+ ';'
+          | ';' complexOrSimpleStmt ^+ ';'
+          | pragmaStmt
+          | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
+                       | (':' expr (',' exprColonEqExpr     ^+ ',' )? ) ) )
+          optPar ')'
 literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
           | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
           | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
@@ -86,7 +89,7 @@ expr = (ifExpr
       | caseExpr
       | tryExpr)
       / simpleExpr
-typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'tuple'
+typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
          | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
 primary = typeKeyw typeDescK
         /  prefixOperator* identOrLiteral primarySuffix*
@@ -165,7 +168,7 @@ objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
 objectPart = IND{>} objectPart^+IND{=} DED
            / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
 object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
-typeClassParam = ('var')? symbol
+typeClassParam = ('var' | 'out')? symbol
 typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
               &IND{>} stmt
 typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
diff --git a/doc/lib.txt b/doc/lib.txt
index 3dc58eebf..4fa49095c 100644
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -374,6 +374,9 @@ Miscellaneous
 * `logging <logging.html>`_
   This module implements a simple logger.
 
+* `options <options.html>`_
+  Types which encapsulate an optional value.
+
 * `future <future.html>`_
   This module implements new experimental features. Currently the syntax
   sugar for anonymous procedures.
diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt
index a73e22988..07d98b289 100644
--- a/doc/manual/generics.txt
+++ b/doc/manual/generics.txt
@@ -213,7 +213,7 @@ Concepts are written in the following form:
 
     Container[T] = concept c
       c.len is Ordinal
-      items(c) is iterator
+      items(c) is T
       for value in c:
         type(value) is T
 
diff --git a/doc/tut1.txt b/doc/tut1.txt
index 7dce8a218..747c1a3ff 100644
--- a/doc/tut1.txt
+++ b/doc/tut1.txt
@@ -758,19 +758,18 @@ However, this cannot be done for mutually recursive procedures:
   # forward declaration:
   proc even(n: int): bool
 
-proc even(n: int): bool
-
-proc odd(n: int): bool =
-  assert(n >= 0) # makes sure we don't run into negative recursion
-  if n == 0: false
-  else:
-    n == 1 or even(n-1)
+.. code-block:: nim
+  proc odd(n: int): bool =
+    assert(n >= 0) # makes sure we don't run into negative recursion
+    if n == 0: false
+    else:
+      n == 1 or even(n-1)
 
-proc even(n: int): bool =
-  assert(n >= 0) # makes sure we don't run into negative recursion
-  if n == 1: false
-  else:
-    n == 0 or odd(n-1)
+  proc even(n: int): bool =
+    assert(n >= 0) # makes sure we don't run into negative recursion
+    if n == 1: false
+    else:
+      n == 0 or odd(n-1)
 
 Here ``odd`` depends on ``even`` and vice versa. Thus ``even`` needs to be
 introduced to the compiler before it is completely defined. The syntax for
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index b063fa838..11df959d7 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -14,36 +14,35 @@ when not defined(js) and not defined(Nimdoc):
   {.error: "This module only works on the JavaScript platform".}
 
 type
-  TEventHandlers* {.importc.} = object of RootObj
-    onabort*: proc (event: ref TEvent) {.nimcall.}
-    onblur*: proc (event: ref TEvent) {.nimcall.}
-    onchange*: proc (event: ref TEvent) {.nimcall.}
-    onclick*: proc (event: ref TEvent) {.nimcall.}
-    ondblclick*: proc (event: ref TEvent) {.nimcall.}
-    onerror*: proc (event: ref TEvent) {.nimcall.}
-    onfocus*: proc (event: ref TEvent) {.nimcall.}
-    onkeydown*: proc (event: ref TEvent) {.nimcall.}
-    onkeypress*: proc (event: ref TEvent) {.nimcall.}
-    onkeyup*: proc (event: ref TEvent) {.nimcall.}
-    onload*: proc (event: ref TEvent) {.nimcall.}
-    onmousedown*: proc (event: ref TEvent) {.nimcall.}
-    onmousemove*: proc (event: ref TEvent) {.nimcall.}
-    onmouseout*: proc (event: ref TEvent) {.nimcall.}
-    onmouseover*: proc (event: ref TEvent) {.nimcall.}
-    onmouseup*: proc (event: ref TEvent) {.nimcall.}
-    onreset*: proc (event: ref TEvent) {.nimcall.}
-    onselect*: proc (event: ref TEvent) {.nimcall.}
-    onsubmit*: proc (event: ref TEvent) {.nimcall.}
-    onunload*: proc (event: ref TEvent) {.nimcall.}
-
-    addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent), useCapture: bool = false) {.nimcall.}
+  EventTarget* = ref EventTargetObj
+  EventTargetObj {.importc.} = object of RootObj
+    onabort*: proc (event: Event) {.nimcall.}
+    onblur*: proc (event: Event) {.nimcall.}
+    onchange*: proc (event: Event) {.nimcall.}
+    onclick*: proc (event: Event) {.nimcall.}
+    ondblclick*: proc (event: Event) {.nimcall.}
+    onerror*: proc (event: Event) {.nimcall.}
+    onfocus*: proc (event: Event) {.nimcall.}
+    onkeydown*: proc (event: Event) {.nimcall.}
+    onkeypress*: proc (event: Event) {.nimcall.}
+    onkeyup*: proc (event: Event) {.nimcall.}
+    onload*: proc (event: Event) {.nimcall.}
+    onmousedown*: proc (event: Event) {.nimcall.}
+    onmousemove*: proc (event: Event) {.nimcall.}
+    onmouseout*: proc (event: Event) {.nimcall.}
+    onmouseover*: proc (event: Event) {.nimcall.}
+    onmouseup*: proc (event: Event) {.nimcall.}
+    onreset*: proc (event: Event) {.nimcall.}
+    onselect*: proc (event: Event) {.nimcall.}
+    onsubmit*: proc (event: Event) {.nimcall.}
+    onunload*: proc (event: Event) {.nimcall.}
 
   Window* = ref WindowObj
-  WindowObj {.importc.} = object of TEventHandlers
+  WindowObj {.importc.} = object of EventTargetObj
     document*: Document
-    event*: ref TEvent
-    history*: ref THistory
-    location*: ref TLocation
+    event*: Event
+    history*: History
+    location*: Location
     closed*: bool
     defaultStatus*: cstring
     innerHeight*, innerWidth*: int
@@ -57,50 +56,15 @@ type
     statusbar*: ref TStatusBar
     status*: cstring
     toolbar*: ref TToolBar
-
-    alert*: proc (msg: cstring) {.nimcall.}
-    back*: proc () {.nimcall.}
-    blur*: proc () {.nimcall.}
-    captureEvents*: proc (eventMask: int) {.nimcall.}
-    clearInterval*: proc (interval: ref TInterval) {.nimcall.}
-    clearTimeout*: proc (timeout: ref TTimeOut) {.nimcall.}
-    close*: proc () {.nimcall.}
-    confirm*: proc (msg: cstring): bool {.nimcall.}
-    disableExternalCapture*: proc () {.nimcall.}
-    enableExternalCapture*: proc () {.nimcall.}
-    find*: proc (text: cstring, caseSensitive = false,
-                 backwards = false) {.nimcall.}
-    focus*: proc () {.nimcall.}
-    forward*: proc () {.nimcall.}
-    handleEvent*: proc (e: ref TEvent) {.nimcall.}
-    home*: proc () {.nimcall.}
-    moveBy*: proc (x, y: int) {.nimcall.}
-    moveTo*: proc (x, y: int) {.nimcall.}
-    open*: proc (uri, windowname: cstring,
-                 properties: cstring = nil): Window {.nimcall.}
-    print*: proc () {.nimcall.}
-    prompt*: proc (text, default: cstring): cstring {.nimcall.}
-    releaseEvents*: proc (eventMask: int) {.nimcall.}
-    resizeBy*: proc (x, y: int) {.nimcall.}
-    resizeTo*: proc (x, y: int) {.nimcall.}
-    routeEvent*: proc (event: ref TEvent) {.nimcall.}
-    scrollBy*: proc (x, y: int) {.nimcall.}
-    scrollTo*: proc (x, y: int) {.nimcall.}
-    setInterval*: proc (code: cstring, pause: int): ref TInterval {.nimcall.}
-    setTimeout*: proc (code: cstring, pause: int): ref TTimeOut {.nimcall.}
-    stop*: proc () {.nimcall.}
     frames*: seq[TFrame]
 
   Frame* = ref FrameObj
   FrameObj {.importc.} = object of WindowObj
 
-  ClassList* {.importc.} = object of RootObj
-    add*: proc (class: cstring) {.nimcall.}
-    remove*: proc (class: cstring) {.nimcall.}
-    contains*: proc (class: cstring):bool {.nimcall.}
-    toggle*: proc (class: cstring) {.nimcall.}
+  ClassList* = ref ClassListObj
+  ClassListObj {.importc.} = object of RootObj
 
-  TNodeType* = enum
+  NodeType* = enum
     ElementNode = 1,
     AttributeNode,
     TextNode,
@@ -115,7 +79,7 @@ type
     NotationNode
 
   Node* = ref NodeObj
-  NodeObj {.importc.} = object of TEventHandlers
+  NodeObj {.importc.} = object of EventTargetObj
     attributes*: seq[Node]
     childNodes*: seq[Node]
     children*: seq[Node]
@@ -124,29 +88,12 @@ type
     lastChild*: Node
     nextSibling*: Node
     nodeName*: cstring
-    nodeType*: TNodeType
+    nodeType*: NodeType
     nodeValue*: cstring
     parentNode*: Node
     previousSibling*: Node
-    appendChild*: proc (child: Node) {.nimcall.}
-    appendData*: proc (data: cstring) {.nimcall.}
-    cloneNode*: proc (copyContent: bool): Node {.nimcall.}
-    deleteData*: proc (start, len: int) {.nimcall.}
-    getAttribute*: proc (attr: cstring): cstring {.nimcall.}
-    getAttributeNode*: proc (attr: cstring): Node {.nimcall.}
-    hasChildNodes*: proc (): bool {.nimcall.}
     innerHTML*: cstring
-    insertBefore*: proc (newNode, before: Node) {.nimcall.}
-    insertData*: proc (position: int, data: cstring) {.nimcall.}
-    removeAttribute*: proc (attr: cstring) {.nimcall.}
-    removeAttributeNode*: proc (attr: Node) {.nimcall.}
-    removeChild*: proc (child: Node) {.nimcall.}
-    replaceChild*: proc (newNode, oldNode: Node) {.nimcall.}
-    replaceData*: proc (start, len: int, text: cstring) {.nimcall.}
-    scrollIntoView*: proc () {.nimcall.}
-    setAttribute*: proc (name, value: cstring) {.nimcall.}
-    setAttributeNode*: proc (attr: Node) {.nimcall.}
-    style*: ref TStyle
+    style*: Style
 
   Document* = ref DocumentObj
   DocumentObj {.importc.} = object of NodeObj
@@ -164,31 +111,16 @@ type
     title*: cstring
     URL*: cstring
     vlinkColor*: cstring
-    captureEvents*: proc (eventMask: int) {.nimcall.}
-    createAttribute*: proc (identifier: cstring): Node {.nimcall.}
-    createElement*: proc (identifier: cstring): Element {.nimcall.}
-    createTextNode*: proc (identifier: cstring): Node {.nimcall.}
-    getElementById*: proc (id: cstring): Element {.nimcall.}
-    getElementsByName*: proc (name: cstring): seq[Element] {.nimcall.}
-    getElementsByTagName*: proc (name: cstring): seq[Element] {.nimcall.}
-    getElementsByClassName*: proc (name: cstring): seq[Element] {.nimcall.}
-    getSelection*: proc (): cstring {.nimcall.}
-    handleEvent*: proc (event: ref TEvent) {.nimcall.}
-    open*: proc () {.nimcall.}
-    releaseEvents*: proc (eventMask: int) {.nimcall.}
-    routeEvent*: proc (event: ref TEvent) {.nimcall.}
-    write*: proc (text: cstring) {.nimcall.}
-    writeln*: proc (text: cstring) {.nimcall.}
     anchors*: seq[AnchorElement]
     forms*: seq[FormElement]
     images*: seq[ImageElement]
-    applets*: seq[ref TApplet]
+    applets*: seq[Element]
     embeds*: seq[EmbedElement]
     links*: seq[LinkElement]
 
   Element* = ref ElementObj
   ElementObj {.importc.} = object of NodeObj
-    classList*: ref Classlist
+    classList*: Classlist
     checked*: bool
     defaultChecked*: bool
     defaultValue*: cstring
@@ -196,14 +128,7 @@ type
     form*: FormElement
     name*: cstring
     readOnly*: bool
-    blur*: proc () {.nimcall.}
-    click*: proc () {.nimcall.}
-    focus*: proc () {.nimcall.}
-    handleEvent*: proc (event: ref TEvent) {.nimcall.}
-    select*: proc () {.nimcall.}
     options*: seq[OptionElement]
-    getElementsByTagName*: proc (name: cstring): seq[Element] {.nimcall.}
-    getElementsByClassName*: proc (name: cstring): seq[Element] {.nimcall.}
 
   LinkElement* = ref LinkObj
   LinkObj {.importc.} = object of ElementObj
@@ -220,16 +145,12 @@ type
     width*: int
     `type`*: cstring
     vspace*: int
-    play*: proc () {.nimcall.}
-    stop*: proc () {.nimcall.}
 
   AnchorElement* = ref AnchorObj
   AnchorObj {.importc.} = object of ElementObj
     text*: cstring
     x*, y*: int
 
-  TApplet* {.importc.} = object of RootObj
-
   OptionElement* = ref OptionObj
   OptionObj {.importc.} = object of ElementObj
     defaultSelected*: bool
@@ -244,8 +165,6 @@ type
     encoding*: cstring
     `method`*: cstring
     target*: cstring
-    reset*: proc () {.nimcall.}
-    submit*: proc () {.nimcall.}
     elements*: seq[Element]
 
   ImageElement* = ref ImageObj
@@ -259,8 +178,8 @@ type
     vspace*: int
     width*: int
 
-
-  TStyle* {.importc.} = object of RootObj
+  Style = ref StyleObj
+  StyleObj {.importc.} = object of RootObj
     background*: cstring
     backgroundAttachment*: cstring
     backgroundColor*: cstring
@@ -350,11 +269,9 @@ type
     width*: cstring
     wordSpacing*: cstring
     zIndex*: int
-    getAttribute*: proc (attr: cstring, caseSensitive=false): cstring {.nimcall.}
-    removeAttribute*: proc (attr: cstring, caseSensitive=false) {.nimcall.}
-    setAttribute*: proc (attr, value: cstring, caseSensitive=false) {.nimcall.}
 
-  TEvent* {.importc.} = object of RootObj
+  Event* = ref EventObj
+  EventObj {.importc.} = object of RootObj
     target*: Node
     altKey*, ctrlKey*, shiftKey*: bool
     button*: int
@@ -393,7 +310,8 @@ type
     SUBMIT*: int
     UNLOAD*: int
 
-  TLocation* {.importc.} = object of RootObj
+  Location* = ref LocationObj
+  LocationObj {.importc.} = object of RootObj
     hash*: cstring
     host*: cstring
     hostname*: cstring
@@ -402,16 +320,13 @@ type
     port*: cstring
     protocol*: cstring
     search*: cstring
-    reload*: proc () {.nimcall.}
-    replace*: proc (s: cstring) {.nimcall.}
 
-  THistory* {.importc.} = object of RootObj
+  History* = ref HistoryObj
+  HistoryObj {.importc.} = object of RootObj
     length*: int
-    back*: proc () {.nimcall.}
-    forward*: proc () {.nimcall.}
-    go*: proc (pagesToJump: int) {.nimcall.}
 
-  TNavigator* {.importc.} = object of RootObj
+  Navigator* = ref NavigatorObj
+  NavigatorObj {.importc.} = object of RootObj
     appCodeName*: cstring
     appName*: cstring
     appVersion*: cstring
@@ -419,7 +334,6 @@ type
     language*: cstring
     platform*: cstring
     userAgent*: cstring
-    javaEnabled*: proc (): bool {.nimcall.}
     mimeTypes*: seq[ref TMimeType]
 
   TPlugin* {.importc.} = object of RootObj
@@ -441,7 +355,8 @@ type
   TToolBar* = TLocationBar
   TStatusBar* = TLocationBar
 
-  TScreen* {.importc.} = object of RootObj
+  Screen = ref ScreenObj
+  ScreenObj {.importc.} = object of RootObj
     availHeight*: int
     availWidth*: int
     colorDepth*: int
@@ -452,11 +367,127 @@ type
   TTimeOut* {.importc.} = object of RootObj
   TInterval* {.importc.} = object of RootObj
 
+{.push importcpp.}
+
+# EventTarget "methods"
+proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
+
+# Window "methods"
+proc alert*(w: Window, msg: cstring)
+proc back*(w: Window)
+proc blur*(w: Window)
+proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
+proc clearInterval*(w: Window, interval: ref TInterval)
+proc clearTimeout*(w: Window, timeout: ref TTimeOut)
+proc close*(w: Window)
+proc confirm*(w: Window, msg: cstring): bool
+proc disableExternalCapture*(w: Window)
+proc enableExternalCapture*(w: Window)
+proc find*(w: Window, text: cstring, caseSensitive = false,
+           backwards = false)
+proc focus*(w: Window)
+proc forward*(w: Window)
+proc handleEvent*(w: Window, e: Event)
+proc home*(w: Window)
+proc moveBy*(w: Window, x, y: int)
+proc moveTo*(w: Window, x, y: int)
+proc open*(w: Window, uri, windowname: cstring,
+           properties: cstring = nil): Window
+proc print*(w: Window)
+proc prompt*(w: Window, text, default: cstring): cstring
+proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
+proc resizeBy*(w: Window, x, y: int)
+proc resizeTo*(w: Window, x, y: int)
+proc routeEvent*(w: Window, event: Event)
+proc scrollBy*(w: Window, x, y: int)
+proc scrollTo*(w: Window, x, y: int)
+proc setInterval*(w: Window, code: cstring, pause: int): ref TInterval
+proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut
+proc stop*(w: Window)
+
+# Node "methods"
+proc appendChild*(n, child: Node)
+proc appendData*(n: Node, data: cstring)
+proc cloneNode*(n: Node, copyContent: bool): Node
+proc deleteData*(n: Node, start, len: int)
+proc getAttribute*(n: Node, attr: cstring): cstring
+proc getAttributeNode*(n: Node, attr: cstring): Node
+proc hasChildNodes*(n: Node): bool
+proc insertBefore*(n, newNode, before: Node)
+proc insertData*(n: Node, position: int, data: cstring)
+proc removeAttribute*(n: Node, attr: cstring)
+proc removeAttributeNode*(n, attr: Node)
+proc removeChild*(n, child: Node)
+proc replaceChild*(n, newNode, oldNode: Node)
+proc replaceData*(n: Node, start, len: int, text: cstring)
+proc scrollIntoView*(n: Node)
+proc setAttribute*(n: Node, name, value: cstring)
+proc setAttributeNode*(n: Node, attr: Node)
+
+# Document "methods"
+proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
+proc createAttribute*(d: Document, identifier: cstring): Node
+proc createElement*(d: Document, identifier: cstring): Element
+proc createTextNode*(d: Document, identifier: cstring): Node
+proc getElementById*(d: Document, id: cstring): Element
+proc getElementsByName*(d: Document, name: cstring): seq[Element]
+proc getElementsByTagName*(d: Document, name: cstring): seq[Element]
+proc getElementsByClassName*(d: Document, name: cstring): seq[Element]
+proc getSelection*(d: Document): cstring
+proc handleEvent*(d: Document, event: Event)
+proc open*(d: Document)
+proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
+proc routeEvent*(d: Document, event: Event)
+proc write*(d: Document, text: cstring)
+proc writeln*(d: Document, text: cstring)
+
+# Element "methods"
+proc blur*(e: Element)
+proc click*(e: Element)
+proc focus*(e: Element)
+proc handleEvent*(e: Element, event: Event)
+proc select*(e: Element)
+proc getElementsByTagName*(e: Element, name: cstring): seq[Element]
+proc getElementsByClassName*(e: Element, name: cstring): seq[Element]
+
+# FormElement "methods"
+proc reset*(f: FormElement)
+proc submit*(f: FormElement)
+
+# EmbedElement "methods"
+proc play*(e: EmbedElement)
+proc stop*(e: EmbedElement)
+
+# Location "methods"
+proc reload*(loc: Location)
+proc replace*(loc: Location, s: cstring)
+
+# History "methods"
+proc back*(h: History)
+proc forward*(h: History)
+proc go*(h: History, pagesToJump: int)
+
+# Navigator "methods"
+proc javaEnabled*(h: Navigator): bool
+
+# ClassList "methods"
+proc add*(c: ClassList, class: cstring)
+proc remove*(c: ClassList, class: cstring)
+proc contains*(c: ClassList, class: cstring):bool
+proc toggle*(c: ClassList, class: cstring)
+
+# Style "methods"
+proc getAttribute*(s: Style, attr: cstring, caseSensitive=false): cstring
+proc removeAttribute*(s: Style, attr: cstring, caseSensitive=false)
+proc setAttribute*(s: Style, attr, value: cstring, caseSensitive=false)
+
+{.pop.}
+
 var
   window* {.importc, nodecl.}: Window
   document* {.importc, nodecl.}: Document
-  navigator* {.importc, nodecl.}: ref TNavigator
-  screen* {.importc, nodecl.}: ref TScreen
+  navigator* {.importc, nodecl.}: Navigator
+  screen* {.importc, nodecl.}: Screen
 
 proc decodeURI*(uri: cstring): cstring {.importc, nodecl.}
 proc encodeURI*(uri: cstring): cstring {.importc, nodecl.}
@@ -474,6 +505,7 @@ proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
 
 
 type
+  TEventHandlers* {.deprecated.} = EventTargetObj
   TWindow* {.deprecated.} = WindowObj
   TFrame* {.deprecated.} = FrameObj
   TNode* {.deprecated.} = NodeObj
@@ -485,3 +517,11 @@ type
   TOption* {.deprecated.} = OptionObj
   TForm* {.deprecated.} = FormObj
   TImage* {.deprecated.} = ImageObj
+  TNodeType* {.deprecated.} = NodeType
+  TEvent* {.deprecated.} = EventObj
+  TLocation* {.deprecated.} = LocationObj
+  THistory* {.deprecated.} = HistoryObj
+  TNavigator* {.deprecated.} = NavigatorObj
+  TStyle* {.deprecated.} = StyleObj
+  TScreen* {.deprecated.} = ScreenObj
+  TApplet* {.importc, deprecated.} = object of RootObj
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 71babe93b..b72face91 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -10,12 +10,9 @@
 ## :Author: Alexander Mitchell-Robinson (Amrykid)
 ##
 ## This module implements operations for the built-in `seq`:idx: type which
-## were inspired by functional programming languages. If you are looking for
-## the typical `map` function which applies a function to every element in a
-## sequence, it already exists in the `system <system.html>`_ module in both
-## mutable and immutable styles.
+## were inspired by functional programming languages.
 ##
-## Also, for functional style programming you may want to pass `anonymous procs
+## For functional style programming you may want to pass `anonymous procs
 ## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
 ## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
 ## which is more convenient in certain situations.
@@ -471,7 +468,7 @@ template toSeq*(iter: expr): expr {.immediate.} =
   ##       if x mod 2 == 1:
   ##         result = true)
   ##   assert odd_numbers == @[1, 3, 5, 7, 9]
-  
+
   when compiles(iter.len):
     var i = 0
     var result = newSeq[type(iter)](iter.len)
diff --git a/lib/pure/fsmonitor.nim b/lib/pure/fsmonitor.nim
index 787acb5d4..115c4739e 100644
--- a/lib/pure/fsmonitor.nim
+++ b/lib/pure/fsmonitor.nim
@@ -34,8 +34,8 @@ type
   MonitorEventType* = enum ## Monitor event type
     MonitorAccess,       ## File was accessed.
     MonitorAttrib,       ## Metadata changed.
-    MonitorCloseWrite,   ## Writtable file was closed.
-    MonitorCloseNoWrite, ## Unwrittable file closed.
+    MonitorCloseWrite,   ## Writable file was closed.
+    MonitorCloseNoWrite, ## Non-writable file closed.
     MonitorCreate,       ## Subfile was created.
     MonitorDelete,       ## Subfile was deleted.
     MonitorDeleteSelf,   ## Watched file/directory was itself deleted.
@@ -78,21 +78,21 @@ proc add*(monitor: FSMonitor, target: string,
   ## watched paths of ``monitor``.
   ## You can specify the events to report using the ``filters`` parameter.
 
-  var INFilter = -1
+  var INFilter = 0
   for f in filters:
     case f
-    of MonitorAccess: INFilter = INFilter and IN_ACCESS
-    of MonitorAttrib: INFilter = INFilter and IN_ATTRIB
-    of MonitorCloseWrite: INFilter = INFilter and IN_CLOSE_WRITE
-    of MonitorCloseNoWrite: INFilter = INFilter and IN_CLOSE_NO_WRITE
-    of MonitorCreate: INFilter = INFilter and IN_CREATE
-    of MonitorDelete: INFilter = INFilter and IN_DELETE
-    of MonitorDeleteSelf: INFilter = INFilter and IN_DELETE_SELF
-    of MonitorModify: INFilter = INFilter and IN_MODIFY
-    of MonitorMoveSelf: INFilter = INFilter and IN_MOVE_SELF
-    of MonitorMoved: INFilter = INFilter and IN_MOVED_FROM and IN_MOVED_TO
-    of MonitorOpen: INFilter = INFilter and IN_OPEN
-    of MonitorAll: INFilter = INFilter and IN_ALL_EVENTS
+    of MonitorAccess: INFilter = INFilter or IN_ACCESS
+    of MonitorAttrib: INFilter = INFilter or IN_ATTRIB
+    of MonitorCloseWrite: INFilter = INFilter or IN_CLOSE_WRITE
+    of MonitorCloseNoWrite: INFilter = INFilter or IN_CLOSE_NO_WRITE
+    of MonitorCreate: INFilter = INFilter or IN_CREATE
+    of MonitorDelete: INFilter = INFilter or IN_DELETE
+    of MonitorDeleteSelf: INFilter = INFilter or IN_DELETE_SELF
+    of MonitorModify: INFilter = INFilter or IN_MODIFY
+    of MonitorMoveSelf: INFilter = INFilter or IN_MOVE_SELF
+    of MonitorMoved: INFilter = INFilter or IN_MOVED_FROM or IN_MOVED_TO
+    of MonitorOpen: INFilter = INFilter or IN_OPEN
+    of MonitorAll: INFilter = INFilter or IN_ALL_EVENTS
 
   result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
   if result < 0:
@@ -200,9 +200,18 @@ proc register*(d: Dispatcher, monitor: FSMonitor,
 
 when not defined(testing) and isMainModule:
   proc main =
-    var disp = newDispatcher()
-    var monitor = newMonitor()
-    echo monitor.add("/home/dom/inotifytests/")
+    var
+      disp = newDispatcher()
+      monitor = newMonitor()
+      n = 0
+    n = monitor.add("/tmp")
+    assert n == 1
+    n = monitor.add("/tmp", {MonitorAll})
+    assert n == 1
+    n = monitor.add("/tmp", {MonitorCloseWrite, MonitorCloseNoWrite})
+    assert n == 1
+    n = monitor.add("/tmp", {MonitorMoved, MonitorOpen, MonitorAccess})
+    assert n == 1
     disp.register(monitor,
       proc (m: FSMonitor, ev: MonitorEvent) =
         echo("Got event: ", ev.kind)
diff --git a/lib/pure/options.nim b/lib/pure/options.nim
index 3122d58b1..2abb80016 100644
--- a/lib/pure/options.nim
+++ b/lib/pure/options.nim
@@ -28,7 +28,7 @@
 ##
 ## .. code-block:: nim
 ##
-##   import optionals
+##   import options
 ##
 ##   proc find(haystack: string, needle: char): Option[int] =
 ##     for i, c in haystack:
@@ -156,7 +156,7 @@ proc `$`*[T]( self: Option[T] ): string =
 when isMainModule:
   import unittest, sequtils
 
-  suite "optionals":
+  suite "options":
     # work around a bug in unittest
     let intNone = none(int)
     let stringNone = none(string)
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 7c14c46cf..1e00f92b1 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -1361,7 +1361,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
   # /proc/<pid>/file
   when defined(windows):
     when useWinUnicode:
-      var buf = cast[WideCString](alloc(256*2))
+      var buf = newWideCString("", 256)
       var len = getModuleFileNameW(0, buf, 256)
       result = buf$len
     else:
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 6c561eaf9..aa29bb073 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -1344,8 +1344,8 @@ proc editDistance*(a, b: string): int {.noSideEffect,
 
 
 # floating point formating:
-
-proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
+when not defined(js):
+  proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
                                      importc: "sprintf", varargs, noSideEffect.}
 
 type
@@ -1370,29 +1370,44 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
   ## after the decimal point for Nim's ``biggestFloat`` type.
   ##
   ## If ``precision == 0``, it tries to format it nicely.
-  const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
-  var
-    frmtstr {.noinit.}: array[0..5, char]
-    buf {.noinit.}: array[0..2500, char]
-    L: cint
-  frmtstr[0] = '%'
-  if precision > 0:
-    frmtstr[1] = '#'
-    frmtstr[2] = '.'
-    frmtstr[3] = '*'
-    frmtstr[4] = floatFormatToChar[format]
-    frmtstr[5] = '\0'
-    L = c_sprintf(buf, frmtstr, precision, f)
+  when defined(js):
+    var res: cstring
+    case format
+    of ffDefault:
+      {.emit: "`res` = `f`.toString();".}
+    of ffDecimal:
+      {.emit: "`res` = `f`.toFixed(`precision`);".}
+    of ffScientific:
+      {.emit: "`res` = `f`.toExponential(`precision`);".}
+    result = $res
+    for i in 0 ..< result.len:
+      # Depending on the locale either dot or comma is produced,
+      # but nothing else is possible:
+      if result[i] in {'.', ','}: result[i] = decimalsep
   else:
-    frmtstr[1] = floatFormatToChar[format]
-    frmtstr[2] = '\0'
-    L = c_sprintf(buf, frmtstr, f)
-  result = newString(L)
-  for i in 0 ..< L:
-    # Depending on the locale either dot or comma is produced,
-    # but nothing else is possible:
-    if buf[i] in {'.', ','}: result[i] = decimalsep
-    else: result[i] = buf[i]
+    const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
+    var
+      frmtstr {.noinit.}: array[0..5, char]
+      buf {.noinit.}: array[0..2500, char]
+      L: cint
+    frmtstr[0] = '%'
+    if precision > 0:
+      frmtstr[1] = '#'
+      frmtstr[2] = '.'
+      frmtstr[3] = '*'
+      frmtstr[4] = floatFormatToChar[format]
+      frmtstr[5] = '\0'
+      L = c_sprintf(buf, frmtstr, precision, f)
+    else:
+      frmtstr[1] = floatFormatToChar[format]
+      frmtstr[2] = '\0'
+      L = c_sprintf(buf, frmtstr, f)
+    result = newString(L)
+    for i in 0 ..< L:
+      # Depending on the locale either dot or comma is produced,
+      # but nothing else is possible:
+      if buf[i] in {'.', ','}: result[i] = decimalsep
+      else: result[i] = buf[i]
 
 proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
                   precision: range[0..32] = 16; decimalSep = '.'): string {.
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index a478b9d65..c9854a650 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -1192,112 +1192,10 @@ proc timeToTimeInterval*(t: Time): TimeInterval =
   # Milliseconds not available from Time
 
 when isMainModule:
-  # $ date --date='@2147483647'
-  # Tue 19 Jan 03:14:07 GMT 2038
-
-  var t = getGMTime(fromSeconds(2147483647))
-  assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
-  assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
-
-  assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
-    " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
-    "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
-
-  assert t.format("yyyyMMddhhmmss") == "20380119031407"
-
-  var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
-  assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
-    " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
-    "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
-
-  when not defined(JS):
-    when sizeof(Time) == 8:
-      var t3 = getGMTime(fromSeconds(889067643645)) # Fri  7 Jun 19:20:45 BST 30143
-      assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
-        " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
-        "7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 0 00 00:00 UTC"
-      assert t3.format(":,[]()-/") == ":,[]()-/"
-
-  var t4 = getGMTime(fromSeconds(876124714)) # Mon  6 Oct 08:58:34 BST 1997
-  assert t4.format("M MM MMM MMMM") == "10 10 Oct October"
-
-  # Interval tests
-  assert((t4 - initInterval(years = 2)).format("yyyy") == "1995")
-  assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10")
-
-  var s = "Tuesday at 09:04am on Dec 15, 2015"
-  var f = "dddd at hh:mmtt on MMM d, yyyy"
-  assert($s.parse(f) == "Tue Dec 15 09:04:00 2015")
-  # ANSIC       = "Mon Jan _2 15:04:05 2006"
-  s = "Thu Jan 12 15:04:05 2006"
-  f = "ddd MMM dd HH:mm:ss yyyy"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  # UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
-  s = "Thu Jan 12 15:04:05 MST 2006"
-  f = "ddd MMM dd HH:mm:ss ZZZ yyyy"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  # RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
-  s = "Thu Jan 12 15:04:05 -07:00 2006"
-  f = "ddd MMM dd HH:mm:ss zzz yyyy"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  # RFC822      = "02 Jan 06 15:04 MST"
-  s = "12 Jan 16 15:04 MST"
-  f = "dd MMM yy HH:mm ZZZ"
-  assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
-  # RFC822Z     = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
-  s = "12 Jan 16 15:04 -07:00"
-  f = "dd MMM yy HH:mm zzz"
-  assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
-  # RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
-  s = "Monday, 12-Jan-06 15:04:05 MST"
-  f = "dddd, dd-MMM-yy HH:mm:ss ZZZ"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  # RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
-  s = "Thu, 12 Jan 2006 15:04:05 MST"
-  f = "ddd, dd MMM yyyy HH:mm:ss ZZZ"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  # RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
-  s = "Thu, 12 Jan 2006 15:04:05 -07:00"
-  f = "ddd, dd MMM yyyy HH:mm:ss zzz"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  # RFC3339     = "2006-01-02T15:04:05Z07:00"
-  s = "2006-01-12T15:04:05Z-07:00"
-  f = "yyyy-MM-ddTHH:mm:ssZzzz"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  f = "yyyy-MM-dd'T'HH:mm:ss'Z'zzz"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
-  s = "2006-01-12T15:04:05.999999999Z-07:00"
-  f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
-  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
-  # Kitchen     = "3:04PM"
-  s = "3:04PM"
-  f = "h:mmtt"
-  assert "15:04:00" in $s.parse(f)
-  when not defined(testing):
-    echo "Kitchen: " & $s.parse(f)
-    var ti = timeToTimeInfo(getTime())
-    echo "Todays date after decoding: ", ti
-    var tint = timeToTimeInterval(getTime())
-    echo "Todays date after decoding to interval: ", tint
-  # checking dayOfWeek matches known days
-  assert getDayOfWeek(21, 9, 1900) == dFri
-  assert getDayOfWeek(1, 1, 1970) == dThu
-  assert getDayOfWeek(21, 9, 1970) == dMon
-  assert getDayOfWeek(1, 1, 2000) == dSat
-  assert getDayOfWeek(1, 1, 2021) == dFri
-  # Julian tests
-  assert getDayOfWeekJulian(21, 9, 1900) == dFri
-  assert getDayOfWeekJulian(21, 9, 1970) == dMon
-  assert getDayOfWeekJulian(1, 1, 2000) == dSat
-  assert getDayOfWeekJulian(1, 1, 2021) == dFri
-
-  # toSeconds tests with GM and Local timezones
-  #var t4 = getGMTime(fromSeconds(876124714)) # Mon  6 Oct 08:58:34 BST 1997
-  var t4L = getLocalTime(fromSeconds(876124714))
-  assert toSeconds(timeInfoToTime(t4L)) == 876124714    # fromSeconds is effectively "localTime"
-  assert toSeconds(timeInfoToTime(t4L)) + t4L.timezone.float == toSeconds(timeInfoToTime(t4))
-
+  # this is testing non-exported function
+  var
+    t4 = getGMTime(fromSeconds(876124714)) # Mon  6 Oct 08:58:34 BST 1997
+    t4L = getLocalTime(fromSeconds(876124714))
   assert toSeconds(t4, initInterval(seconds=0)) == 0.0
   assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1))
   assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1))
@@ -1307,12 +1205,5 @@ when isMainModule:
   assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1))
   assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1))
 
-  # adding intervals
-  var
-    a1L = toSeconds(timeInfoToTime(t4L + initInterval(hours = 1))) + t4L.timezone.float
-    a1G = toSeconds(timeInfoToTime(t4)) + 60.0 * 60.0
-  assert a1L == a1G
-  # subtracting intervals
-  a1L = toSeconds(timeInfoToTime(t4L - initInterval(hours = 1))) + t4L.timezone.float
-  a1G = toSeconds(timeInfoToTime(t4)) - (60.0 * 60.0)
-  assert a1L == a1G
+  # Further tests are in tests/stdlib/ttime.nim
+  # koch test c stdlib
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index b059a7315..45f52eb7f 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -114,6 +114,7 @@ proc validateUtf8*(s: string): int =
     if ord(s[i]) <=% 127:
       inc(i)
     elif ord(s[i]) shr 5 == 0b110:
+      if ord(s[i]) < 0xc2: return i # Catch overlong ascii representations.
       if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
       else: return i
     elif ord(s[i]) shr 4 == 0b1110:
diff --git a/lib/system.nim b/lib/system.nim
index c5dd58c7b..ce7687c34 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -232,8 +232,8 @@ proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
   ##
   ## .. code-block:: nim
   ##  var arr = [1,2,3,4,5,6,7]
-  ##  high(arr) #=> 0
-  ##  high(2) #=> -9223372036854775808
+  ##  low(arr) #=> 0
+  ##  low(2) #=> -9223372036854775808
 
 type
   range*{.magic: "Range".}[T] ## Generic type to construct range types.
@@ -2584,11 +2584,7 @@ when not defined(JS): #and not defined(nimscript):
 
   when hasAlloc:
     var
-      strDesc: TNimType
-
-    strDesc.size = sizeof(string)
-    strDesc.kind = tyString
-    strDesc.flags = {ntfAcyclic}
+      strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
 
   when not defined(nimscript):
     include "system/ansi_c"
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 3ebbc8c1e..b4462ed83 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -40,7 +40,7 @@ when defined(emscripten):
     MAP_PRIVATE = 2'i32        # Changes are private
 
   var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
-  type 
+  type
     PEmscriptenMMapBlock = ptr EmscriptenMMapBlock
     EmscriptenMMapBlock {.pure, inheritable.} = object
       realSize: int        # size of previous chunk; for coalescing
@@ -399,6 +399,9 @@ iterator allObjects(m: MemRegion): pointer {.inline.} =
           let c = cast[PBigChunk](c)
           yield addr(c.data)
 
+proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
+                      magic: "Plugin", compileTime.}
+
 proc isCell(p: pointer): bool {.inline.} =
   result = cast[ptr FreeCell](p).zeroField >% 1
 
diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim
index bb5de6f42..776a2b7ec 100644
--- a/lib/system/cellsets.nim
+++ b/lib/system/cellsets.nim
@@ -201,6 +201,41 @@ iterator elements(t: CellSet): PCell {.inline.} =
       inc(i)
     r = r.next
 
+when false:
+  type
+    CellSetIter = object
+      p: PPageDesc
+      i, w, j: int
+
+  proc next(it: var CellSetIter): PCell =
+    while true:
+      while it.w != 0:         # test all remaining bits for zero
+        if (it.w and 1) != 0:  # the bit is set!
+          result = cast[PCell]((it.p.key shl PageShift) or
+                               (it.i shl IntShift +% it.j) *% MemAlign)
+
+          inc(it.j)
+          it.w = it.w shr 1
+          return
+        else:
+          inc(it.j)
+          it.w = it.w shr 1
+      # load next w:
+      if it.i >= high(it.p.bits):
+        it.i = 0
+        it.j = 0
+        it.p = it.p.next
+        if it.p == nil: return nil
+      else:
+        inc it.i
+      it.w = it.p.bits[i]
+
+  proc init(it: var CellSetIter; t: CellSet): PCell =
+    it.p = t.head
+    it.i = -1
+    it.w = 0
+    result = it.next
+
 iterator elementsExcept(t, s: CellSet): PCell {.inline.} =
   var r = t.head
   while r != nil:
diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim
index 4043c8714..22ac613f8 100644
--- a/lib/system/dyncalls.nim
+++ b/lib/system/dyncalls.nim
@@ -68,7 +68,9 @@ when defined(posix):
 
   proc nimLoadLibrary(path: string): LibHandle =
     result = dlopen(path, RTLD_NOW)
-    #c_fprintf(c_stdout, "%s\n", dlerror())
+    let error = dlerror()
+    if error != nil:
+      c_fprintf(c_stdout, "%s\n", error)
 
   proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
     result = dlsym(lib, name)
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 0c632aeb1..c25cf4606 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -558,7 +558,7 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
       # we split the old refcount in 2 parts. XXX This is still not entirely
       # correct if the pointer that receives growObj's result is on the stack.
       # A better fix would be to emit the location specific write barrier for
-      # 'growObj', but this is lost of more work and who knows what new problems
+      # 'growObj', but this is lots of more work and who knows what new problems
       # this would create.
       res.refcount = rcIncrement
       decRef(ol)
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
index 4ca0d144f..e68a8586e 100644
--- a/lib/system/gc2.nim
+++ b/lib/system/gc2.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,13 +9,15 @@
 
 #            Garbage Collector
 #
-# The basic algorithm is *Deferrent Reference Counting* with cycle detection.
-# This is achieved by combining a Deutsch-Bobrow garbage collector
-# together with Christoper's partial mark-sweep garbage collector.
-#
-# Special care has been taken to avoid recursion as far as possible to avoid
-# stack overflows when traversing deep datastructures. It is well-suited
-# for soft real time applications (like games).
+# The basic algorithm is *Deferred Reference Counting* with an incremental mark
+# and sweep GC to free cycles. It is hard realtime in that if you play
+# according to its rules, no deadline will ever be missed.
+
+# XXX Ensure by smart color masking that the object is not in the ZCT.
+
+when defined(nimCoroutines):
+  import arch
+
 {.push profiler:off.}
 
 const
@@ -29,82 +31,36 @@ const
 when withRealTime and not declared(getTicks):
   include "system/timers"
 when defined(memProfiler):
-  proc nimProfile(requestedSize: int)
-
-const
-  rcShift = 6 # the reference count is shifted so we can use
-              # the least significat bits for additinal flags:
-
-  rcAlive = 0b00000           # object is reachable.
-                              # color *black* in the original paper
-
-  rcCycleCandidate = 0b00001  # possible root of a cycle. *purple*
-
-  rcDecRefApplied = 0b00010   # the first dec-ref phase of the
-                              # collector was already applied to this
-                              # object. *gray*
-
-  rcMaybeDead = 0b00011       # this object is a candidate for deletion
-                              # during the collect cycles algorithm.
-                              # *white*.
-
-  rcReallyDead = 0b00100      # this is proved to be garbage
-
-  rcRetiredBuffer = 0b00101   # this is a seq or string buffer that
-                              # was replaced by a resize operation.
-                              # see growObj for details
+  proc nimProfile(requestedSize: int) {.benign.}
 
-  rcColorMask = RefCount(0b00111)
-
-  rcZct = 0b01000             # already added to ZCT
-  rcInCycleRoots = 0b10000    # already buffered as cycle candidate
-  rcHasStackRef = 0b100000    # the object had a stack ref in the last
-                              # cycle collection
-
-  rcMarkBit = rcHasStackRef   # this is currently used for leak detection
-                              # when traceGC is on
-
-  rcBufferedAnywhere = rcZct or rcInCycleRoots
+type
+  ObjectSpaceIter = object
+    state: range[-1..0]
 
-  rcIncrement = 1 shl rcShift # don't touch the color bits
+iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc)
 
 const
-  NewObjectsAreCycleRoots = true
-    # the alternative is to use the old strategy of adding cycle roots
-    # in incRef (in the compiler itself, this doesn't change much)
-
-  IncRefRemovesCandidates = false
-    # this is safe only if we can reliably track the fact that the object
-    # has stack references. This could be easily done by adding another bit
-    # to the refcount field and setting it up in unmarkStackAndRegisters.
-    # The bit must also be set for new objects that are not rc1 and it must be
-    # examined in the decref loop in collectCycles.
-    # XXX: not implemented yet as tests didn't show any improvement from this
-
-  MarkingSkipsAcyclicObjects = true
-    # Acyclic objects can be safely ignored in the mark and scan phases,
-    # because they cannot contribute to the internal count.
-    # XXX: if we generate specialized `markCyclic` and `markAcyclic`
-    # procs we can further optimize this as there won't be need for any
-    # checks in the code
-
-  MinimumStackMarking = false
-    # Try to scan only the user stack and ignore the part of the stack
-    # belonging to the GC itself. see setStackTop for further info.
-    # XXX: still has problems in release mode in the compiler itself.
-    # investigate how it affects growObj
-
-  CollectCyclesStats = false
-
+  rcIncrement = 0b1000 # so that lowest 3 bits are not touched
+  rcBlackOrig = 0b000
+  rcWhiteOrig = 0b001
+  rcGrey = 0b010   # traditional color for incremental mark&sweep
+  rcUnused = 0b011
+  ZctFlag = 0b100  # in ZCT
+  rcShift = 3      # shift by rcShift to get the reference counter
+  colorMask = 0b011
 type
   WalkOp = enum
-    waPush
+    waMarkGlobal,    # part of the backup mark&sweep
+    waMarkGrey,
+    waZctDecRef #, waDebug
 
-  Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall.}
+  Phase {.pure.} = enum
+    None, Marking, Sweeping
+  Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
     # A ref type can have a finalizer that is called before the object's
     # storage is freed.
 
-  GcStat {.final, pure.} = object
+  GcStat = object
     stackScans: int          # number of performed stack scans (for statistics)
     cycleCollections: int    # number of performed full collections
     maxThreshold: int        # max threshold that has been set
@@ -113,134 +69,78 @@ type
     cycleTableSize: int      # max entries in cycle table
     maxPause: int64          # max measured GC pause in nanoseconds
 
-  GcHeap {.final, pure.} = object # this contains the zero count and
-                                   # non-zero count table
+  GcStack = object
+    prev: ptr GcStack
+    next: ptr GcStack
+    starts: pointer
+    pos: pointer
+    maxStackSize: int
+
+  GcHeap = object # this contains the zero count and
+                  # non-zero count table
+    black: int    # either 0 or 1.
+    stack: ptr GcStack
     stackBottom: pointer
-    stackTop: pointer
+    phase: Phase
     cycleThreshold: int
+    when useCellIds:
+      idGenerator: int
     zct: CellSeq             # the zero count table
     decStack: CellSeq        # cells in the stack that are to decref again
-    cycleRoots: CellSeq
-    tempStack: CellSeq       # temporary stack for recursion elimination
-    freeStack: CellSeq       # objects ready to be freed
+    greyStack: CellSeq
     recGcLock: int           # prevent recursion via finalizers; no thread lock
-    cycleRootsTrimIdx: int   # Trimming is a light-weight collection of the
-                             # cycle roots table that uses a cheap linear scan
-                             # to find only possitively dead objects.
-                             # One strategy is to perform it only for new objects
-                             # allocated between the invocations of collectZCT.
-                             # This index indicates the start of the range of
-                             # such new objects within the table.
     when withRealTime:
       maxPause: Nanos        # max allowed pause in nanoseconds; active if > 0
     region: MemRegion        # garbage collected region
     stat: GcStat
-{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcStat: GcStat,
-              TGcHeap: GcHeap].}
+    additionalRoots: CellSeq # dummy roots for GC_ref/unref
+    spaceIter: ObjectSpaceIter
+
 var
-  gch* {.rtlThreadVar.}: GcHeap
+  gch {.rtlThreadVar.}: GcHeap
 
 when not defined(useNimRtl):
   instantiateForRegion(gch.region)
 
-template acquire(gch: GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    AcquireSys(HeapLock)
-
-template release(gch: GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    releaseSys(HeapLock)
-
-template setColor(c: PCell, color) =
-  c.refcount = (c.refcount and not rcColorMask) or color
-
-template color(c: PCell): expr =
-  c.refcount and rcColorMask
-
-template isBitDown(c: PCell, bit): expr =
-  (c.refcount and bit) == 0
-
-template isBitUp(c: PCell, bit): expr =
-  (c.refcount and bit) != 0
-
-template setBit(c: PCell, bit): expr =
-  c.refcount = c.refcount or bit
-
-template isDead(c: Pcell): expr =
-  c.isBitUp(rcReallyDead) # also covers rcRetiredBuffer
-
-template clearBit(c: PCell, bit): expr =
-  c.refcount = c.refcount and (not RefCount(bit))
-
-when debugGC:
-  var gcCollectionIdx = 0
-
-  proc colorStr(c: PCell): cstring =
-    let color = c.color
-    case color
-    of rcAlive: return "alive"
-    of rcMaybeDead: return "maybedead"
-    of rcCycleCandidate: return "candidate"
-    of rcDecRefApplied: return "marked"
-    of rcRetiredBuffer: return "retired"
-    of rcReallyDead: return "dead"
-    else: return "unknown?"
-
-  proc inCycleRootsStr(c: PCell): cstring =
-    if c.isBitUp(rcInCycleRoots): result = "cycleroot"
-    else: result = ""
-
-  proc inZctStr(c: PCell): cstring =
-    if c.isBitUp(rcZct): result = "zct"
-    else: result = ""
-
-  proc writeCell*(msg: CString, c: PCell, force = false) =
-    var kind = -1
-    if c.typ != nil: kind = ord(c.typ.kind)
-    when trackAllocationSource:
-      c_fprintf(c_stdout, "[GC %d] %s: %p %d rc=%ld %s %s %s from %s(%ld)\n",
-                gcCollectionIdx,
-                msg, c, kind, c.refcount shr rcShift,
-                c.colorStr, c.inCycleRootsStr, c.inZctStr,
-                c.filename, c.line)
-    else:
-      c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld\n",
-                msg, c, kind, c.refcount shr rcShift)
-
-proc addZCT(zct: var CellSeq, c: PCell) {.noinline.} =
-  if c.isBitDown(rcZct):
-    c.setBit rcZct
-    zct.add c
-
-template setStackTop(gch) =
-  # This must be called immediately after we enter the GC code
-  # to minimize the size of the scanned stack. The stack consumed
-  # by the GC procs may amount to 200-400 bytes depending on the
-  # build settings and this contributes to false-positives
-  # in the conservative stack marking
-  when MinimumStackMarking:
-    var stackTop {.volatile.}: pointer
-    gch.stackTop = addr(stackTop)
-
-template addCycleRoot(cycleRoots: var CellSeq, c: PCell) =
-  if c.color != rcCycleCandidate:
-    c.setColor rcCycleCandidate
-
-    # the object may be buffered already. for example, consider:
-    # decref; incref; decref
-    if c.isBitDown(rcInCycleRoots):
-      c.setBit rcInCycleRoots
-      cycleRoots.add c
+proc initGC() =
+  when not defined(useNimRtl):
+    when traceGC:
+      for i in low(CellState)..high(CellState): init(states[i])
+    gch.cycleThreshold = InitialCycleThreshold
+    gch.stat.stackScans = 0
+    gch.stat.cycleCollections = 0
+    gch.stat.maxThreshold = 0
+    gch.stat.maxStackSize = 0
+    gch.stat.maxStackCells = 0
+    gch.stat.cycleTableSize = 0
+    # init the rt
+    init(gch.zct)
+    init(gch.decStack)
+    init(gch.additionalRoots)
+    init(gch.greyStack)
+
+template gcAssert(cond: bool, msg: string) =
+  when defined(useGcAssert):
+    if not cond:
+      echo "[GCASSERT] ", msg
+      GC_disable()
+      writeStackTrace()
+      quit 1
+
+proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
+  if (c.refcount and ZctFlag) == 0:
+    c.refcount = c.refcount or ZctFlag
+    add(s, c)
 
 proc cellToUsr(cell: PCell): pointer {.inline.} =
   # convert object (=pointer to refcount) to pointer to userdata
   result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
 
-proc usrToCell*(usr: pointer): PCell {.inline.} =
+proc usrToCell(usr: pointer): PCell {.inline.} =
   # convert pointer to userdata to object (=pointer to refcount)
   result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
 
-proc canbeCycleRoot(c: PCell): bool {.inline.} =
+proc canBeCycleRoot(c: PCell): bool {.inline.} =
   result = ntfAcyclic notin c.typ.flags
 
 proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
@@ -254,14 +154,40 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
 when BitsPerPage mod (sizeof(int)*8) != 0:
   {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
 
+template color(c): expr = c.refCount and colorMask
+template setColor(c, col) =
+  c.refcount = c.refcount and not colorMask or col
+
+proc writeCell(msg: cstring, c: PCell) =
+  var kind = -1
+  if c.typ != nil: kind = ord(c.typ.kind)
+  when leakDetector:
+    c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n",
+              msg, c, kind, c.refcount shr rcShift, c.filename, c.line)
+  else:
+    c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld; color=%ld\n",
+              msg, c, kind, c.refcount shr rcShift, c.color)
+
+template gcTrace(cell, state: expr): stmt {.immediate.} =
+  when traceGC: traceCell(cell, state)
+
 # forward declarations:
-proc collectCT(gch: var GcHeap)
-proc isOnStack*(p: pointer): bool {.noinline.}
-proc forAllChildren(cell: PCell, op: WalkOp)
-proc doOperation(p: pointer, op: WalkOp)
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp)
+proc collectCT(gch: var GcHeap) {.benign.}
+proc isOnStack(p: pointer): bool {.noinline, benign.}
+proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
+proc doOperation(p: pointer, op: WalkOp) {.benign.}
+proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
 # we need the prototype here for debugging purposes
 
+when hasThreadSupport and hasSharedHeap:
+  template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement
+  template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement)
+else:
+  template `--`(x: expr): expr =
+    dec(x, rcIncrement)
+    x <% rcIncrement
+  template `++`(x: expr): stmt = inc(x, rcIncrement)
+
 proc prepareDealloc(cell: PCell) =
   if cell.typ.finalizer != nil:
     # the finalizer could invoke something that
@@ -273,246 +199,127 @@ proc prepareDealloc(cell: PCell) =
     (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
     dec(gch.recGcLock)
 
-when traceGC:
-  # traceGC is a special switch to enable extensive debugging
-  type
-    CellState = enum
-      csAllocated, csFreed
-  {.deprecated: [TCellState: CellState].}
-  var
-    states: array[CellState, CellSet]
-
-  proc traceCell(c: PCell, state: CellState) =
-    case state
-    of csAllocated:
-      if c in states[csAllocated]:
-        writeCell("attempt to alloc an already allocated cell", c)
-        sysAssert(false, "traceCell 1")
-      excl(states[csFreed], c)
-      # writecell("allocated", c)
-    of csFreed:
-      if c in states[csFreed]:
-        writeCell("attempt to free a cell twice", c)
-        sysAssert(false, "traceCell 2")
-      if c notin states[csAllocated]:
-        writeCell("attempt to free not an allocated cell", c)
-        sysAssert(false, "traceCell 3")
-      excl(states[csAllocated], c)
-      # writecell("freed", c)
-    incl(states[state], c)
-
-  proc computeCellWeight(c: PCell): int =
-    var x: CellSet
-    x.init
-
-    let startLen = gch.tempStack.len
-    c.forAllChildren waPush
-
-    while startLen != gch.tempStack.len:
-      dec gch.tempStack.len
-      var c = gch.tempStack.d[gch.tempStack.len]
-      if c in states[csFreed]: continue
-      inc result
-      if c notin x:
-        x.incl c
-        c.forAllChildren waPush
-
-  template markChildrenRec(cell) =
-    let startLen = gch.tempStack.len
-    cell.forAllChildren waPush
-    let isMarked = cell.isBitUp(rcMarkBit)
-    while startLen != gch.tempStack.len:
-      dec gch.tempStack.len
-      var c = gch.tempStack.d[gch.tempStack.len]
-      if c in states[csFreed]: continue
-      if c.isBitDown(rcMarkBit):
-        c.setBit rcMarkBit
-        c.forAllChildren waPush
-    if c.isBitUp(rcMarkBit) and not isMarked:
-      writecell("cyclic cell", cell)
-      cprintf "Weight %d\n", cell.computeCellWeight
-
-  proc writeLeakage(onlyRoots: bool) =
-    if onlyRoots:
-      for c in elements(states[csAllocated]):
-        if c notin states[csFreed]:
-          markChildrenRec(c)
-    var f = 0
-    var a = 0
-    for c in elements(states[csAllocated]):
-      inc a
-      if c in states[csFreed]: inc f
-      elif c.isBitDown(rcMarkBit):
-        writeCell("leak", c)
-        cprintf "Weight %d\n", c.computeCellWeight
-    cfprintf(cstdout, "Allocations: %ld; freed: %ld\n", a, f)
-
-template gcTrace(cell, state: expr): stmt {.immediate.} =
-  when logGC: writeCell($state, cell)
-  when traceGC: traceCell(cell, state)
-
-template WithHeapLock(blk: stmt): stmt =
-  when hasThreadSupport and hasSharedHeap: AcquireSys(HeapLock)
-  blk
-  when hasThreadSupport and hasSharedHeap: ReleaseSys(HeapLock)
-
 proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
   # we MUST access gch as a global here, because this crosses DLL boundaries!
-  WithHeapLock: addCycleRoot(gch.cycleRoots, c)
+  discard
 
 proc rtlAddZCT(c: PCell) {.rtl, inl.} =
   # we MUST access gch as a global here, because this crosses DLL boundaries!
-  WithHeapLock: addZCT(gch.zct, c)
+  addZCT(gch.zct, c)
 
-type
-  CyclicMode = enum
-    Cyclic,
-    Acyclic,
-    MaybeCyclic
-
-  ReleaseType = enum
-    AddToZTC
-    FreeImmediately
-
-  HeapType = enum
-    LocalHeap
-    SharedHeap
-{.deprecated: [TCyclicMode: CyclicMode, TReleaseType: ReleaseType,
-              THeapType: HeapType].}
-
-template `++` (rc: RefCount, heapType: HeapType): stmt =
-  when heapType == SharedHeap:
-    discard atomicInc(rc, rcIncrement)
-  else:
-    inc rc, rcIncrement
-
-template `--`(rc: RefCount): expr =
-  dec rc, rcIncrement
-  rc <% rcIncrement
-
-template `--` (rc: RefCount, heapType: HeapType): expr =
-  (when heapType == SharedHeap: atomicDec(rc, rcIncrement) <% rcIncrement else: --rc)
-
-template doDecRef(cc: PCell,
-                  heapType = LocalHeap,
-                  cycleFlag = MaybeCyclic): stmt =
-  var c = cc
-  sysAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr")
-  # XXX: move this elesewhere
-
-  sysAssert(c.refcount >=% rcIncrement, "decRef")
-  if c.refcount--(heapType):
-    # this is the last reference from the heap
-    # add to a zero-count-table that will be matched against stack pointers
+proc decRef(c: PCell) {.inline.} =
+  gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr")
+  gcAssert(c.refcount >=% rcIncrement, "decRef")
+  if --c.refcount:
     rtlAddZCT(c)
-  else:
-    when cycleFlag != Acyclic:
-      if cycleFlag == Cyclic or canBeCycleRoot(c):
-        # a cycle may have been broken
-        rtlAddCycleRoot(c)
-
-template doIncRef(cc: PCell,
-                 heapType = LocalHeap,
-                 cycleFlag = MaybeCyclic): stmt =
-  var c = cc
-  c.refcount++(heapType)
-  when cycleFlag != Acyclic:
-    when NewObjectsAreCycleRoots:
-      if canbeCycleRoot(c):
-        addCycleRoot(gch.cycleRoots, c)
-    elif IncRefRemovesCandidates:
-      c.setColor rcAlive
-  # XXX: this is not really atomic enough!
-
-proc nimGCref(p: pointer) {.compilerProc, inline.} = doIncRef(usrToCell(p))
-proc nimGCunref(p: pointer) {.compilerProc, inline.} = doDecRef(usrToCell(p))
+
+proc incRef(c: PCell) {.inline.} =
+  gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
+  c.refcount = c.refcount +% rcIncrement
+
+proc nimGCref(p: pointer) {.compilerProc.} =
+  let cell = usrToCell(p)
+  incRef(cell)
+  add(gch.additionalRoots, cell)
+
+proc nimGCunref(p: pointer) {.compilerProc.} =
+  let cell = usrToCell(p)
+  decRef(cell)
+  var L = gch.additionalRoots.len-1
+  var i = L
+  let d = gch.additionalRoots.d
+  while i >= 0:
+    if d[i] == cell:
+      d[i] = d[L]
+      dec gch.additionalRoots.len
+      break
+    dec(i)
+
+template markGrey(x: PCell) =
+  if x.color == 1-gch.black and gch.phase == Phase.Marking:
+    x.setColor(rcGrey)
+    add(gch.greyStack, x)
+
+proc GC_addCycleRoot*[T](p: ref T) {.inline.} =
+  ## adds 'p' to the cycle candidate set for the cycle collector. It is
+  ## necessary if you used the 'acyclic' pragma for optimization
+  ## purposes and need to break cycles manually.
+  rtlAddCycleRoot(usrToCell(cast[pointer](p)))
 
 proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
   sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle")
   var c = usrToCell(p)
-  sysAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr")
-  if c.refcount--(LocalHeap):
+  gcAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr")
+  if --c.refcount:
     rtlAddZCT(c)
     sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2")
   sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5")
 
-template doAsgnRef(dest: PPointer, src: pointer,
-                  heapType = LocalHeap, cycleFlag = MaybeCyclic): stmt =
-  sysAssert(not isOnStack(dest), "asgnRef")
-  # BUGFIX: first incRef then decRef!
-  if src != nil: doIncRef(usrToCell(src), heapType, cycleFlag)
-  if dest[] != nil: doDecRef(usrToCell(dest[]), heapType, cycleFlag)
-  dest[] = src
-
 proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
   # the code generator calls this proc!
-  doAsgnRef(dest, src, LocalHeap, MaybeCyclic)
+  gcAssert(not isOnStack(dest), "asgnRef")
+  # BUGFIX: first incRef then decRef!
+  if src != nil:
+    let s = usrToCell(src)
+    incRef(s)
+    markGrey(s)
+  if dest[] != nil: decRef(usrToCell(dest[]))
+  dest[] = src
 
 proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
   # the code generator calls this proc if it is known at compile time that no
   # cycle is possible.
-  doAsgnRef(dest, src, LocalHeap, Acyclic)
+  gcAssert(not isOnStack(dest), "asgnRefNoCycle")
+  if src != nil:
+    var c = usrToCell(src)
+    ++c.refcount
+    markGrey(c)
+  if dest[] != nil:
+    var c = usrToCell(dest[])
+    if --c.refcount:
+      rtlAddZCT(c)
+  dest[] = src
 
 proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
   # unsureAsgnRef updates the reference counters only if dest is not on the
   # stack. It is used by the code generator if it cannot decide wether a
   # reference is in the stack or not (this can happen for var parameters).
   if not isOnStack(dest):
-    if src != nil: doIncRef(usrToCell(src))
-    # XXX we must detect a shared heap here
-    # better idea may be to just eliminate the need for unsureAsgnRef
-    #
+    if src != nil:
+      let s = usrToCell(src)
+      incRef(s)
+      markGrey(s)
     # XXX finally use assembler for the stack checking instead!
     # the test for '!= nil' is correct, but I got tired of the segfaults
     # resulting from the crappy stack checking:
-    if cast[int](dest[]) >=% PageSize: doDecRef(usrToCell(dest[]))
+    if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
   else:
     # can't be an interior pointer if it's a stack location!
-    sysAssert(interiorAllocatedPtr(gch.region, dest)==nil,
-              "stack loc AND interior pointer")
+    gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
+             "stack loc AND interior pointer")
   dest[] = src
 
-when hasThreadSupport and hasSharedHeap:
-  # shared heap version of the above procs
-  proc asgnRefSh(dest: PPointer, src: pointer) {.compilerProc, inline.} =
-    doAsgnRef(dest, src, SharedHeap, MaybeCyclic)
-
-  proc asgnRefNoCycleSh(dest: PPointer, src: pointer) {.compilerProc, inline.} =
-    doAsgnRef(dest, src, SharedHeap, Acyclic)
+type
+  GlobalMarkerProc = proc () {.nimcall, benign.}
+var
+  globalMarkersLen: int
+  globalMarkers: array[0.. 7_000, GlobalMarkerProc]
 
-proc initGC() =
-  when not defined(useNimRtl):
-    when traceGC:
-      for i in low(CellState)..high(CellState): init(states[i])
-    gch.cycleThreshold = InitialCycleThreshold
-    gch.stat.stackScans = 0
-    gch.stat.cycleCollections = 0
-    gch.stat.maxThreshold = 0
-    gch.stat.maxStackSize = 0
-    gch.stat.maxStackCells = 0
-    gch.stat.cycleTableSize = 0
-    # init the rt
-    init(gch.zct)
-    init(gch.tempStack)
-    init(gch.freeStack)
-    init(gch.cycleRoots)
-    init(gch.decStack)
+proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
+  if globalMarkersLen <= high(globalMarkers):
+    globalMarkers[globalMarkersLen] = markerProc
+    inc globalMarkersLen
+  else:
+    echo "[GC] cannot register global variable; too many global variables"
+    quit 1
 
-proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) =
+proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
   var d = cast[ByteAddress](dest)
   case n.kind
   of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
   of nkList:
     for i in 0..n.len-1:
-      # inlined for speed
-      if n.sons[i].kind == nkSlot:
-        if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
-          doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
-        else:
-          forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
-                            n.sons[i].typ, op)
-      else:
-        forAllSlotsAux(dest, n.sons[i], op)
+      forAllSlotsAux(dest, n.sons[i], op)
   of nkCase:
     var m = selectBranch(dest, n)
     if m != nil: forAllSlotsAux(dest, m, op)
@@ -533,9 +340,10 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
     else: discard
 
 proc forAllChildren(cell: PCell, op: WalkOp) =
-  sysAssert(cell != nil, "forAllChildren: 1")
-  sysAssert(cell.typ != nil, "forAllChildren: 2")
-  sysAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
+  gcAssert(cell != nil, "forAllChildren: 1")
+  gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
+  gcAssert(cell.typ != nil, "forAllChildren: 3")
+  gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
   let marker = cell.typ.marker
   if marker != nil:
     marker(cellToUsr(cell), op.int)
@@ -547,10 +355,9 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
       var d = cast[ByteAddress](cellToUsr(cell))
       var s = cast[PGenericSeq](d)
       if s != nil:
-        let baseAddr = d +% GenericSeqSize
         for i in 0..s.len-1:
-          forAllChildrenAux(cast[pointer](baseAddr +% i *% cell.typ.base.size),
-                            cell.typ.base, op)
+          forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
+            GenericSeqSize), cell.typ.base, op)
     else: discard
 
 proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
@@ -571,7 +378,7 @@ proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
     template replaceZctEntry(i: expr) =
       c = d[i]
       if c.refcount >=% rcIncrement:
-        c.clearBit(rcZct)
+        c.refcount = c.refcount and not ZctFlag
         d[i] = res
         return
     if L > 8:
@@ -592,408 +399,335 @@ proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
     for i in countdown(L-1, max(0, L-8)):
       var c = d[i]
       if c.refcount >=% rcIncrement:
-        c.clearBit(rcZct)
+        c.refcount = c.refcount and not ZctFlag
         d[i] = res
         return
     add(gch.zct, res)
 
-proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer =
+{.push stackTrace: off, profiler:off.}
+proc gcInvariant*() =
+  sysAssert(allocInv(gch.region), "injected")
+  when declared(markForDebug):
+    markForDebug(gch)
+{.pop.}
+
+proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   # generates a new object and sets its reference counter to 0
-  acquire(gch)
   sysAssert(allocInv(gch.region), "rawNewObj begin")
-  sysAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
-
+  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch)
-  sysAssert(allocInv(gch.region), "rawNewObj after collect")
-
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
-  sysAssert(allocInv(gch.region), "rawNewObj after rawAlloc")
-
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
-
+  gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
+  # now it is buffered in the ZCT
   res.typ = typ
-
-  when trackAllocationSource and not hasThreadSupport:
-    if framePtr != nil and framePtr.prev != nil and framePtr.prev.prev != nil:
-      res.filename = framePtr.prev.prev.filename
-      res.line = framePtr.prev.prev.line
-    else:
-      res.filename = "nofile"
-
-  if rc1:
-    res.refcount = rcIncrement # refcount is 1
-  else:
-    # its refcount is zero, so add it to the ZCT:
-    res.refcount = rcZct
-    addNewObjToZCT(res, gch)
-
-    if NewObjectsAreCycleRoots and canBeCycleRoot(res):
-      res.setBit(rcInCycleRoots)
-      res.setColor rcCycleCandidate
-      gch.cycleRoots.add res
-
+  when leakDetector and not hasThreadSupport:
+    if framePtr != nil and framePtr.prev != nil:
+      res.filename = framePtr.prev.filename
+      res.line = framePtr.prev.line
+  # refcount is zero, color is black, but mark it to be in the ZCT
+  res.refcount = ZctFlag or gch.black
   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)
   gcTrace(res, csAllocated)
-  release(gch)
+  when useCellIds:
+    inc gch.idGenerator
+    res.id = gch.idGenerator
   result = cellToUsr(res)
   sysAssert(allocInv(gch.region), "rawNewObj end")
 
 {.pop.}
 
-proc freeCell(gch: var GcHeap, c: PCell) =
-  # prepareDealloc(c)
-  gcTrace(c, csFreed)
-
-  when reallyDealloc: rawDealloc(gch.region, c)
-  else:
-    sysAssert(c.typ != nil, "collectCycles")
-    zeroMem(c, sizeof(Cell))
-
-template eraseAt(cells: var CellSeq, at: int): stmt =
-  cells.d[at] = cells.d[cells.len - 1]
-  dec cells.len
-
-template trimAt(roots: var CellSeq, at: int): stmt =
-  # This will remove a cycle root candidate during trimming.
-  # a candidate is removed either because it received a refup and
-  # it's no longer a candidate or because it received further refdowns
-  # and now it's dead for sure.
-  let c = roots.d[at]
-  c.clearBit(rcInCycleRoots)
-  roots.eraseAt(at)
-  if c.isBitUp(rcReallyDead) and c.refcount <% rcIncrement:
-    # This case covers both dead objects and retired buffers
-    # That's why we must also check the refcount (it may be
-    # kept possitive by stack references).
-    freeCell(gch, c)
+proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
+  result = rawNewObj(typ, size, gch)
+  when defined(memProfiler): nimProfile(size)
 
 proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  setStackTop(gch)
-  result = rawNewObj(typ, size, gch, false)
+  result = rawNewObj(typ, size, gch)
   zeroMem(result, size)
   when defined(memProfiler): nimProfile(size)
 
-proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  setStackTop(gch)
-  result = rawNewObj(typ, size, gch, false)
-  when defined(memProfiler): nimProfile(size)
-
 proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  setStackTop(gch)
   # `newObj` already uses locks, so no need for them here.
   let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
   result = newObj(typ, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
+  when defined(memProfiler): nimProfile(size)
 
 proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  setStackTop(gch)
-  result = rawNewObj(typ, size, gch, true)
+  # generates a new object and sets its reference counter to 1
+  sysAssert(allocInv(gch.region), "newObjRC1 begin")
+  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
+  collectCT(gch)
+  sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
+
+  var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
+  sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
+  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
+  # now it is buffered in the ZCT
+  res.typ = typ
+  when leakDetector and not hasThreadSupport:
+    if framePtr != nil and framePtr.prev != nil:
+      res.filename = framePtr.prev.filename
+      res.line = framePtr.prev.line
+  res.refcount = rcIncrement or gch.black # refcount is 1
+  sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
+  when logGC: writeCell("new cell", res)
+  gcTrace(res, csAllocated)
+  when useCellIds:
+    inc gch.idGenerator
+    res.id = gch.idGenerator
+  result = cellToUsr(res)
+  zeroMem(result, size)
+  sysAssert(allocInv(gch.region), "newObjRC1 end")
   when defined(memProfiler): nimProfile(size)
 
 proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  setStackTop(gch)
   let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
   result = newObjRC1(typ, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
+  when defined(memProfiler): nimProfile(size)
 
 proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
-  acquire(gch)
   collectCT(gch)
   var ol = usrToCell(old)
+  gcAssert(isAllocatedPtr(gch.region, ol), "growObj: freed pointer?")
+
   sysAssert(ol.typ != nil, "growObj: 1")
-  sysAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
+  gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
   sysAssert(allocInv(gch.region), "growObj begin")
 
   var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
-  var elemSize = if ol.typ.kind != tyString: ol.typ.base.size
-                 else: 1
+  var elemSize = 1
+  if ol.typ.kind != tyString: elemSize = ol.typ.base.size
 
-  var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
-
-  # XXX: This should happen outside
-  # call user-defined move code
-  # call user-defined default constructor
+  let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
   copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
+  zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
           newsize-oldsize)
-
   sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
-  sysAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
-
-  when false:
-    if ol.isBitUp(rcZct):
-      var j = gch.zct.len-1
-      var d = gch.zct.d
-      while j >= 0:
-        if d[j] == ol:
-          d[j] = res
-          break
-        dec(j)
-
-    if ol.isBitUp(rcInCycleRoots):
-      for i in 0 .. <gch.cycleRoots.len:
-        if gch.cycleRoots.d[i] == ol:
-          eraseAt(gch.cycleRoots, i)
-
-    freeCell(gch, ol)
-
-  else:
-    # the new buffer inherits the GC state of the old one
-    if res.isBitUp(rcZct): gch.zct.add res
-    if res.isBitUp(rcInCycleRoots): gch.cycleRoots.add res
-
-    # Pay attention to what's going on here! We're not releasing the old memory.
-    # This is because at this point there may be an interior pointer pointing
-    # into this buffer somewhere on the stack (due to `var` parameters now and
-    # and `let` and `var:var` stack locations in the future).
-    # We'll release the memory in the next GC cycle. If we release it here,
-    # we cannot guarantee that no memory will be corrupted when only safe
-    # language features are used. Accessing the memory after the seq/string
-    # has been invalidated may still result in logic errors in the user code.
-    # We may improve on that by protecting the page in debug builds or
-    # by providing a warning when we detect a stack pointer into it.
-    let bufferFlags = ol.refcount and rcBufferedAnywhere
-    if bufferFlags == 0:
-      # we need this in order to collect it safely later
-      ol.refcount = rcRetiredBuffer or rcZct
-      gch.zct.add ol
-    else:
-      ol.refcount = rcRetiredBuffer or bufferFlags
-
-    when logGC:
-      writeCell("growObj old cell", ol)
-      writeCell("growObj new cell", res)
-
+  # 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)
+  gcTrace(ol, csZctFreed)
   gcTrace(res, csAllocated)
-  release(gch)
+  when reallyDealloc:
+    sysAssert(allocInv(gch.region), "growObj before dealloc")
+    if ol.refcount shr rcShift <=% 1:
+      # free immediately to save space:
+      if (ol.refcount and ZctFlag) != 0:
+        var j = gch.zct.len-1
+        var d = gch.zct.d
+        while j >= 0:
+          if d[j] == ol:
+            d[j] = res
+            break
+          dec(j)
+      rawDealloc(gch.region, ol)
+    else:
+      # we split the old refcount in 2 parts. XXX This is still not entirely
+      # correct if the pointer that receives growObj's result is on the stack.
+      # A better fix would be to emit the location specific write barrier for
+      # 'growObj', but this is lots of more work and who knows what new problems
+      # this would create.
+      res.refcount = rcIncrement or gch.black
+      decRef(ol)
+  else:
+    sysAssert(ol.typ != nil, "growObj: 5")
+    zeroMem(ol, sizeof(Cell))
+  when useCellIds:
+    inc gch.idGenerator
+    res.id = gch.idGenerator
   result = cellToUsr(res)
   sysAssert(allocInv(gch.region), "growObj end")
   when defined(memProfiler): nimProfile(newsize-oldsize)
 
 proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
-  setStackTop(gch)
   result = growObj(old, newsize, gch)
 
 {.push profiler:off.}
 
-# ---------------- cycle collector -------------------------------------------
 
-proc doOperation(p: pointer, op: WalkOp) =
-  if p == nil: return
-  var c: PCell = usrToCell(p)
-  sysAssert(c != nil, "doOperation: 1")
-  gch.tempStack.add c
+template takeStartTime(workPackageSize) {.dirty.} =
+  const workPackage = workPackageSize
+  var debugticker = 1000
+  when withRealTime:
+    var steps = workPackage
+    var t0: Ticks
+    if gch.maxPause > 0: t0 = getticks()
 
-proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
-  doOperation(d, WalkOp(op))
+template takeTime {.dirty.} =
+  when withRealTime: dec steps
+  dec debugticker
+
+template checkTime {.dirty.} =
+  if debugticker <= 0:
+    echo "in loop"
+    debugticker = 1000
+  when withRealTime:
+    if steps == 0:
+      steps = workPackage
+      if gch.maxPause > 0:
+        let duration = getticks() - t0
+        # the GC's measuring is not accurate and needs some cleanup actions
+        # (stack unmarking), so subtract some short amount of time in
+        # order to miss deadlines less often:
+        if duration >= gch.maxPause - 50_000:
+          return false
 
-type
-  RecursionType = enum
-    FromChildren,
-    FromRoot
-{.deprecated: [TRecursionType: RecursionType].}
+# ---------------- cycle collector -------------------------------------------
 
-proc collectZCT(gch: var GcHeap): bool
+proc freeCyclicCell(gch: var GcHeap, c: PCell) =
+  gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?")
 
-template pseudoRecursion(typ: RecursionType, body: stmt): stmt =
-  discard
+  var d = gch.decStack.d
+  for i in 0..gch.decStack.len-1:
+    gcAssert d[i] != c, "wtf man, freeing obviously alive stuff?!!"
+
+  prepareDealloc(c)
+  gcTrace(c, csCycFreed)
+  when logGC: writeCell("cycle collector dealloc cell", c)
+  when reallyDealloc:
+    sysAssert(allocInv(gch.region), "free cyclic cell")
+    rawDealloc(gch.region, c)
+  else:
+    gcAssert(c.typ != nil, "freeCyclicCell")
+    zeroMem(c, sizeof(Cell))
 
-proc trimCycleRoots(gch: var GcHeap, startIdx = gch.cycleRootsTrimIdx) =
-  var i = startIdx
-  while i < gch.cycleRoots.len:
-    if gch.cycleRoots.d[i].color != rcCycleCandidate:
-      gch.cycleRoots.trimAt i
-    else:
-      inc i
+proc sweep(gch: var GcHeap): bool =
+  takeStartTime(100)
+  echo "loop start"
+  let black = gch.black
+  while true:
+    let x = allObjectsAsProc(gch.region, addr gch.spaceIter)
+    if gch.spaceIter.state < 0: break
+    takeTime()
+    if isCell(x):
+      # cast to PCell is correct here:
+      var c = cast[PCell](x)
+      gcAssert c.color != rcGrey, "cell is still grey?"
+      if c.color != black: freeCyclicCell(gch, c)
+      # Since this is incremental, we MUST not set the object to 'white' here.
+      # We could set all the remaining objects to white after the 'sweep'
+      # completed but instead we flip the meaning of black/white to save one
+      # traversal over the heap!
+    checkTime()
+  # prepare for next iteration:
+  echo "loop end"
+  gch.spaceIter = ObjectSpaceIter()
+  result = true
 
-  gch.cycleRootsTrimIdx = gch.cycleRoots.len
+proc markRoot(gch: var GcHeap, c: PCell) =
+  # since we start with 'black' cells, we need to mark them here too:
+  if c.color != rcGrey:
+    c.setColor(rcGrey)
+    add(gch.greyStack, c)
 
-# we now use a much simpler and non-recursive algorithm for cycle removal
-proc collectCycles(gch: var GcHeap) =
-  if gch.cycleRoots.len == 0: return
-  gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, gch.cycleRoots.len)
+proc markIncremental(gch: var GcHeap): bool =
+  var L = addr(gch.greyStack.len)
+  takeStartTime(100)
+  while L[] > 0:
+    var c = gch.greyStack.d[0]
+    sysAssert(isAllocatedPtr(gch.region, c), "markIncremental: isAllocatedPtr")
+    gch.greyStack.d[0] = gch.greyStack.d[L[] - 1]
+    dec(L[])
+    takeTime()
+    if c.color == rcGrey:
+      c.setColor(gch.black)
+      forAllChildren(c, waMarkGrey)
+    checkTime()
+  gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty "
+  result = true
 
-  when CollectCyclesStats:
-    let l0 = gch.cycleRoots.len
-    let tStart = getTicks()
+proc markGlobals(gch: var GcHeap) =
+  for i in 0 .. < globalMarkersLen: globalMarkers[i]()
 
+proc markLocals(gch: var GcHeap) =
+  var d = gch.decStack.d
+  for i in 0 .. < gch.decStack.len:
+    sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals"
+    markRoot(gch, d[i])
+
+when logGC:
   var
-    decrefs = 0
-    increfs = 0
-    collected = 0
-    maybedeads = 0
-
-  template ignoreObject(c: PCell): expr =
-    # This controls which objects will be ignored in the mark and scan stages
-    (when MarkingSkipsAcyclicObjects: not canbeCycleRoot(c) else: false)
-    # not canbeCycleRoot(c)
-    # false
-    # c.isBitUp(rcHasStackRef)
-
-  template earlyMarkAliveRec(cell) =
-    let startLen = gch.tempStack.len
-    cell.setColor rcAlive
-    cell.forAllChildren waPush
-
-    while startLen != gch.tempStack.len:
-      dec gch.tempStack.len
-      var c = gch.tempStack.d[gch.tempStack.len]
-      if c.color != rcAlive:
-        c.setColor rcAlive
-        c.forAllChildren waPush
-
-  template earlyMarkAlive(stackRoots) =
-    # This marks all objects reachable from the stack as alive before any
-    # of the other stages is executed. Such objects cannot be garbage and
-    # they don't need to participate in the recursive decref/incref.
-    for i in 0 .. <stackRoots.len:
-      var c = stackRoots.d[i]
-      # c.setBit rcHasStackRef
-      earlyMarkAliveRec(c)
-
-  earlyMarkAlive(gch.decStack)
-
-  when CollectCyclesStats:
-    let tAfterEarlyMarkAlive = getTicks()
-
-  template recursiveDecRef(cell) =
-    let startLen = gch.tempStack.len
-    cell.setColor rcDecRefApplied
-    cell.forAllChildren waPush
-
-    while startLen != gch.tempStack.len:
-      dec gch.tempStack.len
-      var c = gch.tempStack.d[gch.tempStack.len]
-      if ignoreObject(c): continue
-
-      sysAssert(c.refcount >=% rcIncrement, "recursive dec ref")
-      dec c.refcount, rcIncrement
-      inc decrefs
-      if c.color != rcDecRefApplied:
-        c.setColor rcDecRefApplied
-        c.forAllChildren waPush
-
-  template markRoots(roots) =
-    var i = 0
-    while i < roots.len:
-      if roots.d[i].color == rcCycleCandidate:
-        recursiveDecRef(roots.d[i])
-        inc i
-      else:
-        roots.trimAt i
-
-  markRoots(gch.cycleRoots)
-
-  when CollectCyclesStats:
-    let tAfterMark = getTicks()
-    c_printf "COLLECT CYCLES %d: %d/%d\n", gcCollectionIdx, gch.cycleRoots.len, l0
-
-  template recursiveMarkAlive(cell) =
-    let startLen = gch.tempStack.len
-    cell.setColor rcAlive
-    cell.forAllChildren waPush
-
-    while startLen != gch.tempStack.len:
-      dec gch.tempStack.len
-      var c = gch.tempStack.d[gch.tempStack.len]
-      if ignoreObject(c): continue
-      inc c.refcount, rcIncrement
-      inc increfs
-
-      if c.color != rcAlive:
-        c.setColor rcAlive
-        c.forAllChildren waPush
-
-  template scanRoots(roots) =
-    for i in 0 .. <roots.len:
-      let startLen = gch.tempStack.len
-      gch.tempStack.add roots.d[i]
-
-      while startLen != gch.tempStack.len:
-        dec gch.tempStack.len
-        var c = gch.tempStack.d[gch.tempStack.len]
-        if ignoreObject(c): continue
-        if c.color == rcDecRefApplied:
-          if c.refcount >=% rcIncrement:
-            recursiveMarkAlive(c)
-          else:
-            # note that this is not necessarily the ultimate
-            # destiny of the object. we may still mark it alive
-            # later if we encounter another node from where it's
-            # reachable.
-            c.setColor rcMaybeDead
-            inc maybedeads
-            c.forAllChildren waPush
-
-  scanRoots(gch.cycleRoots)
-
-  when CollectCyclesStats:
-    let tAfterScan = getTicks()
-
-  template collectDead(roots) =
-    for i in 0 .. <roots.len:
-      var c = roots.d[i]
-      c.clearBit(rcInCycleRoots)
-
-      let startLen = gch.tempStack.len
-      gch.tempStack.add c
-
-      while startLen != gch.tempStack.len:
-        dec gch.tempStack.len
-        var c = gch.tempStack.d[gch.tempStack.len]
-        when MarkingSkipsAcyclicObjects:
-          if not canbeCycleRoot(c):
-            # This is an acyclic object reachable from a dead cyclic object
-            # We must do a normal decref here that may add the acyclic object
-            # to the ZCT
-            doDecRef(c, LocalHeap, Cyclic)
-            continue
-        if c.color == rcMaybeDead and not c.isBitUp(rcInCycleRoots):
-          c.setColor(rcReallyDead)
-          inc collected
-          c.forAllChildren waPush
-          # we need to postpone the actual deallocation in order to allow
-          # the finalizers to run while the data structures are still intact
-          gch.freeStack.add c
-          prepareDealloc(c)
-
-    for i in 0 .. <gch.freeStack.len:
-      freeCell(gch, gch.freeStack.d[i])
-
-  collectDead(gch.cycleRoots)
-
-  when CollectCyclesStats:
-    let tFinal = getTicks()
-    cprintf "times:\n  early mark alive: %d ms\n  mark: %d ms\n  scan: %d ms\n  collect: %d ms\n  decrefs: %d\n  increfs: %d\n  marked dead: %d\n  collected: %d\n",
-      (tAfterEarlyMarkAlive - tStart)  div 1_000_000,
-      (tAfterMark - tAfterEarlyMarkAlive) div 1_000_000,
-      (tAfterScan - tAfterMark) div 1_000_000,
-      (tFinal - tAfterScan) div 1_000_000,
-      decrefs,
-      increfs,
-      maybedeads,
-      collected
-
-  deinit(gch.cycleRoots)
-  init(gch.cycleRoots)
-
-  deinit(gch.freeStack)
-  init(gch.freeStack)
-
-  when MarkingSkipsAcyclicObjects:
-    # Collect the acyclic objects that became unreachable due to collected
-    # cyclic objects.
-    discard collectZCT(gch)
-    # collectZCT may add new cycle candidates and we may decide to loop here
-    # if gch.cycleRoots.len > 0: repeat
-
-var gcDebugging* = false
-
-var seqdbg* : proc (s: PGenericSeq) {.cdecl.}
+    cycleCheckA: array[100, PCell]
+    cycleCheckALen = 0
+
+  proc alreadySeen(c: PCell): bool =
+    for i in 0 .. <cycleCheckALen:
+      if cycleCheckA[i] == c: return true
+    if cycleCheckALen == len(cycleCheckA):
+      gcAssert(false, "cycle detection overflow")
+      quit 1
+    cycleCheckA[cycleCheckALen] = c
+    inc cycleCheckALen
+
+  proc debugGraph(s: PCell) =
+    if alreadySeen(s):
+      writeCell("child cell (already seen) ", s)
+    else:
+      writeCell("cell {", s)
+      forAllChildren(s, waDebug)
+      c_fprintf(c_stdout, "}\n")
+
+proc doOperation(p: pointer, op: WalkOp) =
+  if p == nil: return
+  var c: PCell = usrToCell(p)
+  gcAssert(c != nil, "doOperation: 1")
+  # the 'case' should be faster than function pointers because of easy
+  # prediction:
+  case op
+  of waZctDecRef:
+    #if not isAllocatedPtr(gch.region, c):
+    #  c_fprintf(c_stdout, "[GC] decref bug: %p", c)
+    gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
+    gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
+    #c.refcount = c.refcount -% rcIncrement
+    when logGC: writeCell("decref (from doOperation)", c)
+    decRef(c)
+    #if c.refcount <% rcIncrement: addZCT(gch.zct, c)
+  of waMarkGlobal:
+    when hasThreadSupport:
+      # could point to a cell which we don't own and don't want to touch/trace
+      if isAllocatedPtr(gch.region, c):
+        markRoot(gch, c)
+    else:
+      markRoot(gch, c)
+  of waMarkGrey:
+    if c.color == 1-gch.black:
+      c.setColor(rcGrey)
+      add(gch.greyStack, c)
+  #of waDebug: debugGraph(c)
+
+proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
+  doOperation(d, WalkOp(op))
+
+proc collectZCT(gch: var GcHeap): bool {.benign.}
+
+proc collectCycles(gch: var GcHeap): bool =
+  # ensure the ZCT 'color' is not used:
+  while gch.zct.len > 0: discard collectZCT(gch)
+  case gch.phase
+  of Phase.None, Phase.Marking:
+    #if gch.phase == Phase.None:
+    gch.phase = Phase.Marking
+    markGlobals(gch)
+    markLocals(gch)
+    if markIncremental(gch):
+      gch.phase = Phase.Sweeping
+  of Phase.Sweeping:
+    gcAssert gch.greyStack.len == 0, "greystack not empty"
+    if sweep(gch):
+      gch.phase = Phase.None
+      # flip black/white meanings:
+      gch.black = 1 - gch.black
+      result = true
 
 proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   # the addresses are not as cells on the stack, so turn them to cells:
@@ -1005,235 +739,33 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
     var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
     if objStart != nil:
       # mark the cell:
-      if objStart.color != rcReallyDead:
-        if gcDebugging:
-          # writeCell("marking ", objStart)
-          discard
-        else:
-          inc objStart.refcount, rcIncrement
-          gch.decStack.add objStart
-      else:
-        # With incremental clean-up, objects spend some time
-        # in various lists before being deallocated.
-        # We just found a reference on the stack to an object,
-        # which we have previously labeled as unreachable.
-        # This is either a bug in the GC or a pure accidental
-        # coincidence due to the conservative stack marking.
-        when debugGC:
-          # writeCell("marking dead object", objStart)
-          discard
-    when false:
-      if isAllocatedPtr(gch.region, cell):
-        sysAssert false, "allocated pointer but not interior?"
-        # mark the cell:
-        inc cell.refcount, rcIncrement
-        add(gch.decStack, cell)
+      objStart.refcount = objStart.refcount +% rcIncrement
+      add(gch.decStack, objStart)
   sysAssert(allocInv(gch.region), "gcMark end")
 
-proc markThreadStacks(gch: var GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    {.error: "not fully implemented".}
-    var it = threadList
-    while it != nil:
-      # mark registers:
-      for i in 0 .. high(it.registers): gcMark(gch, it.registers[i])
-      var sp = cast[ByteAddress](it.stackBottom)
-      var max = cast[ByteAddress](it.stackTop)
-      # XXX stack direction?
-      # XXX unroll this loop:
-      while sp <=% max:
-        gcMark(gch, cast[PPointer](sp)[])
-        sp = sp +% sizeof(pointer)
-      it = it.next
-
-# ----------------- stack management --------------------------------------
-#  inspired from Smart Eiffel
-
-when defined(sparc):
-  const stackIncreases = false
-elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
-     defined(hp9000s700) or defined(hp9000s800) or defined(hp9000s820):
-  const stackIncreases = true
-else:
-  const stackIncreases = false
-
-when not defined(useNimRtl):
-  {.push stack_trace: off.}
-  proc setStackBottom(theStackBottom: pointer) =
-    #c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
-    # the first init must be the one that defines the stack bottom:
-    if gch.stackBottom == nil: gch.stackBottom = theStackBottom
-    else:
-      var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2
-      var b = cast[ByteAddress](gch.stackBottom)
-      #c_fprintf(c_stdout, "old: %p new: %p;\n",gch.stackBottom,theStackBottom)
-      when stackIncreases:
-        gch.stackBottom = cast[pointer](min(a, b))
-      else:
-        gch.stackBottom = cast[pointer](max(a, b))
-  {.pop.}
-
-proc stackSize(): int {.noinline.} =
-  var stackTop {.volatile.}: pointer
-  result = abs(cast[int](addr(stackTop)) - cast[int](gch.stackBottom))
+include gc_common
 
-var
-  jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
-    # a little hack to get the size of a JmpBuf in the generated C code
-    # in a platform independent way
-
-when defined(sparc): # For SPARC architecture.
-  proc isOnStack(p: pointer): bool =
-    var stackTop {.volatile.}: pointer
-    stackTop = addr(stackTop)
-    var b = cast[ByteAddress](gch.stackBottom)
-    var a = cast[ByteAddress](stackTop)
-    var x = cast[ByteAddress](p)
-    result = a <=% x and x <=% b
-
-  proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
-    when defined(sparcv9):
-      asm  """"flushw \n" """
-    else:
-      asm  """"ta      0x3   ! ST_FLUSH_WINDOWS\n" """
-
-    var
-      max = gch.stackBottom
-      sp: PPointer
-      stackTop: array[0..1, pointer]
-    sp = addr(stackTop[0])
-    # Addresses decrease as the stack grows.
-    while sp <= max:
-      gcMark(gch, sp[])
-      sp = cast[PPointer](cast[ByteAddress](sp) +% sizeof(pointer))
-
-elif defined(ELATE):
-  {.error: "stack marking code is to be written for this architecture".}
-
-elif stackIncreases:
-  # ---------------------------------------------------------------------------
-  # Generic code for architectures where addresses increase as the stack grows.
-  # ---------------------------------------------------------------------------
-  proc isOnStack(p: pointer): bool =
-    var stackTop {.volatile.}: pointer
-    stackTop = addr(stackTop)
-    var a = cast[ByteAddress](gch.stackBottom)
-    var b = cast[ByteAddress](stackTop)
-    var x = cast[ByteAddress](p)
-    result = a <=% x and x <=% b
-
-  proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
-    var registers: C_JmpBuf
-    if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
-      var max = cast[ByteAddress](gch.stackBottom)
-      var sp = cast[ByteAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer)
-      # sp will traverse the JMP_BUF as well (jmp_buf size is added,
-      # otherwise sp would be below the registers structure).
-      while sp >=% max:
-        gcMark(gch, cast[PPointer](sp)[])
-        sp = sp -% sizeof(pointer)
-
-else:
-  # ---------------------------------------------------------------------------
-  # Generic code for architectures where addresses decrease as the stack grows.
-  # ---------------------------------------------------------------------------
-  proc isOnStack(p: pointer): bool =
-    var stackTop {.volatile.}: pointer
-    stackTop = addr(stackTop)
-    var b = cast[ByteAddress](gch.stackBottom)
-    var a = cast[ByteAddress](stackTop)
-    var x = cast[ByteAddress](p)
-    result = a <=% x and x <=% b
-
-  proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
-    # We use a jmp_buf buffer that is in the C stack.
-    # Used to traverse the stack and registers assuming
-    # that 'setjmp' will save registers in the C stack.
-    type PStackSlice = ptr array [0..7, pointer]
-    var registers: C_JmpBuf
-    if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
-      when MinimumStackMarking:
-        # mark the registers
-        var jmpbufPtr = cast[ByteAddress](addr(registers))
-        var jmpbufEnd = jmpbufPtr +% jmpbufSize
-
-        while jmpbufPtr <=% jmpbufEnd:
-          gcMark(gch, cast[PPointer](jmpbufPtr)[])
-          jmpbufPtr = jmpbufPtr +% sizeof(pointer)
-
-        var sp = cast[ByteAddress](gch.stackTop)
-      else:
-        var sp = cast[ByteAddress](addr(registers))
-      # mark the user stack
-      var max = cast[ByteAddress](gch.stackBottom)
-      # loop unrolled:
-      while sp <% max - 8*sizeof(pointer):
-        gcMark(gch, cast[PStackSlice](sp)[0])
-        gcMark(gch, cast[PStackSlice](sp)[1])
-        gcMark(gch, cast[PStackSlice](sp)[2])
-        gcMark(gch, cast[PStackSlice](sp)[3])
-        gcMark(gch, cast[PStackSlice](sp)[4])
-        gcMark(gch, cast[PStackSlice](sp)[5])
-        gcMark(gch, cast[PStackSlice](sp)[6])
-        gcMark(gch, cast[PStackSlice](sp)[7])
-        sp = sp +% sizeof(pointer)*8
-      # last few entries:
-      while sp <=% max:
-        gcMark(gch, cast[PPointer](sp)[])
-        sp = sp +% sizeof(pointer)
-
-# ----------------------------------------------------------------------------
-# end of non-portable code
-# ----------------------------------------------------------------------------
-
-proc releaseCell(gch: var GcHeap, cell: PCell) =
-  if cell.color != rcReallyDead:
-    prepareDealloc(cell)
-    cell.setColor rcReallyDead
-
-    let l1 = gch.tempStack.len
-    cell.forAllChildren waPush
-    let l2 = gch.tempStack.len
-    for i in l1 .. <l2:
-      var cc = gch.tempStack.d[i]
-      if cc.refcount--(LocalHeap):
-        releaseCell(gch, cc)
-      else:
-        if canbeCycleRoot(cc):
-          addCycleRoot(gch.cycleRoots, cc)
-
-    gch.tempStack.len = l1
-
-  if cell.isBitDown(rcBufferedAnywhere):
-    freeCell(gch, cell)
-  # else:
-  # This object is either buffered in the cycleRoots list and we'll leave
-  # it there to be collected in the next collectCycles or it's pending in
-  # the ZCT:
-  # (e.g. we are now cleaning the 15th object, but this one is 18th in the
-  #  list. Note that this can happen only if we reached this point by the
-  #  recursion).
-  # We can ignore it now as the ZCT cleaner will reach it soon.
+proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
+  forEachStackSlot(gch, gcMark)
 
 proc collectZCT(gch: var GcHeap): bool =
-  const workPackage = 100
+  # Note: Freeing may add child objects to the ZCT! So essentially we do
+  # deep freeing, which is bad for incremental operation. In order to
+  # avoid a deep stack, we move objects to keep the ZCT small.
+  # This is performance critical!
   var L = addr(gch.zct.len)
-
-  when withRealtime:
-    var steps = workPackage
-    var t0: Ticks
-    if gch.maxPause > 0: t0 = getticks()
+  takeStartTime(100)
 
   while L[] > 0:
     var c = gch.zct.d[0]
-    sysAssert c.isBitUp(rcZct), "collectZCT: rcZct missing!"
-    sysAssert(isAllocatedPtr(gch.region, c), "collectZCT: isAllocatedPtr")
-
+    sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
     # remove from ZCT:
-    c.clearBit(rcZct)
+    gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
+
+    c.refcount = c.refcount and not ZctFlag
     gch.zct.d[0] = gch.zct.d[L[] - 1]
     dec(L[])
-    when withRealtime: dec steps
+    takeTime()
     if c.refcount <% rcIncrement:
       # It may have a RC > 0, if it is in the hardware stack or
       # it has not been removed yet from the ZCT. This is because
@@ -1241,92 +773,78 @@ 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!**
-      if c.color == rcRetiredBuffer:
-        if c.isBitDown(rcInCycleRoots):
-          freeCell(gch, c)
+      when logGC: writeCell("zct dealloc cell", c)
+      gcTrace(c, csZctFreed)
+      # We are about to free the object, call the finalizer BEFORE its
+      # children are deleted as well, because otherwise the finalizer may
+      # access invalid memory. This is done by prepareDealloc():
+      prepareDealloc(c)
+      forAllChildren(c, waZctDecRef)
+      when reallyDealloc:
+        sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
+        rawDealloc(gch.region, c)
       else:
-        # if c.color == rcReallyDead: writeCell("ReallyDead in ZCT?", c)
-        releaseCell(gch, c)
-    when withRealtime:
-      if steps == 0:
-        steps = workPackage
-        if gch.maxPause > 0:
-          let duration = getticks() - t0
-          # the GC's measuring is not accurate and needs some cleanup actions
-          # (stack unmarking), so subtract some short amount of time in to
-          # order to miss deadlines less often:
-          if duration >= gch.maxPause - 50_000:
-            return false
+        sysAssert(c.typ != nil, "collectZCT 2")
+        zeroMem(c, sizeof(Cell))
+    checkTime()
   result = true
-  gch.trimCycleRoots
-  #deInit(gch.zct)
-  #init(gch.zct)
 
 proc unmarkStackAndRegisters(gch: var GcHeap) =
   var d = gch.decStack.d
-  for i in 0 .. <gch.decStack.len:
+  for i in 0..gch.decStack.len-1:
     sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
-    # XXX: just call doDecRef?
-    var c = d[i]
-    sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
-
-    if c.color == rcRetiredBuffer:
-      continue
-
-    # XXX no need for an atomic dec here:
-    if c.refcount--(LocalHeap):
-      # the object survived only because of a stack reference
-      # it still doesn't have heap references
-      addZCT(gch.zct, c)
-
-    if canbeCycleRoot(c):
-      # any cyclic object reachable from the stack can be turned into
-      # a leak if it's orphaned through the stack reference
-      # that's because the write-barrier won't be executed for stack
-      # locations
-      addCycleRoot(gch.cycleRoots, c)
-
+    decRef(d[i])
   gch.decStack.len = 0
 
 proc collectCTBody(gch: var GcHeap) =
-  when withRealtime:
+  when withRealTime:
     let t0 = getticks()
-  when debugGC: inc gcCollectionIdx
   sysAssert(allocInv(gch.region), "collectCT: begin")
 
-  gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
+  when not defined(nimCoroutines):
+    gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
   sysAssert(gch.decStack.len == 0, "collectCT")
   prepareForInteriorPointerChecking(gch.region)
   markStackAndRegisters(gch)
-  markThreadStacks(gch)
   gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
   inc(gch.stat.stackScans)
   if collectZCT(gch):
     when cycleGC:
       if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC:
-        collectCycles(gch)
-        sysAssert gch.zct.len == 0, "zct is not null after collect cycles"
-        inc(gch.stat.cycleCollections)
-        gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
-                                 CycleIncrease)
-        gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
+        if collectCycles(gch):
+          inc(gch.stat.cycleCollections)
+          gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
+                                   CycleIncrease)
+          gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
   unmarkStackAndRegisters(gch)
   sysAssert(allocInv(gch.region), "collectCT: end")
 
-  when withRealtime:
+  when withRealTime:
     let duration = getticks() - t0
     gch.stat.maxPause = max(gch.stat.maxPause, duration)
     when defined(reportMissedDeadlines):
       if gch.maxPause > 0 and duration > gch.maxPause:
         c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration)
 
+when defined(nimCoroutines):
+  proc currentStackSizes(): int =
+    for stack in items(gch.stack):
+      result = result + stackSize(stack.starts, stack.pos)
+
 proc collectCT(gch: var GcHeap) =
-  if (gch.zct.len >= ZctThreshold or (cycleGC and
+  # stackMarkCosts prevents some pathological behaviour: Stack marking
+  # becomes more expensive with large stacks and large stacks mean that
+  # cells with RC=0 are more likely to be kept alive by the stack.
+  when defined(nimCoroutines):
+    let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold)
+  else:
+    let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
+  if (gch.zct.len >= stackMarkCosts or (cycleGC and
       getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
       gch.recGcLock == 0:
     collectCTBody(gch)
 
-when withRealtime:
+when withRealTime:
   proc toNano(x: int): Nanos {.inline.} =
     result = x * 1000
 
@@ -1334,13 +852,11 @@ when withRealtime:
     gch.maxPause = MaxPauseInUs.toNano
 
   proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
-    acquire(gch)
     gch.maxPause = us.toNano
     if (gch.zct.len >= ZctThreshold or (cycleGC and
         getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
         strongAdvice:
       collectCTBody(gch)
-    release(gch)
 
   proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice)
 
@@ -1358,11 +874,7 @@ when not defined(useNimRtl):
         dec(gch.recGcLock)
 
   proc GC_setStrategy(strategy: GC_Strategy) =
-    case strategy
-    of gcThroughput: discard
-    of gcResponsiveness: discard
-    of gcOptimizeSpace: discard
-    of gcOptimizeTime: discard
+    discard
 
   proc GC_enableMarkAndSweep() =
     gch.cycleThreshold = InitialCycleThreshold
@@ -1372,13 +884,10 @@ when not defined(useNimRtl):
     # set to the max value to suppress the cycle detector
 
   proc GC_fullCollect() =
-    setStackTop(gch)
-    acquire(gch)
     var oldThreshold = gch.cycleThreshold
     gch.cycleThreshold = 0 # forces cycle collection
     collectCT(gch)
     gch.cycleThreshold = oldThreshold
-    release(gch)
 
   proc GC_getStatistics(): string =
     GC_disable()
@@ -1390,9 +899,13 @@ when not defined(useNimRtl):
              "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
              "[GC] zct capacity: " & $gch.zct.cap & "\n" &
              "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
-             "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" &
              "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000)
-    when traceGC: writeLeakage(true)
+    when defined(nimCoroutines):
+      result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
+      for stack in items(gch.stack):
+        result = result & "[GC]   stack " & stack.starts.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
+    else:
+      result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
     GC_enable()
 
 {.pop.}
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 5bcddc5e6..54c6796c9 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -33,6 +33,9 @@ type
     lineNumber {.importc.}: int
     message {.importc.}: cstring
     stack {.importc.}: cstring
+
+  JSRef = ref RootObj # Fake type.
+
 {.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].}
 
 var
@@ -282,61 +285,6 @@ proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
     return true;
   """
 
-type
-  Document {.importc.} = object of RootObj
-    write: proc (text: cstring) {.nimcall.}
-    writeln: proc (text: cstring) {.nimcall.}
-    createAttribute: proc (identifier: cstring): ref Node {.nimcall.}
-    createElement: proc (identifier: cstring): ref Node {.nimcall.}
-    createTextNode: proc (identifier: cstring): ref Node {.nimcall.}
-    getElementById: proc (id: cstring): ref Node {.nimcall.}
-    getElementsByName: proc (name: cstring): seq[ref Node] {.nimcall.}
-    getElementsByTagName: proc (name: cstring): seq[ref Node] {.nimcall.}
-
-  NodeType* = enum
-    ElementNode = 1,
-    AttributeNode,
-    TextNode,
-    CDATANode,
-    EntityRefNode,
-    EntityNode,
-    ProcessingInstructionNode,
-    CommentNode,
-    DocumentNode,
-    DocumentTypeNode,
-    DocumentFragmentNode,
-    NotationNode
-  Node* {.importc.} = object of RootObj
-    attributes*: seq[ref Node]
-    childNodes*: seq[ref Node]
-    data*: cstring
-    firstChild*: ref Node
-    lastChild*: ref Node
-    nextSibling*: ref Node
-    nodeName*: cstring
-    nodeType*: NodeType
-    nodeValue*: cstring
-    parentNode*: ref Node
-    previousSibling*: ref Node
-    appendChild*: proc (child: ref Node) {.nimcall.}
-    appendData*: proc (data: cstring) {.nimcall.}
-    cloneNode*: proc (copyContent: bool) {.nimcall.}
-    deleteData*: proc (start, len: int) {.nimcall.}
-    getAttribute*: proc (attr: cstring): cstring {.nimcall.}
-    getAttributeNode*: proc (attr: cstring): ref Node {.nimcall.}
-    getElementsByTagName*: proc (): seq[ref Node] {.nimcall.}
-    hasChildNodes*: proc (): bool {.nimcall.}
-    insertBefore*: proc (newNode, before: ref Node) {.nimcall.}
-    insertData*: proc (position: int, data: cstring) {.nimcall.}
-    removeAttribute*: proc (attr: cstring) {.nimcall.}
-    removeAttributeNode*: proc (attr: ref Node) {.nimcall.}
-    removeChild*: proc (child: ref Node) {.nimcall.}
-    replaceChild*: proc (newNode, oldNode: ref Node) {.nimcall.}
-    replaceData*: proc (start, len: int, text: cstring) {.nimcall.}
-    setAttribute*: proc (name, value: cstring) {.nimcall.}
-    setAttributeNode*: proc (attr: ref Node) {.nimcall.}
-{.deprecated: [TNode: Node, TNodeType: NodeType, TDocument: Document].}
-
 when defined(kwin):
   proc rawEcho {.compilerproc, asmNoStackFrame.} =
     asm """
@@ -360,28 +308,28 @@ elif defined(nodejs):
     """
 
 else:
-  var
-    document {.importc, nodecl.}: ref Document
-
   proc ewriteln(x: cstring) =
-    var node = document.getElementsByTagName("body")[0]
-    if node != nil:
-      node.appendChild(document.createTextNode(x))
-      node.appendChild(document.createElement("br"))
-    else:
+    var node : JSRef
+    {.emit: "`node` = document.getElementsByTagName('body')[0];".}
+    if node.isNil:
       raise newException(ValueError, "<body> element does not exist yet!")
+    {.emit: """
+    `node`.appendChild(document.createTextNode(`x`));
+    `node`.appendChild(document.createElement("br"));
+    """.}
 
   proc rawEcho {.compilerproc.} =
-    var node = document.getElementsByTagName("body")[0]
-    if node == nil:
+    var node : JSRef
+    {.emit: "`node` = document.getElementsByTagName('body')[0];".}
+    if node.isNil:
       raise newException(IOError, "<body> element does not exist yet!")
-    asm """
-      for (var i = 0; i < arguments.length; ++i) {
-        var x = `toJSStr`(arguments[i]);
-        `node`.appendChild(document.createTextNode(x))
-      }
-    """
-    node.appendChild(document.createElement("br"))
+    {.emit: """
+    for (var i = 0; i < arguments.length; ++i) {
+      var x = `toJSStr`(arguments[i]);
+      `node`.appendChild(document.createTextNode(x));
+    }
+    `node`.appendChild(document.createElement("br"));
+    """.}
 
 # Arithmetic:
 proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
@@ -532,8 +480,6 @@ proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
 type NimString = string # hack for hti.nim
 include "system/hti"
 
-type JSRef = ref RootObj # Fake type.
-
 proc isFatPointer(ti: PNimType): bool =
   # This has to be consistent with the code generator!
   return ti.base.kind notin {tyObject,
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 326c601bd..e2137e8f4 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -229,7 +229,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
     # we need to decref here, otherwise the GC leaks!
     when not defined(boehmGC) and not defined(nogc) and
          not defined(gcMarkAndSweep) and not defined(gogc):
-      when compileOption("gc", "v2"):
+      when false: # compileOption("gc", "v2"):
         for i in newLen..result.len-1:
           let len0 = gch.tempStack.len
           forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
diff --git a/tests/concepts/tmanual.nim b/tests/concepts/tmanual.nim
index 7cf08af06..43290a6ad 100644
--- a/tests/concepts/tmanual.nim
+++ b/tests/concepts/tmanual.nim
@@ -23,7 +23,7 @@ template reject(e: expr) =
 type
   Container[T] = concept c
     c.len is Ordinal
-    items(c) is iterator
+    items(c) is T
     for value in c:
       type(value) is T
 
diff --git a/tests/iter/titerable.nim b/tests/iter/titerable.nim
index 3ec79f68d..fc1a8f934 100644
--- a/tests/iter/titerable.nim
+++ b/tests/iter/titerable.nim
@@ -6,8 +6,11 @@ discard """
 8
 12
 '''
+  disabled: "true"
 """
 
+# Will eventually fix it...
+
 iterator map[T, U](s: iterator:T{.inline.}, f: proc(x: T): U): U =
   for e in s: yield f(e)
 
diff --git a/tests/metatype/ttypeclasses.nim b/tests/metatype/ttypeclasses.nim
index db9db7713..720527088 100644
--- a/tests/metatype/ttypeclasses.nim
+++ b/tests/metatype/ttypeclasses.nim
@@ -17,12 +17,12 @@ type
   TFoo[T] = object
     val: T
 
-  T1 = expr
-  T2 = expr
+  T1 = auto
+  T2 = auto
 
   Numeric = int|float
 
-proc takesExpr(x, y) =
+proc takesExpr(x, y: auto) =
   echo x, y
 
 proc same(x, y: T1) =
@@ -31,7 +31,7 @@ proc same(x, y: T1) =
 proc takesFoo(x, y: TFoo) =
   echo x.val, y.val
 
-proc takes2Types(x,y: T1, z: T2) =
+proc takes2Types[T1, T2](x,y: T1, z: T2) =
   echo x, y, z
 
 takesExpr(1, 2)
diff --git a/tests/misc/tfsmonitor.nim b/tests/misc/tfsmonitor.nim
new file mode 100644
index 000000000..27e1a2e32
--- /dev/null
+++ b/tests/misc/tfsmonitor.nim
@@ -0,0 +1,12 @@
+#
+# fsmonitor test
+#
+
+import unittest
+import fsmonitor
+
+suite "fsmonitor":
+  test "should not raise OSError, bug# 3611":
+    let m = newMonitor()
+    m.add("foo", {MonitorCloseWrite, MonitorCloseNoWrite})
+
diff --git a/tests/stdlib/ttime.nim b/tests/stdlib/ttime.nim
index 859f0abdd..efc371995 100644
--- a/tests/stdlib/ttime.nim
+++ b/tests/stdlib/ttime.nim
@@ -1,6 +1,125 @@
 # test the new time module
+discard """
+  file: "ttime.nim"
+"""
 
 import
-  times
+  times, strutils
 
-write(stdout, $getTime())
+assert( $getTime() == getLocalTime(getTime()).format("ddd MMM dd HH:mm:ss yyyy"))
+# $ date --date='@2147483647'
+# Tue 19 Jan 03:14:07 GMT 2038
+
+var t = getGMTime(fromSeconds(2147483647))
+assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
+assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
+
+assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
+  " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
+  "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
+
+assert t.format("yyyyMMddhhmmss") == "20380119031407"
+
+var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
+assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
+  " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
+  "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
+
+when not defined(JS):
+  when sizeof(Time) == 8:
+    var t3 = getGMTime(fromSeconds(889067643645)) # Fri  7 Jun 19:20:45 BST 30143
+    assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
+      " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
+      "7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 0 00 00:00 UTC"
+    assert t3.format(":,[]()-/") == ":,[]()-/"
+
+var t4 = getGMTime(fromSeconds(876124714)) # Mon  6 Oct 08:58:34 BST 1997
+assert t4.format("M MM MMM MMMM") == "10 10 Oct October"
+
+# Interval tests
+assert((t4 - initInterval(years = 2)).format("yyyy") == "1995")
+assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10")
+
+var s = "Tuesday at 09:04am on Dec 15, 2015"
+var f = "dddd at hh:mmtt on MMM d, yyyy"
+assert($s.parse(f) == "Tue Dec 15 09:04:00 2015")
+# ANSIC       = "Mon Jan _2 15:04:05 2006"
+s = "Thu Jan 12 15:04:05 2006"
+f = "ddd MMM dd HH:mm:ss yyyy"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+# UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
+s = "Thu Jan 12 15:04:05 MST 2006"
+f = "ddd MMM dd HH:mm:ss ZZZ yyyy"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+# RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
+s = "Thu Jan 12 15:04:05 -07:00 2006"
+f = "ddd MMM dd HH:mm:ss zzz yyyy"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+# RFC822      = "02 Jan 06 15:04 MST"
+s = "12 Jan 16 15:04 MST"
+f = "dd MMM yy HH:mm ZZZ"
+assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
+# RFC822Z     = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
+s = "12 Jan 16 15:04 -07:00"
+f = "dd MMM yy HH:mm zzz"
+assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
+# RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
+s = "Monday, 12-Jan-06 15:04:05 MST"
+f = "dddd, dd-MMM-yy HH:mm:ss ZZZ"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+# RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
+s = "Thu, 12 Jan 2006 15:04:05 MST"
+f = "ddd, dd MMM yyyy HH:mm:ss ZZZ"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+# RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
+s = "Thu, 12 Jan 2006 15:04:05 -07:00"
+f = "ddd, dd MMM yyyy HH:mm:ss zzz"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+# RFC3339     = "2006-01-02T15:04:05Z07:00"
+s = "2006-01-12T15:04:05Z-07:00"
+f = "yyyy-MM-ddTHH:mm:ssZzzz"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+f = "yyyy-MM-dd'T'HH:mm:ss'Z'zzz"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
+s = "2006-01-12T15:04:05.999999999Z-07:00"
+f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
+assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+# Kitchen     = "3:04PM"
+s = "3:04PM"
+f = "h:mmtt"
+assert "15:04:00" in $s.parse(f)
+#when not defined(testing):
+#  echo "Kitchen: " & $s.parse(f)
+#  var ti = timeToTimeInfo(getTime())
+#  echo "Todays date after decoding: ", ti
+#  var tint = timeToTimeInterval(getTime())
+#  echo "Todays date after decoding to interval: ", tint
+
+# checking dayOfWeek matches known days
+assert getDayOfWeek(21, 9, 1900) == dFri
+assert getDayOfWeek(1, 1, 1970) == dThu
+assert getDayOfWeek(21, 9, 1970) == dMon
+assert getDayOfWeek(1, 1, 2000) == dSat
+assert getDayOfWeek(1, 1, 2021) == dFri
+# Julian tests
+assert getDayOfWeekJulian(21, 9, 1900) == dFri
+assert getDayOfWeekJulian(21, 9, 1970) == dMon
+assert getDayOfWeekJulian(1, 1, 2000) == dSat
+assert getDayOfWeekJulian(1, 1, 2021) == dFri
+
+# toSeconds tests with GM and Local timezones
+#var t4 = getGMTime(fromSeconds(876124714)) # Mon  6 Oct 08:58:34 BST 1997
+var t4L = getLocalTime(fromSeconds(876124714))
+assert toSeconds(timeInfoToTime(t4L)) == 876124714    # fromSeconds is effectively "localTime"
+assert toSeconds(timeInfoToTime(t4L)) + t4L.timezone.float == toSeconds(timeInfoToTime(t4))
+
+# adding intervals
+var
+  a1L = toSeconds(timeInfoToTime(t4L + initInterval(hours = 1))) + t4L.timezone.float
+  a1G = toSeconds(timeInfoToTime(t4)) + 60.0 * 60.0
+assert a1L == a1G
+# subtracting intervals
+a1L = toSeconds(timeInfoToTime(t4L - initInterval(hours = 1))) + t4L.timezone.float
+a1G = toSeconds(timeInfoToTime(t4)) - (60.0 * 60.0)
+assert a1L == a1G
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 762c92792..73d72289c 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -223,6 +223,9 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
                    "varres/tvartup"]:
     test "tests/" & testfile & ".nim"
 
+  for testfile in ["pure/strutils"]:
+    test "lib/" & testfile & ".nim"
+
 # ------------------------- manyloc -------------------------------------------
 #proc runSpecialTests(r: var TResults, options: string) =
 #  for t in ["lib/packages/docutils/highlite"]:
diff --git a/tests/types/tisop.nim b/tests/types/tisop.nim
index d0b7c32b2..ad5928016 100644
--- a/tests/types/tisop.nim
+++ b/tests/types/tisop.nim
@@ -32,7 +32,7 @@ static: assert(f.y.type.name == "string")
 when compiles(f.z):
   {.error: "Foo should not have a `z` field".}
 
-proc p(a, b) =
+proc p(a, b: auto) =
   when a.type is int:
     static: assert false
 
diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim
index b9acfa5fb..14999ebee 100644
--- a/tests/types/tisopr.nim
+++ b/tests/types/tisopr.nim
@@ -23,22 +23,23 @@ template yes(e: expr): stmt =
 template no(e: expr): stmt =
   static: assert(not e)
 
-var s = @[1, 2, 3]
+when false:
+  var s = @[1, 2, 3]
 
-yes s.items is iterator
-no  s.items is proc
+  yes s.items is iterator
+  no  s.items is proc
 
-yes s.items is iterator: int
-no  s.items is iterator: float
+  yes s.items is iterator: int
+  no  s.items is iterator: float
 
-yes s.items is iterator: TNumber
-no  s.items is iterator: object
+  yes s.items is iterator: TNumber
+  no  s.items is iterator: object
 
-type
-  Iter[T] = iterator: T
+  type
+    Iter[T] = iterator: T
 
-yes s.items is Iter[TNumber]
-no  s.items is Iter[float]
+  yes s.items is Iter[TNumber]
+  no  s.items is Iter[float]
 
 type
   Foo[N: static[int], T] = object
diff --git a/web/news.txt b/web/news.txt
index 192ddbb74..67a7782cb 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -14,6 +14,11 @@ News
 
   - ``macros.newLit`` for ``bool`` now produces false/true symbols which
     actually work with the bool datatype.
+  - when compiling to JS, ``Node``, ``NodeType`` and ``Document`` are no longer
+    defined. Use the types defined in ``dom.nim`` instead.
+  - The check ``x is iterator`` (used for instance in concepts) was always a
+    weird special case (you could not use ``x is proc``) and was removed from
+    the language.
 
 
 2015-10-27 Version 0.12.0 released
diff --git a/web/website.ini b/web/website.ini
index 9d7aab664..023262aa6 100644
--- a/web/website.ini
+++ b/web/website.ini
@@ -56,8 +56,8 @@ srcdoc2: "pure/memfiles;pure/subexes;pure/collections/critbits"
 srcdoc2: "deprecated/pure/asyncio;deprecated/pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
 srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
 srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
-srcdoc2: "packages/docutils/rstgen;pure/logging;pure/asyncdispatch;pure/asyncnet"
-srcdoc2: "deprecated/pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future"
+srcdoc2: "packages/docutils/rstgen;pure/logging;pure/options;pure/asyncdispatch;pure/asyncnet"
+srcdoc2: "pure/nativesockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future"
 srcdoc2: "deprecated/pure/ftpclient"
 srcdoc2: "pure/asyncfile;pure/asyncftpclient"
 srcdoc2: "pure/md5;pure/rationals"
@@ -76,5 +76,3 @@ webdoc: "wrappers/libuv;wrappers/joyent_http_parser"
 
 webdoc: "posix/posix;wrappers/odbcsql"
 webdoc: "wrappers/libsvm.nim"
-
-