summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/core/macros.nim77
-rw-r--r--lib/impure/nre.nim12
-rw-r--r--lib/impure/re.nim2
-rw-r--r--lib/js/dom.nim321
-rw-r--r--lib/js/jsre.nim43
-rw-r--r--lib/nimhcr.nim4
-rw-r--r--lib/nimrtl.nim4
-rw-r--r--lib/packages/docutils/highlite.nim2
-rw-r--r--lib/packages/docutils/rst.nim4
-rw-r--r--lib/pure/asyncdispatch.nim30
-rw-r--r--lib/pure/asyncftpclient.nim4
-rw-r--r--lib/pure/asyncmacro.nim6
-rw-r--r--lib/pure/asyncnet.nim69
-rw-r--r--lib/pure/asyncstreams.nim2
-rw-r--r--lib/pure/cgi.nim44
-rw-r--r--lib/pure/collections/deques.nim11
-rw-r--r--lib/pure/collections/hashcommon.nim14
-rw-r--r--lib/pure/collections/sequtils.nim3
-rw-r--r--lib/pure/collections/setimpl.nim4
-rw-r--r--lib/pure/collections/sets.nim30
-rw-r--r--lib/pure/collections/sharedlist.nim6
-rw-r--r--lib/pure/collections/sharedtables.nim18
-rw-r--r--lib/pure/collections/tableimpl.nim6
-rw-r--r--lib/pure/collections/tables.nim52
-rw-r--r--lib/pure/colors.nim3
-rw-r--r--lib/pure/concurrency/threadpool.nim10
-rw-r--r--lib/pure/includes/decode_helpers.nim24
-rw-r--r--lib/pure/logging.nim13
-rw-r--r--lib/pure/nativesockets.nim38
-rw-r--r--lib/pure/net.nim6
-rw-r--r--lib/pure/os.nim45
-rw-r--r--lib/pure/osproc.nim92
-rw-r--r--lib/pure/parseopt.nim4
-rw-r--r--lib/pure/ssl_certs.nim4
-rw-r--r--lib/pure/streamwrapper.nim117
-rw-r--r--lib/pure/strformat.nim61
-rw-r--r--lib/pure/strutils.nim5
-rw-r--r--lib/pure/terminal.nim2
-rw-r--r--lib/pure/typetraits.nim75
-rw-r--r--lib/pure/unittest.nim15
-rw-r--r--lib/pure/uri.nim16
-rw-r--r--lib/std/exitprocs.nim22
-rw-r--r--lib/std/isolation.nim31
-rw-r--r--lib/std/jsonutils.nim88
-rw-r--r--lib/std/private/nimbleutils.nim32
-rw-r--r--lib/system.nim7
-rw-r--r--lib/system/io.nim9
-rw-r--r--lib/system/nimscript.nim12
-rw-r--r--lib/system/repr_v2.nim38
-rw-r--r--lib/system/seqs_v2.nim4
-rw-r--r--lib/system/strmantle.nim8
-rw-r--r--lib/system/strs_v2.nim15
-rw-r--r--lib/system/widestrs.nim1
-rw-r--r--lib/system_overview.rst41
-rw-r--r--lib/wrappers/odbcsql.nim4
55 files changed, 1154 insertions, 456 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 5be3a2cce..e4a56d6f9 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -140,10 +140,6 @@ const
 
 {.push warnings: off.}
 
-proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated:
-  "Deprecated since version 0.18.0: Use 'ident' or 'newIdentNode' instead.".}
-  ## Constructs an identifier from the string `s`.
-
 proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated:
   "Deprecated since version 0.18.0: Use 'ident' or 'newIdentNode' instead.".}
   ## Constructs an identifier from the string `s`.
@@ -281,9 +277,12 @@ else: # bootstrapping substitute
 
 {.pop.}
 
-when defined(nimSymImplTransform):
+when (NimMajor, NimMinor, NimPatch) >= (1, 3, 5) or defined(nimSymImplTransform):
   proc getImplTransformed*(symbol: NimNode): NimNode {.magic: "GetImplTransf", noSideEffect.}
-    ## For a typed proc returns the AST after transformation pass.
+    ## For a typed proc returns the AST after transformation pass; this is useful
+    ## for debugging how the compiler transforms code (eg: `defer`, `for`) but
+    ## note that code transformations are implementation dependent and subject to change.
+    ## See an example in `tests/macros/tmacros_various.nim`.
 
 when defined(nimHasSymOwnerInMacro):
   proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
@@ -393,13 +392,6 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, de
 
 {.pop.}
 
-#proc `typ=`*(n: NimNode, typ: typedesc) {.magic: "NSetType".}
-# this is not sound! Unfortunately forbidding 'typ=' is not enough, as you
-# can easily do:
-#   let bracket = semCheck([1, 2])
-#   let fake = semCheck(2.0)
-#   bracket[0] = fake  # constructs a mixed array with ints and floats!
-
 proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
   ## Sets the string value of a string literal or comment.
   ## Setting `strVal` is disallowed for `nnkIdent` and `nnkSym` nodes; a new node
@@ -830,14 +822,6 @@ proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime
   for i in countdown(pack.len - 1, 0):
     result = newCall(op, pack[i], result)
 
-{.push warnings: off.}
-
-proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated:
-  "Deprecated since v0.18.1; use one of 'nestList(NimNode, ...)' instead.".} =
-  nestList(newIdentNode(theProc), x)
-
-{.pop.}
-
 proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} =
   if level > 0:
     if indented:
@@ -1628,21 +1612,6 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
   if result.kind == nnkEmpty:
     error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
 
-
-when not defined(booting):
-  template emit*(e: static[string]): untyped {.deprecated.} =
-    ## Accepts a single string argument and treats it as nim code
-    ## that should be inserted verbatim in the program
-    ## Example:
-    ##
-    ## .. code-block:: nim
-    ##   emit("echo " & '"' & "hello world".toUpper & '"')
-    ##
-    ## Deprecated since version 0.15 since it's so rarely useful.
-    macro payload: untyped {.gensym.} =
-      result = parseStmt(e)
-    payload()
-
 macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
   result = newCall(callee)
   for i in 0 ..< args.len:
@@ -1692,3 +1661,39 @@ when defined(nimMacrosSizealignof):
 
 proc isExported*(n: NimNode): bool {.noSideEffect.} =
   ## Returns whether the symbol is exported or not.
+
+proc extractDocCommentsAndRunnables*(n: NimNode): NimNode =
+  ## returns a `nnkStmtList` containing the top-level doc comments and
+  ## runnableExamples in `a`, stopping at the first child that is neither.
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##  import macros
+  ##  macro transf(a): untyped =
+  ##    result = quote do:
+  ##      proc fun2*() = discard
+  ##    let header = extractDocCommentsAndRunnables(a.body)
+  ##    # correct usage: rest is appended
+  ##    result.body = header
+  ##    result.body.add quote do: discard # just an example
+  ##    # incorrect usage: nesting inside a nnkStmtList:
+  ##    # result.body = quote do: (`header`; discard)
+  ##
+  ##  proc fun*() {.transf.} =
+  ##    ## first comment
+  ##    runnableExamples: discard
+  ##    runnableExamples: discard
+  ##    ## last comment
+  ##    discard # first statement after doc comments + runnableExamples
+  ##    ## not docgen'd
+
+  result = newStmtList()
+  for ni in n:
+    case ni.kind
+    of nnkCommentStmt:
+      result.add ni
+    of nnkCall:
+      if ni[0].kind == nnkIdent and ni[0].strVal == "runnableExamples":
+        result.add ni
+      else: break
+    else: break
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index fe71f89fe..0817ca4ec 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -7,7 +7,7 @@
 #
 
 when defined(js):
-  {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE.".}
+  {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
 
 ## What is NRE?
 ## ============
@@ -498,7 +498,7 @@ proc re*(pattern: string): Regex =
   initRegex(pattern, flags, study)
 
 proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Option[RegexMatch] =
-  var myResult = RegexMatch(pattern : pattern, str : str)
+  var myResult = RegexMatch(pattern: pattern, str: str)
   # See PCRE man pages.
   # 2x capture count to make room for start-end pairs
   # 1x capture count as slack space for PCRE
@@ -528,13 +528,13 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
     of pcre.ERROR_NULL:
       raise newException(AccessViolationDefect, "Expected non-null parameters")
     of pcre.ERROR_BADOPTION:
-      raise RegexInternalError(msg : "Unknown pattern flag. Either a bug or " &
+      raise RegexInternalError(msg: "Unknown pattern flag. Either a bug or " &
         "outdated PCRE.")
     of pcre.ERROR_BADUTF8, pcre.ERROR_SHORTUTF8, pcre.ERROR_BADUTF8_OFFSET:
-      raise InvalidUnicodeError(msg : "Invalid unicode byte sequence",
-        pos : myResult.pcreMatchBounds[0].a)
+      raise InvalidUnicodeError(msg: "Invalid unicode byte sequence",
+        pos: myResult.pcreMatchBounds[0].a)
     else:
-      raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
+      raise RegexInternalError(msg: "Unknown internal error: " & $execRet)
 
 proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
   ## Like ` ``find(...)`` <#proc-find>`_, but anchored to the start of the
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 1a6622677..fe3ea96ff 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -8,7 +8,7 @@
 #
 
 when defined(js):
-  {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE.".}
+  {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
 
 ## Regular expression support for Nim.
 ##
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index 48d32fc18..be676a813 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -106,7 +106,17 @@ type
     memory*: PerformanceMemory
     timing*: PerformanceTiming
 
-  Selection* {.importc.} = ref object ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Selection>`_
+  Range* {.importc.} = ref object
+    ## see `docs{https://developer.mozilla.org/en-US/docs/Web/API/Range}`_
+    collapsed*: bool
+    commonAncestorContainer*: Node
+    endContainer*: Node
+    endOffset*: int
+    startContainer*: Node
+    startOffset*: int
+
+  Selection* {.importc.} = ref object
+    ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Selection>`_
     anchorNode*: Node
     anchorOffset*: int
     focusNode*: Node
@@ -186,6 +196,9 @@ type
     innerText*: cstring
     textContent*: cstring
     style*: Style
+    baseURI*: cstring
+    parentElement*: Element
+    isConnected*: bool
 
   Document* = ref DocumentObj
   DocumentObj {.importc.} = object of NodeObj
@@ -210,9 +223,11 @@ type
     applets*: seq[Element]
     embeds*: seq[EmbedElement]
     links*: seq[LinkElement]
+    fonts*: FontFaceSet
 
   Element* = ref ElementObj
   ElementObj {.importc.} = object of NodeObj
+    className*: cstring
     classList*: ClassList
     checked*: bool
     defaultChecked*: bool
@@ -371,19 +386,73 @@ type
 
   Style* = ref StyleObj
   StyleObj {.importc.} = object of RootObj
+    alignContent*: cstring
+    alignItems*: cstring
+    alignSelf*: cstring
+    all*: cstring
+    animation*: cstring
+    animationDelay*: cstring
+    animationDirection*: cstring
+    animationDuration*: cstring
+    animationFillMode*: cstring
+    animationIterationCount*: cstring
+    animationName*: cstring
+    animationPlayState*: cstring
+    animationTimingFunction*: cstring
+    backdropFilter*: cstring
+    backfaceVisibility*: cstring
     background*: cstring
     backgroundAttachment*: cstring
+    backgroundBlendMode*: cstring
+    backgroundClip*: cstring
     backgroundColor*: cstring
     backgroundImage*: cstring
+    backgroundOrigin*: cstring
     backgroundPosition*: cstring
     backgroundRepeat*: cstring
     backgroundSize*: cstring
+    blockSize*: cstring
     border*: cstring
+    borderBlock*: cstring
+    borderBlockColor*: cstring
+    borderBlockEnd*: cstring
+    borderBlockEndColor*: cstring
+    borderBlockEndStyle*: cstring
+    borderBlockEndWidth*: cstring
+    borderBlockStart*: cstring
+    borderBlockStartColor*: cstring
+    borderBlockStartStyle*: cstring
+    borderBlockStartWidth*: cstring
+    borderBlockStyle*: cstring
+    borderBlockWidth*: cstring
     borderBottom*: cstring
     borderBottomColor*: cstring
+    borderBottomLeftRadius*: cstring
+    borderBottomRightRadius*: cstring
     borderBottomStyle*: cstring
     borderBottomWidth*: cstring
+    borderCollapse*: cstring
     borderColor*: cstring
+    borderEndEndRadius*: cstring
+    borderEndStartRadius*: cstring
+    borderImage*: cstring
+    borderImageOutset*: cstring
+    borderImageRepeat*: cstring
+    borderImageSlice*: cstring
+    borderImageSource*: cstring
+    borderImageWidth*: cstring
+    borderInline*: cstring
+    borderInlineColor*: cstring
+    borderInlineEnd*: cstring
+    borderInlineEndColor*: cstring
+    borderInlineEndStyle*: cstring
+    borderInlineEndWidth*: cstring
+    borderInlineStart*: cstring
+    borderInlineStartColor*: cstring
+    borderInlineStartStyle*: cstring
+    borderInlineStartWidth*: cstring
+    borderInlineStyle*: cstring
+    borderInlineWidth*: cstring
     borderLeft*: cstring
     borderLeftColor*: cstring
     borderLeftStyle*: cstring
@@ -393,86 +462,298 @@ type
     borderRightColor*: cstring
     borderRightStyle*: cstring
     borderRightWidth*: cstring
+    borderSpacing*: cstring
+    borderStartEndRadius*: cstring
+    borderStartStartRadius*: cstring
     borderStyle*: cstring
     borderTop*: cstring
     borderTopColor*: cstring
+    borderTopLeftRadius*: cstring
+    borderTopRightRadius*: cstring
     borderTopStyle*: cstring
     borderTopWidth*: cstring
     borderWidth*: cstring
     bottom*: cstring
-    boxSizing*: cstring
+    boxDecorationBreak*: cstring
     boxShadow*: cstring
+    boxSizing*: cstring
+    breakAfter*: cstring
+    breakBefore*: cstring
+    breakInside*: cstring
     captionSide*: cstring
+    caretColor*: cstring
     clear*: cstring
     clip*: cstring
     clipPath*: cstring
     color*: cstring
+    colorAdjust*: cstring
+    columnCount*: cstring
+    columnFill*: cstring
+    columnGap*: cstring
+    columnRule*: cstring
+    columnRuleColor*: cstring
+    columnRuleStyle*: cstring
+    columnRuleWidth*: cstring
+    columnSpan*: cstring
+    columnWidth*: cstring
+    columns*: cstring
+    contain*: cstring
+    content*: cstring
+    counterIncrement*: cstring
+    counterReset*: cstring
+    counterSet*: cstring
     cursor*: cstring
     direction*: cstring
     display*: cstring
     emptyCells*: cstring
+    filter*: cstring
+    flex*: cstring
+    flexBasis*: cstring
+    flexDirection*: cstring
+    flexFlow*: cstring
+    flexGrow*: cstring
+    flexShrink*: cstring
+    flexWrap*: cstring
     cssFloat*: cstring
     font*: cstring
     fontFamily*: cstring
+    fontFeatureSettings*: cstring
+    fontKerning*: cstring
+    fontLanguageOverride*: cstring
+    fontOpticalSizing*: cstring
     fontSize*: cstring
+    fontSizeAdjust*: cstring
     fontStretch*: cstring
     fontStyle*: cstring
+    fontSynthesis*: cstring
     fontVariant*: cstring
+    fontVariantAlternates*: cstring
+    fontVariantCaps*: cstring
+    fontVariantEastAsian*: cstring
+    fontVariantLigatures*: cstring
+    fontVariantNumeric*: cstring
+    fontVariantPosition*: cstring
+    fontVariationSettings*: cstring
     fontWeight*: cstring
+    gap*: cstring
+    grid*: cstring
+    gridArea*: cstring
+    gridAutoColumns*: cstring
+    gridAutoFlow*: cstring
+    gridAutoRows*: cstring
+    gridColumn*: cstring
+    gridColumnEnd*: cstring
+    gridColumnStart*: cstring
+    gridRow*: cstring
+    gridRowEnd*: cstring
+    gridRowStart*: cstring
+    gridTemplate*: cstring
+    gridTemplateAreas*: cstring
+    gridTemplateColumns*: cstring
+    gridTemplateRows*: cstring
+    hangingPunctuation*: cstring
     height*: cstring
+    hyphens*: cstring
+    imageOrientation*: cstring
+    imageRendering*: cstring
+    inlineSize*: cstring
+    inset*: cstring
+    insetBlock*: cstring
+    insetBlockEnd*: cstring
+    insetBlockStart*: cstring
+    insetInline*: cstring
+    insetInlineEnd*: cstring
+    insetInlineStart*: cstring
+    isolation*: cstring
+    justifyContent*: cstring
+    justifyItems*: cstring
+    justifySelf*: cstring
     left*: cstring
     letterSpacing*: cstring
+    lineBreak*: cstring
     lineHeight*: cstring
     listStyle*: cstring
     listStyleImage*: cstring
     listStylePosition*: cstring
     listStyleType*: cstring
     margin*: cstring
+    marginBlock*: cstring
+    marginBlockEnd*: cstring
+    marginBlockStart*: cstring
     marginBottom*: cstring
+    marginInline*: cstring
+    marginInlineEnd*: cstring
+    marginInlineStart*: cstring
     marginLeft*: cstring
     marginRight*: cstring
     marginTop*: cstring
+    mask*: cstring
+    maskBorder*: cstring
+    maskBorderMode*: cstring
+    maskBorderOutset*: cstring
+    maskBorderRepeat*: cstring
+    maskBorderSlice*: cstring
+    maskBorderSource*: cstring
+    maskBorderWidth*: cstring
+    maskClip*: cstring
+    maskComposite*: cstring
+    maskImage*: cstring
+    maskMode*: cstring
+    maskOrigin*: cstring
+    maskPosition*: cstring
+    maskRepeat*: cstring
+    maskSize*: cstring
+    maskType*: cstring
+    maxBlockSize*: cstring
     maxHeight*: cstring
+    maxInlineSize*: cstring
     maxWidth*: cstring
+    minBlockSize*: cstring
     minHeight*: cstring
+    minInlineSize*: cstring
     minWidth*: cstring
+    mixBlendMode*: cstring
+    objectFit*: cstring
+    objectPosition*: cstring
+    offset*: cstring
+    offsetAnchor*: cstring
+    offsetDistance*: cstring
+    offsetPath*: cstring
+    offsetRotate*: cstring
     opacity*: cstring
+    order*: cstring
+    orphans*: cstring
     outline*: cstring
+    outlineColor*: cstring
+    outlineOffset*: cstring
+    outlineStyle*: cstring
+    outlineWidth*: cstring
     overflow*: cstring
+    overflowAnchor*: cstring
+    overflowBlock*: cstring
+    overflowInline*: cstring
+    overflowWrap*: cstring
     overflowX*: cstring
     overflowY*: cstring
+    overscrollBehavior*: cstring
+    overscrollBehaviorBlock*: cstring
+    overscrollBehaviorInline*: cstring
+    overscrollBehaviorX*: cstring
+    overscrollBehaviorY*: cstring
     padding*: cstring
+    paddingBlock*: cstring
+    paddingBlockEnd*: cstring
+    paddingBlockStart*: cstring
     paddingBottom*: cstring
+    paddingInline*: cstring
+    paddingInlineEnd*: cstring
+    paddingInlineStart*: cstring
     paddingLeft*: cstring
     paddingRight*: cstring
     paddingTop*: cstring
     pageBreakAfter*: cstring
     pageBreakBefore*: cstring
+    pageBreakInside*: cstring
+    paintOrder*: cstring
+    perspective*: cstring
+    perspectiveOrigin*: cstring
+    placeContent*: cstring
+    placeItems*: cstring
+    placeSelf*: cstring
     pointerEvents*: cstring
     position*: cstring
+    quotes*: cstring
     resize*: cstring
     right*: cstring
+    rotate*: cstring
+    rowGap*: cstring
+    scale*: cstring
+    scrollBehavior*: cstring
+    scrollMargin*: cstring
+    scrollMarginBlock*: cstring
+    scrollMarginBlockEnd*: cstring
+    scrollMarginBlockStart*: cstring
+    scrollMarginBottom*: cstring
+    scrollMarginInline*: cstring
+    scrollMarginInlineEnd*: cstring
+    scrollMarginInlineStart*: cstring
+    scrollMarginLeft*: cstring
+    scrollMarginRight*: cstring
+    scrollMarginTop*: cstring
+    scrollPadding*: cstring
+    scrollPaddingBlock*: cstring
+    scrollPaddingBlockEnd*: cstring
+    scrollPaddingBlockStart*: cstring
+    scrollPaddingBottom*: cstring
+    scrollPaddingInline*: cstring
+    scrollPaddingInlineEnd*: cstring
+    scrollPaddingInlineStart*: cstring
+    scrollPaddingLeft*: cstring
+    scrollPaddingRight*: cstring
+    scrollPaddingTop*: cstring
+    scrollSnapAlign*: cstring
+    scrollSnapStop*: cstring
+    scrollSnapType*: cstring
     scrollbar3dLightColor*: cstring
     scrollbarArrowColor*: cstring
     scrollbarBaseColor*: cstring
+    scrollbarColor*: cstring
     scrollbarDarkshadowColor*: cstring
     scrollbarFaceColor*: cstring
     scrollbarHighlightColor*: cstring
     scrollbarShadowColor*: cstring
     scrollbarTrackColor*: cstring
+    scrollbarWidth*: cstring
+    shapeImageThreshold*: cstring
+    shapeMargin*: cstring
+    shapeOutside*: cstring
+    tabSize*: cstring
     tableLayout*: cstring
     textAlign*: cstring
+    textAlignLast*: cstring
+    textCombineUpright*: cstring
     textDecoration*: cstring
+    textDecorationColor*: cstring
+    textDecorationLine*: cstring
+    textDecorationSkipInk*: cstring
+    textDecorationStyle*: cstring
+    textDecorationThickness*: cstring
+    textEmphasis*: cstring
+    textEmphasisColor*: cstring
+    textEmphasisPosition*: cstring
+    textEmphasisStyle*: cstring
     textIndent*: cstring
+    textJustify*: cstring
+    textOrientation*: cstring
+    textOverflow*: cstring
+    textRendering*: cstring
+    textShadow*: cstring
     textTransform*: cstring
-    transform*: cstring
+    textUnderlineOffset*: cstring
+    textUnderlinePosition*: cstring
     top*: cstring
+    touchAction*: cstring
+    transform*: cstring
+    transformBox*: cstring
+    transformOrigin*: cstring
+    transformStyle*: cstring
+    transition*: cstring
+    transitionDelay*: cstring
+    transitionDuration*: cstring
+    transitionProperty*: cstring
+    transitionTimingFunction*: cstring
+    translate*: cstring
+    unicodeBidi*: cstring
     verticalAlign*: cstring
     visibility*: cstring
-    width*: cstring
     whiteSpace*: cstring
+    widows*: cstring
+    width*: cstring
+    willChange*: cstring
+    wordBreak*: cstring
     wordSpacing*: cstring
-    zIndex*: int
+    writingMode*: cstring
+    zIndex*: cstring
 
   EventPhase* = enum
     None = 0,
@@ -923,6 +1204,10 @@ type
     ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DragEvent>`_
     dataTransfer*: DataTransfer
 
+  ClipboardEvent* {.importc.} = object of Event
+    ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent>`_
+    clipboardData*: DataTransfer
+
   TouchList* {.importc.} = ref object of RootObj
     length*: int
 
@@ -1002,6 +1287,15 @@ type
     once*: bool
     passive*: bool
 
+  FontFaceSetReady* {.importc.} = ref object
+    ## see: `docs<https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready>`_
+    then*: proc(cb: proc())
+
+  FontFaceSet* {.importc.} = ref object
+    ## see: `docs<https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet>`_
+    ready*: FontFaceSetReady
+    onloadingdone*: proc(event: Event)
+
 since (1, 3):
   type
     DomParser* = ref object
@@ -1127,6 +1421,7 @@ else:
   proc getElementById*(id: cstring): Element {.importc: "document.getElementById", nodecl.}
   proc appendChild*(n, child: Node) {.importcpp.}
   proc removeChild*(n, child: Node) {.importcpp.}
+  proc remove*(child: Node) {.importcpp.}
   proc replaceChild*(n, newNode, oldNode: Node) {.importcpp.}
   proc insertBefore*(n, newNode, before: Node) {.importcpp.}
   proc getElementById*(d: Document, id: cstring): Element {.importcpp.}
@@ -1149,7 +1444,6 @@ proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event))
 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 Interval)
 proc clearTimeout*(w: Window, timeout: ref TimeOut)
 proc close*(w: Window)
@@ -1169,7 +1463,6 @@ 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)
@@ -1190,7 +1483,9 @@ proc deleteData*(n: Node, start, len: int)
 proc focus*(e: Node)
 proc getAttribute*(n: Node, attr: cstring): cstring
 proc getAttributeNode*(n: Node, attr: cstring): Node
+proc hasAttribute*(n: Node, attr: cstring): bool
 proc hasChildNodes*(n: Node): bool
+proc normalize*(n: Node)
 proc insertData*(n: Node, position: int, data: cstring)
 proc removeAttribute*(n: Node, attr: cstring)
 proc removeAttributeNode*(n, attr: Node)
@@ -1200,17 +1495,23 @@ proc setAttribute*(n: Node, name, value: cstring)
 proc setAttributeNode*(n: Node, attr: Node)
 proc querySelector*(n: Node, selectors: cstring): Element
 proc querySelectorAll*(n: Node, selectors: cstring): seq[Element]
+proc compareDocumentPosition*(n: Node, otherNode:Node): int
+proc lookupPrefix*(n: Node): cstring
+proc lookupNamespaceURI*(n: Node): cstring
+proc isDefaultNamespace*(n: Node): bool
+proc contains*(n: Node): bool
+proc isEqualNode*(n: Node): bool
+proc isSameNode*(n: Node): bool
 
 # Document "methods"
-proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
 proc createAttribute*(d: Document, identifier: cstring): Node
 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 insertNode*(range: Range, node: Node)
 proc getSelection*(d: Document): Selection
 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)
@@ -1299,6 +1600,8 @@ proc now*(p: Performance): float
 
 # Selection "methods"
 proc removeAllRanges*(s: Selection)
+proc deleteFromDocument*(s: Selection)
+proc getRangeAt*(s: Selection, index: int): Range
 converter toString*(s: Selection): cstring
 proc `$`*(s: Selection): string = $(s.toString())
 
diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim
new file mode 100644
index 000000000..31cfa3d0b
--- /dev/null
+++ b/lib/js/jsre.nim
@@ -0,0 +1,43 @@
+## Regular Expressions for the JavaScript target.
+## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
+
+type RegExp* {.importjs.} = object    ## Regular Expressions for JavaScript target.
+  flags* {.importjs.}: cstring        ## cstring that contains the flags of the RegExp object.
+  dotAll* {.importjs.}: bool          ## Whether `.` matches newlines or not.
+  global* {.importjs.}: bool          ## Whether to test against all possible matches in a string, or only against the first.
+  ignoreCase* {.importjs.}: bool      ## Whether to ignore case while attempting a match in a string.
+  multiline* {.importjs.}: bool       ## Whether to search in strings across multiple lines.
+  source* {.importjs.}: cstring       ## The text of the pattern.
+  sticky* {.importjs.}: bool          ## Whether the search is sticky.
+  unicode* {.importjs.}: bool         ## Whether Unicode features are enabled.
+  lastIndex* {.importjs.}: cint       ## Index at which to start the next match (read/write property).
+  input* {.importjs.}: cstring        ## Read-only and modified on successful match.
+  lastMatch* {.importjs.}: cstring    ## Ditto.
+  lastParen* {.importjs.}: cstring    ## Ditto.
+  leftContext* {.importjs.}: cstring  ## Ditto.
+  rightContext* {.importjs.}: cstring ## Ditto.
+
+func newRegExp*(pattern: cstring; flags: cstring): RegExp {.importjs: "new RegExp(@)".}
+  ## Creates a new RegExp object.
+
+func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.compile(@)".}
+  ## Recompiles a regular expression during execution of a script.
+
+func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "#.exec(#)".}
+  ## Executes a search for a match in its string parameter.
+
+func test*(self: RegExp; pattern: cstring): bool {.importjs: "#.test(#)".}
+  ## Tests for a match in its string parameter.
+
+func toString*(self: RegExp): cstring {.importjs: "#.toString()".}
+  ## Returns a string representing the RegExp object.
+
+
+runnableExamples:
+  let jsregex: RegExp = newRegExp(r"\s+", r"i")
+  jsregex.compile(r"\w+", r"i")
+  doAssert jsregex.test(r"nim javascript")
+  doAssert jsregex.exec(r"nim javascript") == @["nim".cstring]
+  doAssert jsregex.toString() == r"/\w+/i"
+  jsregex.compile(r"[0-9]", r"i")
+  doAssert jsregex.test(r"0123456789abcd")
diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim
index 372ee1973..89bece6dc 100644
--- a/lib/nimhcr.nim
+++ b/lib/nimhcr.nim
@@ -1,3 +1,7 @@
+discard """
+batchable: false
+"""
+
 #
 #
 #            Nim's Runtime Library
diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim
index 7ec5eb981..a2a10c6bd 100644
--- a/lib/nimrtl.nim
+++ b/lib/nimrtl.nim
@@ -1,3 +1,7 @@
+discard """
+batchable: false
+"""
+
 #
 #
 #            Nim's Runtime Library
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index ca6a3e05e..796c17d7d 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -342,7 +342,7 @@ proc nimNextToken(g: var GeneralTokenizer) =
         inc(pos)
         g.kind = gtNone
   g.length = pos - g.pos
-  if g.kind != gtEof and g.length <= 0:
+  if g.kind != gtEof and g.state != gtNone and g.length <= 0:
     assert false, "nimNextToken: produced an empty token"
   g.pos = pos
 
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 07cb3eccc..05b58c56b 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -313,7 +313,7 @@ proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind,
   else: writeLine(stdout, message)
 
 proc defaultFindFile*(filename: string): string =
-  if existsFile(filename): result = filename
+  if fileExists(filename): result = filename
   else: result = ""
 
 proc newSharedState(options: RstParseOptions,
@@ -328,7 +328,7 @@ proc newSharedState(options: RstParseOptions,
 
 proc findRelativeFile(p: RstParser; filename: string): string =
   result = p.filename.splitFile.dir / filename
-  if not existsFile(result):
+  if not fileExists(result):
     result = p.s.findFile(filename)
 
 proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index a15442109..6330a6ba9 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -1228,14 +1228,19 @@ else:
     let newLength = max(len(curList), InitCallbackListSize)
     var newList = newSeqOfCap[Callback](newLength)
 
+    var eventsExtinguished = false
     for cb in curList:
+      if eventsExtinguished:
+        newList.add(cb)
+        continue
       if not cb(fd):
         # Callback wants to be called again.
         newList.add(cb)
         # This callback has returned with EAGAIN, so we don't need to
         # call any other callbacks as they are all waiting for the same event
         # on the same fd.
-        break
+        # We do need to ensure they are called again though.
+        eventsExtinguished = true
 
     withData(selector, fd.int, fdData) do:
       # Descriptor is still present in the queue.
@@ -1601,11 +1606,16 @@ else:
     p.selector.registerEvent(SelectEvent(ev), data)
 
 proc drain*(timeout = 500) =
-  ## Waits for completion events and processes them. Raises ``ValueError``
+  ## Waits for completion of **all** events and processes them. Raises ``ValueError``
   ## if there are no pending operations. In contrast to ``poll`` this
-  ## processes as many events as are available.
-  if runOnce(timeout) or hasPendingOperations():
-    while hasPendingOperations() and runOnce(timeout): discard
+  ## processes as many events as are available until the timeout has elapsed.
+  var curTimeout = timeout
+  let start = now()
+  while hasPendingOperations():
+    discard runOnce(curTimeout) 
+    curTimeout -= (now() - start).inMilliseconds.int
+    if curTimeout < 0:
+      break
 
 proc poll*(timeout = 500) =
   ## Waits for completion events and processes them. Raises ``ValueError``
@@ -1635,16 +1645,6 @@ proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
                               inheritable = defined(nimInheritHandles)): AsyncFD =
   createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
 
-proc newAsyncNativeSocket*(domain: cint, sockType: cint,
-    protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} =
-  createAsyncNativeSocketImpl(domain, sockType, protocol)
-
-proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
-                           sockType: SockType = SOCK_STREAM,
-                           protocol: Protocol = IPPROTO_TCP): AsyncFD
-                           {.deprecated: "use createAsyncNativeSocket instead".} =
-  createAsyncNativeSocketImpl(domain, sockType, protocol)
-
 when defined(windows) or defined(nimdoc):
   proc bindToDomain(handle: SocketHandle, domain: Domain) =
     # Extracted into a separate proc, because connect() on Windows requires
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
index 132682b23..4e62ecd97 100644
--- a/lib/pure/asyncftpclient.nim
+++ b/lib/pure/asyncftpclient.nim
@@ -134,7 +134,7 @@ proc expectReply(ftp: AsyncFtpClient): Future[TaintedString] {.async.} =
   var line = await ftp.csock.recvLine()
   result = TaintedString(line)
   var count = 0
-  while line[3] == '-':
+  while line.len > 3 and line[3] == '-':
     ## Multi-line reply.
     line = await ftp.csock.recvLine()
     string(result).add("\n" & line)
@@ -231,7 +231,7 @@ proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} =
 
   result = splitLines(await ftp.getLines())
 
-proc existsFile*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} =
+proc fileExists*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} =
   ## Determines whether ``file`` exists.
   var files = await ftp.listDirs()
   for f in items(files):
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index 621a4b00c..219ef6c67 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -180,8 +180,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
   # Extract the documentation comment from the original procedure declaration.
   # Note that we're not removing it from the body in order not to make this
   # transformation even more complex.
-  if prc.body.len > 1 and prc.body[0].kind == nnkCommentStmt:
-    outerProcBody.add(prc.body[0])
+  let body2 = extractDocCommentsAndRunnables(prc.body)
 
   # -> var retFuture = newFuture[T]()
   var retFutureSym = genSym(nskVar, "retFuture")
@@ -276,9 +275,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
       (cast[type(f)](internalTmpFuture)).read()
 
   if procBody.kind != nnkEmpty:
-    result.body = quote:
+    body2.add quote do:
       `awaitDefinition`
       `outerProcBody`
+    result.body = body2
 
   #echo(treeRepr(result))
   #if prcName == "recvLineInto":
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 3fc1a0177..410310e29 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -853,40 +853,49 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string,
     else:
       raise newException(IOError, "Couldn't resolve address: " & address)
 
-proc recvFrom*(socket: AsyncSocket, size: int,
-               flags = {SocketFlag.SafeDisconn}):
-              owned(Future[tuple[data: string, address: string, port: Port]])
+proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
+               address: FutureVar[string], port: FutureVar[Port],
+               flags = {SocketFlag.SafeDisconn}): owned(Future[int])
               {.async, since: (1, 3).} =
-  ## Receives a datagram data from ``socket``, which must be at least of size
-  ## ``size``. Returned future will complete once one datagram has been received
-  ## and will return tuple with: data of packet received; and address and port
-  ## of datagram's sender.
-  ## 
+  ## Receives a datagram data from ``socket`` into ``data``, which must be at
+  ## least of size ``size``. The address and port of datagram's sender will be
+  ## stored into ``address`` and ``port``, respectively. Returned future will
+  ## complete once one datagram has been received, and will return size of
+  ## packet received.
+  ##
   ## If an error occurs an OSError exception will be raised.
   ## 
   ## This proc is normally used with connectionless sockets (UDP sockets).
+  ## 
+  ## **Notes**
+  ## * ``data`` must be initialized to the length of ``size``.
+  ## * ``address`` must be initialized to 46 in length.
   template adaptRecvFromToDomain(domain: Domain) =
     var lAddr = sizeof(sAddr).SockLen
     
-    let fut = await recvFromInto(AsyncFD(getFd(socket)), cstring(data), size,
-                                 cast[ptr SockAddr](addr sAddr), addr lAddr,
-                                 flags)
+    result = await recvFromInto(AsyncFD(getFd(socket)), cstring(data.mget()), size,
+                                cast[ptr SockAddr](addr sAddr), addr lAddr,
+                                flags)
     
-    data.setLen(fut)
+    data.mget().setLen(result)
+    data.complete()
+
+    getAddrString(cast[ptr SockAddr](addr sAddr), address.mget())
     
-    result.data = data
-    result.address = getAddrString(cast[ptr SockAddr](addr sAddr))
+    address.complete()
 
     when domain == AF_INET6:
-      result.port = ntohs(sAddr.sin6_port).Port
+      port.complete(ntohs(sAddr.sin6_port).Port)
     else:
-      result.port = ntohs(sAddr.sin_port).Port
+      port.complete(ntohs(sAddr.sin_port).Port)
 
   assert(socket.protocol != IPPROTO_TCP,
          "Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead")
   assert(not socket.closed, "Cannot `recvFrom` on a closed socket")
-
-  var data = newString(size)
+  assert(size == len(data.mget()),
+         "`date` was not initialized correctly. `size` != `len(data.mget())`")
+  assert(46 == len(address.mget()),
+         "`address` was not initialized correctly. 46 != `len(address.mget())`")
   
   case socket.domain
   of AF_INET6:
@@ -898,6 +907,30 @@ proc recvFrom*(socket: AsyncSocket, size: int,
   else:
     raise newException(ValueError, "Unknown socket address family")
 
+proc recvFrom*(socket: AsyncSocket, size: int,
+               flags = {SocketFlag.SafeDisconn}):
+              owned(Future[tuple[data: string, address: string, port: Port]])
+              {.async, since: (1, 3).} =
+  ## Receives a datagram data from ``socket``, which must be at least of size
+  ## ``size``. Returned future will complete once one datagram has been received
+  ## and will return tuple with: data of packet received; and address and port
+  ## of datagram's sender.
+  ## 
+  ## If an error occurs an OSError exception will be raised.
+  ## 
+  ## This proc is normally used with connectionless sockets (UDP sockets).
+  var
+    data = newFutureVar[string]()
+    address = newFutureVar[string]()
+    port = newFutureVar[Port]()
+  
+  data.mget().setLen(size)
+  address.mget().setLen(46)
+  
+  let read = await recvFrom(socket, data, size, address, port, flags)
+
+  result = (data.mget(), address.mget(), port.mget())
+
 when not defined(testing) and isMainModule:
   type
     TestCases = enum
diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim
index 44e73003e..393262c4f 100644
--- a/lib/pure/asyncstreams.nim
+++ b/lib/pure/asyncstreams.nim
@@ -96,7 +96,7 @@ proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) =
       if resFut.finished: return
 
       # We don't want this callback called again.
-      future.cb = nil
+      #future.cb = nil
 
       # The return value depends on whether the FutureStream has finished.
       var res: (bool, T)
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index dd7c4d477..734ab9171 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -32,12 +32,7 @@
 import strutils, os, strtabs, cookies, uri
 export uri.encodeUrl, uri.decodeUrl
 
-proc handleHexChar(c: char, x: var int) {.inline.} =
-  case c
-  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
-  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
-  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
-  else: assert(false)
+include includes/decode_helpers
 
 proc addXmlChar(dest: var string, c: char) {.inline.} =
   case c
@@ -93,40 +88,27 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
 iterator decodeData*(data: string): tuple[key, value: TaintedString] =
   ## Reads and decodes CGI data and yields the (name, value) pairs the
   ## data consists of.
+  proc parseData(data: string, i: int, field: var string): int =
+    result = i
+    while result < data.len:
+      case data[result]
+      of '%': add(field, decodePercent(data, result))
+      of '+': add(field, ' ')
+      of '=', '&': break
+      else: add(field, data[result])
+      inc(result)
+
   var i = 0
   var name = ""
   var value = ""
   # decode everything in one pass:
   while i < data.len:
     setLen(name, 0) # reuse memory
-    while i < data.len:
-      case data[i]
-      of '%':
-        var x = 0
-        handleHexChar(data[i+1], x)
-        handleHexChar(data[i+2], x)
-        inc(i, 2)
-        add(name, chr(x))
-      of '+': add(name, ' ')
-      of '=', '&': break
-      else: add(name, data[i])
-      inc(i)
+    i = parseData(data, i, name)
     if i >= data.len or data[i] != '=': cgiError("'=' expected")
     inc(i) # skip '='
     setLen(value, 0) # reuse memory
-    while i < data.len:
-      case data[i]
-      of '%':
-        var x = 0
-        if i+2 < data.len:
-          handleHexChar(data[i+1], x)
-          handleHexChar(data[i+2], x)
-        inc(i, 2)
-        add(value, chr(x))
-      of '+': add(value, ' ')
-      of '&', '\0': break
-      else: add(value, data[i])
-      inc(i)
+    i = parseData(data, i, value)
     yield (name.TaintedString, value.TaintedString)
     if i < data.len:
       if data[i] == '&': inc(i)
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index d096874a3..8150563cc 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -67,9 +67,9 @@ const
   defaultInitialSize* = 4
 
 template initImpl(result: typed, initialSize: int) =
-  assert isPowerOfTwo(initialSize)
-  result.mask = initialSize-1
-  newSeq(result.data, initialSize)
+  let correctSize = nextPowerOfTwo(initialSize)
+  result.mask = correctSize-1
+  newSeq(result.data, correctSize)
 
 template checkIfInitialized(deq: typed) =
   when compiles(defaultInitialSize):
@@ -82,11 +82,6 @@ proc initDeque*[T](initialSize: int = 4): Deque[T] =
   ## Optionally, the initial capacity can be reserved via `initialSize`
   ## as a performance optimization.
   ## The length of a newly created deque will still be 0.
-  ##
-  ## ``initialSize`` must be a power of two (default: 4).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_.
   result.initImpl(initialSize)
 
 proc len*[T](deq: Deque[T]): int {.inline.} =
diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim
index e998145e7..336dbc07d 100644
--- a/lib/pure/collections/hashcommon.nim
+++ b/lib/pure/collections/hashcommon.nim
@@ -30,17 +30,23 @@ proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = (h + 1) and maxHash
 
 proc mustRehash[T](t: T): bool {.inline.} =
+  # If this is changed, make sure to synchronize it with `slotsNeeded` below
   assert(t.dataLen > t.counter)
   result = (t.dataLen * 2 < t.counter * 3) or (t.dataLen - t.counter < 4)
 
-proc rightSize*(count: Natural): int {.inline.} =
+proc slotsNeeded(count: Natural): int {.inline.} =
+  # Make sure to synchronize with `mustRehash` above
+  result = nextPowerOfTwo(count * 3 div 2 + 4)
+
+proc rightSize*(count: Natural): int {.inline, deprecated: "Deprecated since 1.4.0".} =
+  ## **Deprecated since Nim v1.4.0**, it is not needed anymore
+  ## because picking the correct size is done internally.
+  ##
   ## Return the value of ``initialSize`` to support ``count`` items.
   ##
   ## If more items are expected to be added, simply add that
   ## expected extra amount to the parameter before calling this.
-  #
-  # Make sure to synchronize with `mustRehash`
-  result = nextPowerOfTwo(count * 3 div 2 + 4)
+  result = count
 
 template rawGetKnownHCImpl() {.dirty.} =
   if t.dataLen == 0:
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index f101d508e..51d8ade85 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -527,7 +527,8 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) =
     assert dest == outcome
 
   var j = len(dest) - 1
-  var i = len(dest) + len(src) - 1
+  var i = j + len(src)
+  if i == j: return
   dest.setLen(i + 1)
 
   # Move items after `pos` to the end of the sequence.
diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim
index d798cbcb3..20da6b6c2 100644
--- a/lib/pure/collections/setimpl.nim
+++ b/lib/pure/collections/setimpl.nim
@@ -16,12 +16,12 @@ template dataLen(t): untyped = len(t.data)
 include hashcommon
 
 template initImpl(s: typed, size: int) =
-  assert isPowerOfTwo(size)
+  let correctSize = slotsNeeded(size)
   when s is OrderedSet:
     s.first = -1
     s.last = -1
   s.counter = 0
-  newSeq(s.data, size)
+  newSeq(s.data, correctSize)
 
 template rawInsertImpl() {.dirty.} =
   if data.len == 0:
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 2b270c2cb..b019da2a7 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -94,11 +94,6 @@ include setimpl
 proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) =
   ## Initializes a hash set.
   ##
-  ## The `initialSize` parameter needs to be a power of two (default: 64).
-  ## If you need to accept runtime values for this, you can use
-  ## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo,int>`_ or
-  ## `rightSize proc <#rightSize,Natural>`_ from this module.
-  ##
   ## Starting from Nim v0.20, sets are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
@@ -222,7 +217,7 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
     assert len(b) == 5
     ## b == {'a', 'b', 'c', 'd', 'r'}
 
-  result = initHashSet[A](rightSize(keys.len))
+  result = initHashSet[A](keys.len)
   for key in items(keys): result.incl(key)
 
 iterator items*[A](s: HashSet[A]): A =
@@ -628,11 +623,6 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
 proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) =
   ## Initializes an ordered hash set.
   ##
-  ## The `initialSize` parameter needs to be a power of two (default: 64).
-  ## If you need to accept runtime values for this, you can use
-  ## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo,int>`_ or
-  ## `rightSize proc <#rightSize,Natural>`_ from this module.
-  ##
   ## Starting from Nim v0.20, sets are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
@@ -685,7 +675,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
     assert len(b) == 5
     ## b == {'a', 'b', 'r', 'c', 'd'} # different than in HashSet
 
-  result = initOrderedSet[A](rightSize(keys.len))
+  result = initOrderedSet[A](keys.len)
   for key in items(keys): result.incl(key)
 
 proc contains*[A](s: OrderedSet[A], key: A): bool =
@@ -980,7 +970,7 @@ when isMainModule and not defined(release):
 
     block toSeqAndString:
       var a = toHashSet([2, 7, 5])
-      var b = initHashSet[int](rightSize(a.len))
+      var b = initHashSet[int](a.len)
       for x in [2, 7, 5]: b.incl(x)
       assert($a == $b)
       #echo a
@@ -1098,20 +1088,6 @@ when isMainModule and not defined(release):
       b.incl(2)
       assert b.len == 1
 
-    block:
-      type FakeTable = object
-        dataLen: int
-        counter: int
-        countDeleted: int
-
-      var t: FakeTable
-      for i in 0 .. 32:
-        var s = rightSize(i)
-        t.dataLen = s
-        t.counter = i
-        doAssert s > i and not mustRehash(t),
-          "performance issue: rightSize() will not elide enlarge() at: " & $i
-
     block missingOrExcl:
       var s = toOrderedSet([2, 3, 6, 7])
       assert s.missingOrExcl(4) == true
diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim
index f9182acce..790529b79 100644
--- a/lib/pure/collections/sharedlist.nim
+++ b/lib/pure/collections/sharedlist.nim
@@ -94,10 +94,4 @@ proc deinitSharedList*[A](t: var SharedList[A]) =
   clear(t)
   deinitLock t.lock
 
-proc initSharedList*[A](): SharedList[A] {.deprecated: "use 'init' instead".} =
-  ## This is not posix compliant, may introduce undefined behavior.
-  initLock result.lock
-  result.head = nil
-  result.tail = nil
-
 {.pop.}
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index 23b653c82..cbd922db7 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -207,15 +207,11 @@ proc len*[A, B](t: var SharedTable[A, B]): int =
   withLock t:
     result = t.counter
 
-proc init*[A, B](t: var SharedTable[A, B], initialSize = 64) =
+proc init*[A, B](t: var SharedTable[A, B], initialSize = 32) =
   ## creates a new hash table that is empty.
   ##
   ## This proc must be called before any other usage of `t`.
-  ##
-  ## `initialSize` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module or the ``rightSize`` proc from this module.
-  assert isPowerOfTwo(initialSize)
+  let initialSize = slotsNeeded(initialSize)
   t.counter = 0
   t.dataLen = initialSize
   t.data = cast[KeyValuePairSeq[A, B]](allocShared0(
@@ -225,13 +221,3 @@ proc init*[A, B](t: var SharedTable[A, B], initialSize = 64) =
 proc deinitSharedTable*[A, B](t: var SharedTable[A, B]) =
   deallocShared(t.data)
   deinitLock t.lock
-
-proc initSharedTable*[A, B](initialSize = 64): SharedTable[A, B] {.deprecated:
-  "use 'init' instead".} =
-  ## This is not posix compliant, may introduce undefined behavior.
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  result.dataLen = initialSize
-  result.data = cast[KeyValuePairSeq[A, B]](allocShared0(
-                                     sizeof(KeyValuePair[A, B]) * initialSize))
-  initLock result.lock
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index 47c14af93..b9d7c70d9 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -121,12 +121,12 @@ template ctAnd(a, b): bool =
   else: false
 
 template initImpl(result: typed, size: int) =
+  let correctSize = slotsNeeded(size)
   when ctAnd(declared(SharedTable), type(result) is SharedTable):
-    init(result, size)
+    init(result, correctSize)
   else:
-    assert isPowerOfTwo(size)
     result.counter = 0
-    newSeq(result.data, size)
+    newSeq(result.data, correctSize)
     when compiles(result.first):
       result.first = -1
       result.last = -1
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 2ea58ce1f..cf864c640 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -239,7 +239,7 @@ type
     ## <#newTable,int>`_.
 
 const
-  defaultInitialSize* = 64
+  defaultInitialSize* = 32
 
 # ------------------------------ helpers ---------------------------------
 
@@ -288,12 +288,6 @@ proc enlarge[A, B](t: var Table[A, B]) =
 proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] =
   ## Creates a new hash table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## Starting from Nim v0.20, tables are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
@@ -335,7 +329,7 @@ proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
     let b = toTable(a)
     assert b == {'a': 5, 'b': 9}.toTable
 
-  result = initTable[A, B](rightSize(pairs.len))
+  result = initTable[A, B](pairs.len)
   for key, val in items(pairs): result[key] = val
 
 proc `[]`*[A, B](t: Table[A, B], key: A): B =
@@ -780,12 +774,6 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B =
 proc newTable*[A, B](initialSize = defaultInitialSize): <//>TableRef[A, B] =
   ## Creates a new ref hash table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## See also:
   ## * `newTable proc<#newTable,openArray[]>`_ for creating a `TableRef`
   ##   from a collection of `(key, value)` pairs
@@ -1260,12 +1248,6 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
 proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, B] =
   ## Creates a new ordered hash table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## Starting from Nim v0.20, tables are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
@@ -1309,7 +1291,7 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
     let b = toOrderedTable(a)
     assert b == {'a': 5, 'b': 9}.toOrderedTable
 
-  result = initOrderedTable[A, B](rightSize(pairs.len))
+  result = initOrderedTable[A, B](pairs.len)
   for key, val in items(pairs): result[key] = val
 
 proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
@@ -1771,12 +1753,6 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
 proc newOrderedTable*[A, B](initialSize = defaultInitialSize): <//>OrderedTableRef[A, B] =
   ## Creates a new ordered ref hash table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## See also:
   ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for creating
   ##   an `OrderedTableRef` from a collection of `(key, value)` pairs
@@ -1803,7 +1779,7 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): <//>OrderedTableRef[A, B]
     let b = newOrderedTable(a)
     assert b == {'a': 5, 'b': 9}.newOrderedTable
 
-  result = newOrderedTable[A, B](rightSize(pairs.len))
+  result = newOrderedTable[A, B](pairs.len)
   for key, val in items(pairs): result.add(key, val)
 
 
@@ -2251,12 +2227,6 @@ proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1)
 proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] =
   ## Creates a new count table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## Starting from Nim v0.20, tables are initialized by default and it is
   ## not necessary to call this function explicitly.
   ##
@@ -2269,7 +2239,7 @@ proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] =
 proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
   ## Creates a new count table with every member of a container ``keys``
   ## having a count of how many times it occurs in that container.
-  result = initCountTable[A](rightSize(keys.len))
+  result = initCountTable[A](keys.len)
   for key in items(keys): result.inc(key)
 
 proc `[]`*[A](t: CountTable[A], key: A): int =
@@ -2617,12 +2587,6 @@ proc inc*[A](t: CountTableRef[A], key: A, val = 1)
 proc newCountTable*[A](initialSize = defaultInitialSize): <//>CountTableRef[A] =
   ## Creates a new ref count table that is empty.
   ##
-  ## ``initialSize`` must be a power of two (default: 64).
-  ## If you need to accept runtime values for this you could use the
-  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
-  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
-  ## from this module.
-  ##
   ## See also:
   ## * `newCountTable proc<#newCountTable,openArray[A]>`_ for creating
   ##   a `CountTableRef` from a collection
@@ -2634,7 +2598,7 @@ proc newCountTable*[A](initialSize = defaultInitialSize): <//>CountTableRef[A] =
 proc newCountTable*[A](keys: openArray[A]): <//>CountTableRef[A] =
   ## Creates a new ref count table with every member of a container ``keys``
   ## having a count of how many times it occurs in that container.
-  result = newCountTable[A](rightSize(keys.len))
+  result = newCountTable[A](keys.len)
   for key in items(keys): result.inc(key)
 
 proc `[]`*[A](t: CountTableRef[A], key: A): int =
@@ -2670,14 +2634,14 @@ proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
     doAssert a == newCountTable("aaabbbbbbbbbbb")
   t[].inc(key, val)
 
-proc smallest*[A](t: CountTableRef[A]): (A, int) =
+proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
   ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
   ##
   ## See also:
   ## * `largest proc<#largest,CountTableRef[A]>`_
   t[].smallest
 
-proc largest*[A](t: CountTableRef[A]): (A, int) =
+proc largest*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
   ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n)
   ##
   ## See also:
diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim
index d57e309e9..dbcb1b4a2 100644
--- a/lib/pure/colors.nim
+++ b/lib/pure/colors.nim
@@ -452,7 +452,7 @@ proc parseColor*(name: string): Color =
     assert parseColor(b) == Color(0x01_79_fc)
     doAssertRaises(ValueError): discard parseColor(c)
 
-  if name[0] == '#':
+  if name.len > 0 and name[0] == '#':
     result = Color(parseHexInt(name))
   else:
     var idx = binarySearch(colorNames, name, colorNameCmp)
@@ -472,6 +472,7 @@ proc isColor*(name: string): bool =
     assert b.isColor
     assert not c.isColor
 
+  if name.len == 0: return false
   if name[0] == '#':
     for i in 1 .. name.len-1:
       if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 2abcafb80..f0269f526 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -208,7 +208,7 @@ proc finished(fv: var FlowVarBaseObj) =
   # the worker thread waits for "data" to be set to nil before shutting down
   owner.data = nil
 
-proc `=destroy`[T](fv: var FlowVarObj[T]) = 
+proc `=destroy`[T](fv: var FlowVarObj[T]) =
   finished(fv)
   `=destroy`(fv.blob)
 
@@ -321,15 +321,15 @@ var
   currentPoolSize: int
   maxPoolSize = MaxThreadPoolSize
   minPoolSize = 4
-  gSomeReady : Semaphore
+  gSomeReady: Semaphore
   readyWorker: ptr Worker
 
 # A workaround for recursion deadlock issue
 # https://github.com/nim-lang/Nim/issues/4597
 var
   numSlavesLock: Lock
-  numSlavesRunning {.guard: numSlavesLock}: int
-  numSlavesWaiting {.guard: numSlavesLock}: int
+  numSlavesRunning {.guard: numSlavesLock.}: int
+  numSlavesWaiting {.guard: numSlavesLock.}: int
   isSlave {.threadvar.}: bool
 
 numSlavesLock.initLock
@@ -464,7 +464,7 @@ proc pinnedSpawn*(id: ThreadId; call: sink typed): void {.magic: "Spawn".}
   ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a
   ## return type that is either ``void`` or compatible with ``FlowVar[T]``.
 
-template spawnX*(call): void =
+template spawnX*(call) =
   ## Spawns a new task if a CPU core is ready, otherwise executes the
   ## call in the calling thread.
   ##
diff --git a/lib/pure/includes/decode_helpers.nim b/lib/pure/includes/decode_helpers.nim
new file mode 100644
index 000000000..74fe37d07
--- /dev/null
+++ b/lib/pure/includes/decode_helpers.nim
@@ -0,0 +1,24 @@
+# Include file that implements 'decodePercent' and friends. Do not import it!
+
+proc handleHexChar(c: char, x: var int, f: var bool) {.inline.} =
+  case c
+  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
+  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
+  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
+  else: f = true
+
+proc decodePercent(s: string, i: var int): char =
+  ## Converts `%xx` hexadecimal to the charracter with ordinal number `xx`.
+  ##
+  ## If `xx` is not a valid hexadecimal value, it is left intact: only the
+  ## leading `%` is returned as-is, and `xx` characters will be processed in the
+  ## next step (e.g. in `uri.decodeUrl`) as regular characters.
+  result = '%'
+  if i+2 < s.len:
+    var x = 0
+    var failed = false
+    handleHexChar(s[i+1], x, failed)
+    handleHexChar(s[i+2], x, failed)
+    if not failed:
+      result = chr(x)
+      inc(i, 2)
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index 2637fdf9d..fa6285a04 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -287,6 +287,7 @@ proc substituteLog*(frmt: string, level: Level,
   runnableExamples:
     doAssert substituteLog(defaultFmtStr, lvlInfo, "a message") == "INFO a message"
     doAssert substituteLog("$levelid - ", lvlError, "an error") == "E - an error"
+    doAssert substituteLog("$levelid", lvlDebug, "error") == "Derror"
   var msgLen = 0
   for arg in args:
     msgLen += arg.len
@@ -300,7 +301,7 @@ proc substituteLog*(frmt: string, level: Level,
       inc(i)
       var v = ""
       let app = when defined(js): "" else: getAppFilename()
-      while frmt[i] in IdentChars:
+      while i < frmt.len and frmt[i] in IdentChars:
         v.add(toLowerAscii(frmt[i]))
         inc(i)
       case v
@@ -364,7 +365,12 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
     let ln = substituteLog(logger.fmtStr, level, args)
     when defined(js):
       let cln: cstring = ln
-      {.emit: "console.log(`cln`);".}
+      case level
+      of lvlDebug: {.emit: "console.debug(`cln`);".}
+      of lvlInfo:  {.emit: "console.info(`cln`);".}
+      of lvlWarn:  {.emit: "console.warn(`cln`);".}
+      of lvlError: {.emit: "console.error(`cln`);".}
+      else:        {.emit: "console.log(`cln`);".}
     else:
       try:
         var handle = stdout
@@ -504,7 +510,6 @@ when not defined(js):
   # ------
 
   proc countLogLines(logger: RollingFileLogger): int =
-    result = 0
     let fp = open(logger.baseName, fmRead)
     for line in fp.lines():
       result.inc()
@@ -531,7 +536,7 @@ when not defined(js):
                             mode: FileMode = fmReadWrite,
                             levelThreshold = lvlAll,
                             fmtStr = defaultFmtStr,
-                            maxLines = 1000,
+                            maxLines: Positive = 1000,
                             bufSize: int = -1): RollingFileLogger =
     ## Creates a new `RollingFileLogger<#RollingFileLogger>`_.
     ##
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 67e24eedc..49a1192df 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -461,6 +461,40 @@ proc getAddrString*(sockAddr: ptr SockAddr): string =
         return "unix"
     raise newException(IOError, "Unknown socket family in getAddrString")
 
+proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) =
+  ## Stores in ``strAddress`` the string representation of the address inside
+  ## ``sockAddr``
+  ## 
+  ## **Note**
+  ## * ``strAddress`` must be initialized to 46 in length.
+  assert(46 == len(strAddress),
+         "`strAddress` was not initialized correctly. 46 != `len(strAddress)`")
+  if sockAddr.sa_family.cint == nativeAfInet:
+    let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr
+    when not useWinVersion:
+      if posix.inet_ntop(posix.AF_INET, addr4, addr strAddress[0],
+                         strAddress.len.int32) == nil:
+        raiseOSError(osLastError())
+    else:
+      if winlean.inet_ntop(winlean.AF_INET, addr4, addr strAddress[0],
+                           strAddress.len.int32) == nil:
+        raiseOSError(osLastError())
+  elif sockAddr.sa_family.cint == nativeAfInet6:
+    let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
+    when not useWinVersion:
+      if posix.inet_ntop(posix.AF_INET6, addr6, addr strAddress[0],
+                         strAddress.len.int32) == nil:
+        raiseOSError(osLastError())
+      if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
+        strAddress = strAddress.substr("::ffff:".len)
+    else:
+      if winlean.inet_ntop(winlean.AF_INET6, addr6, addr strAddress[0],
+                           strAddress.len.int32) == nil:
+        raiseOSError(osLastError())
+  else:
+    raise newException(IOError, "Unknown socket family in getAddrString")
+  setLen(strAddress, len(cstring(strAddress)))
+
 when defined(posix) and not defined(nimdoc):
   proc makeUnixAddr*(path: string): Sockaddr_un =
     result.sun_family = AF_UNIX.TSa_Family
@@ -622,7 +656,7 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
 proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
   ## When a socket in ``readfds`` is ready to be read from then a non-zero
   ## value will be returned specifying the count of the sockets which can be
-  ## read from. The sockets which can be read from will also be removed
+  ## read from. The sockets which cannot be read from will also be removed
   ## from ``readfds``.
   ##
   ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
@@ -644,7 +678,7 @@ proc selectWrite*(writefds: var seq[SocketHandle],
                   timeout = 500): int {.tags: [ReadIOEffect].} =
   ## When a socket in ``writefds`` is ready to be written to then a non-zero
   ## value will be returned specifying the count of the sockets which can be
-  ## written to. The sockets which can be written to will also be removed
+  ## written to. The sockets which cannot be written to will also be removed
   ## from ``writefds``.
   ##
   ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index c1896dc1c..7aeffbc35 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -513,10 +513,10 @@ when defineSsl:
 
   # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html
   proc loadCertificates(ctx: SslCtx, certFile, keyFile: string) =
-    if certFile != "" and not existsFile(certFile):
+    if certFile != "" and not fileExists(certFile):
       raise newException(system.IOError,
           "Certificate file could not be found: " & certFile)
-    if keyFile != "" and not existsFile(keyFile):
+    if keyFile != "" and not fileExists(keyFile):
       raise newException(system.IOError, "Key file could not be found: " & keyFile)
 
     if certFile != "":
@@ -614,7 +614,7 @@ when defineSsl:
       if verifyMode != CVerifyNone:
         # Use the caDir and caFile parameters if set
         if caDir != "" or caFile != "":
-          if newCTX.SSL_CTX_load_verify_locations(caDir, caFile) != 0:
+          if newCTX.SSL_CTX_load_verify_locations(caFile, caDir) != 0:
             raise newException(IOError, "Failed to load SSL/TLS CA certificate(s).")
 
         else:
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index dcb63458b..8bbceddeb 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -1097,14 +1097,14 @@ when defined(windows) and not weirdTarget:
     result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
              f.cFileName[1].int == dot and f.cFileName[2].int == 0)
 
-proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
+proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
                                           tags: [ReadDirEffect], noNimJs.} =
   ## Returns true if `filename` exists and is a regular file or symlink.
   ##
   ## Directories, device files, named pipes and sockets return false.
   ##
   ## See also:
-  ## * `existsDir proc <#existsDir,string>`_
+  ## * `dirExists proc <#dirExists,string>`_
   ## * `symlinkExists proc <#symlinkExists,string>`_
   when defined(windows):
     when useWinUnicode:
@@ -1117,13 +1117,13 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
     var res: Stat
     return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
 
-proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
+proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
                                      noNimJs.} =
   ## Returns true if the directory `dir` exists. If `dir` is a file, false
   ## is returned. Follows symlinks.
   ##
   ## See also:
-  ## * `existsFile proc <#existsFile,string>`_
+  ## * `fileExists proc <#fileExists,string>`_
   ## * `symlinkExists proc <#symlinkExists,string>`_
   when defined(windows):
     when useWinUnicode:
@@ -1143,8 +1143,8 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
   ## regardless of whether the link points to a directory or file.
   ##
   ## See also:
-  ## * `existsFile proc <#existsFile,string>`_
-  ## * `existsDir proc <#existsDir,string>`_
+  ## * `fileExists proc <#fileExists,string>`_
+  ## * `dirExists proc <#dirExists,string>`_
   when defined(windows):
     when useWinUnicode:
       wrapUnary(a, getFileAttributesW, link)
@@ -1156,21 +1156,14 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
     var res: Stat
     return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
 
-proc fileExists*(filename: string): bool {.inline, noNimJs.} =
-  ## Alias for `existsFile proc <#existsFile,string>`_.
-  ##
-  ## See also:
-  ## * `existsDir proc <#existsDir,string>`_
-  ## * `symlinkExists proc <#symlinkExists,string>`_
-  existsFile(filename)
 
-proc dirExists*(dir: string): bool {.inline, noNimJs.} =
-  ## Alias for `existsDir proc <#existsDir,string>`_.
-  ##
-  ## See also:
-  ## * `existsFile proc <#existsFile,string>`_
-  ## * `symlinkExists proc <#symlinkExists,string>`_
-  existsDir(dir)
+when not defined(nimscript):
+  when not defined(js): # `noNimJs` doesn't work with templates, this should improve.
+    template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} =
+      fileExists(args)
+    template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} =
+      dirExists(args)
+  # {.deprecated: [existsFile: fileExists].} # pending bug #14819; this would avoid above mentioned issue
 
 when not defined(windows) and not weirdTarget:
   proc checkSymlink(path: string): bool =
@@ -1200,7 +1193,7 @@ proc findExe*(exe: string, followSymlinks: bool = true;
   template checkCurrentDir() =
     for ext in extensions:
       result = addFileExt(exe, ext)
-      if existsFile(result): return
+      if fileExists(result): return
   when defined(posix):
     if '/' in exe: checkCurrentDir()
   else:
@@ -1216,7 +1209,7 @@ proc findExe*(exe: string, followSymlinks: bool = true;
       var x = expandTilde(candidate) / exe
     for ext in extensions:
       var x = addFileExt(x, ext)
-      if existsFile(x):
+      if fileExists(x):
         when not defined(windows):
           while followSymlinks: # doubles as if here
             if x.checkSymlink:
@@ -2027,7 +2020,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
     # way of retrieving the true filename
     for x in walkFiles(result):
       result = x
-    if not existsFile(result) and not existsDir(result):
+    if not fileExists(result) and not dirExists(result):
       # consider using: `raiseOSError(osLastError(), result)`
       raise newException(OSError, "file '" & result & "' does not exist")
   else:
@@ -2324,7 +2317,7 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
   result = not rawCreateDir(dir)
   if result:
     # path already exists - need to check that it is indeed a directory
-    if not existsDir(dir):
+    if not dirExists(dir):
       raise newException(IOError, "Failed to create '" & dir & "'")
 
 proc createDir*(dir: string) {.rtl, extern: "nos$1",
@@ -2893,7 +2886,7 @@ when not weirdTarget and defined(openbsd):
       # search in path
       for p in split(string(getEnv("PATH")), {PathSep}):
         var x = joinPath(p, exePath)
-        if existsFile(x):
+        if fileExists(x):
           return expandFilename(x)
     else:
       result = ""
@@ -2908,7 +2901,7 @@ when not (defined(windows) or defined(macosx) or weirdTarget):
         # iterate over any path in the $PATH environment variable
         for p in split(string(getEnv("PATH")), {PathSep}):
           var x = joinPath(p, result)
-          if existsFile(x): return x
+          if fileExists(x): return x
     else:
       result = ""
 
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 3e3391d52..5113695d8 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -18,7 +18,8 @@
 include "system/inclrtl"
 
 import
-  strutils, os, strtabs, streams, cpuinfo
+  strutils, os, strtabs, streams, cpuinfo, streamwrapper,
+  std/private/since
 
 export quoteShell, quoteShellWindows, quoteShellPosix
 
@@ -237,6 +238,10 @@ proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
 proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
   ## Returns ``p``'s output stream for reading from.
   ##
+  ## You cannot perform peek/write/setOption operations to this stream.
+  ## Use `peekableOutputStream proc <#peekableOutputStream,Process>`_
+  ## if you need to peek stream.
+  ##
   ## **WARNING**: The returned `Stream` should not be closed manually as it
   ## is closed when closing the Process ``p``.
   ##
@@ -247,6 +252,10 @@ proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
 proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
   ## Returns ``p``'s error stream for reading from.
   ##
+  ## You cannot perform peek/write/setOption operations to this stream.
+  ## Use `peekableErrorStream proc <#peekableErrorStream,Process>`_
+  ## if you need to peek stream.
+  ##
   ## **WARNING**: The returned `Stream` should not be closed manually as it
   ## is closed when closing the Process ``p``.
   ##
@@ -254,6 +263,30 @@ proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
   ## * `inputStream proc <#inputStream,Process>`_
   ## * `outputStream proc <#outputStream,Process>`_
 
+proc peekableOutputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [], since: (1, 3).}
+  ## Returns ``p``'s output stream for reading from.
+  ##
+  ## You can peek returned stream.
+  ##
+  ## **WARNING**: The returned `Stream` should not be closed manually as it
+  ## is closed when closing the Process ``p``.
+  ##
+  ## See also:
+  ## * `outputStream proc <#outputStream,Process>`_
+  ## * `peekableErrorStream proc <#peekableErrorStream,Process>`_
+
+proc peekableErrorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [], since: (1, 3).}
+  ## Returns ``p``'s error stream for reading from.
+  ##
+  ## You can run peek operation to returned stream.
+  ##
+  ## **WARNING**: The returned `Stream` should not be closed manually as it
+  ## is closed when closing the Process ``p``.
+  ##
+  ## See also:
+  ## * `errorStream proc <#errorStream,Process>`_
+  ## * `peekableOutputStream proc <#peekableOutputStream,Process>`_
+
 proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
   tags: [].} =
   ## Returns ``p``'s input file handle for writing to.
@@ -737,6 +770,18 @@ when defined(Windows) and not defined(useNimRtl):
       p.errStream = newFileHandleStream(p.errHandle)
     result = p.errStream
 
+  proc peekableOutputStream(p: Process): Stream =
+    streamAccess(p)
+    if p.outStream == nil:
+      p.outStream = newFileHandleStream(p.outHandle).newPipeOutStream
+    result = p.outStream
+
+  proc peekableErrorStream(p: Process): Stream =
+    streamAccess(p)
+    if p.errStream == nil:
+      p.errStream = newFileHandleStream(p.errHandle).newPipeOutStream
+    result = p.errStream
+
   proc execCmd(command: string): int =
     var
       si: STARTUPINFO
@@ -1360,28 +1405,40 @@ elif not defined(useNimRtl):
         p.exitStatus = status
         result = exitStatusLikeShell(status)
 
-  proc createStream(stream: var owned(Stream), handle: var FileHandle,
-                    fileMode: FileMode) =
+  proc createStream(handle: var FileHandle,
+                    fileMode: FileMode): owned FileStream =
     var f: File
     if not open(f, handle, fileMode): raiseOSError(osLastError())
-    stream = newFileStream(f)
+    return newFileStream(f)
 
   proc inputStream(p: Process): Stream =
     streamAccess(p)
     if p.inStream == nil:
-      createStream(p.inStream, p.inHandle, fmWrite)
+      p.inStream = createStream(p.inHandle, fmWrite)
     return p.inStream
 
   proc outputStream(p: Process): Stream =
     streamAccess(p)
     if p.outStream == nil:
-      createStream(p.outStream, p.outHandle, fmRead)
+      p.outStream = createStream(p.outHandle, fmRead)
     return p.outStream
 
   proc errorStream(p: Process): Stream =
     streamAccess(p)
     if p.errStream == nil:
-      createStream(p.errStream, p.errHandle, fmRead)
+      p.errStream = createStream(p.errHandle, fmRead)
+    return p.errStream
+
+  proc peekableOutputStream(p: Process): Stream =
+    streamAccess(p)
+    if p.outStream == nil:
+      p.outStream = createStream(p.outHandle, fmRead).newPipeOutStream
+    return p.outStream
+
+  proc peekableErrorStream(p: Process): Stream =
+    streamAccess(p)
+    if p.errStream == nil:
+      p.errStream = createStream(p.errHandle, fmRead).newPipeOutStream
     return p.errStream
 
   proc csystem(cmd: cstring): cint {.nodecl, importc: "system",
@@ -1446,6 +1503,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
   ## A convenience proc that runs the `command`, and returns its `output` and
   ## `exitCode`. `env` and `workingDir` params behave as for `startProcess`.
   ## If `input.len > 0`, it is passed as stdin.
+  ##
   ## Note: this could block if `input.len` is greater than your OS's maximum
   ## pipe buffer size.
   ##
@@ -1456,15 +1514,17 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
   ## * `execProcess proc
   ##   <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
   ##
-  runnableExamples:
-    var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4")
-    import strutils, strtabs
-    stripLineEnd(result[0]) ## portable way to remove trailing newline, if any
-    doAssert result == ("12", 0)
-    doAssert execCmdEx("ls --nonexistant").exitCode != 0
-    when defined(posix):
-      assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0)
-      assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0)
+  ## Example:
+  ##
+  ## .. code-block:: Nim
+  ##   var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4")
+  ##   import strutils, strtabs
+  ##   stripLineEnd(result[0]) ## portable way to remove trailing newline, if any
+  ##   doAssert result == ("12", 0)
+  ##   doAssert execCmdEx("ls --nonexistant").exitCode != 0
+  ##   when defined(posix):
+  ##     assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0)
+  ##     assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0)
 
   when (NimMajor, NimMinor, NimPatch) < (1, 3, 5):
     doAssert input.len == 0
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index a95a5b48d..94df5ea40 100644
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -280,8 +280,8 @@ proc handleShortOption(p: var OptParser; cmd: string) =
   while i < cmd.len and cmd[i] in {'\t', ' '}:
     inc(i)
     p.inShortState = false
-  if i < cmd.len and cmd[i] in {':', '='} or
-      card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal:
+  if i < cmd.len and (cmd[i] in {':', '='} or
+      card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal):
     if i < cmd.len and cmd[i] in {':', '='}:
       inc(i)
     p.inShortState = false
diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim
index 806316b02..d04244307 100644
--- a/lib/pure/ssl_certs.nim
+++ b/lib/pure/ssl_certs.nim
@@ -79,9 +79,9 @@ iterator scanSSLCertificates*(useEnvVars = false): string =
     when not defined(haiku):
       for p in certificate_paths:
         if p.endsWith(".pem") or p.endsWith(".crt"):
-          if existsFile(p):
+          if fileExists(p):
             yield p
-        elif existsDir(p):
+        elif dirExists(p):
           for fn in joinPath(p, "*").walkFiles():
             yield fn
     else:
diff --git a/lib/pure/streamwrapper.nim b/lib/pure/streamwrapper.nim
new file mode 100644
index 000000000..b99982f1b
--- /dev/null
+++ b/lib/pure/streamwrapper.nim
@@ -0,0 +1,117 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements stream wrapper.
+##
+## **Since** version 1.2.
+
+import deques, streams
+
+type
+  PipeOutStream*[T] = ref object of T
+    # When stream peek operation is called, it reads from base stream
+    # type using `baseReadDataImpl` and stores the content to this buffer.
+    # Next stream read operation returns data in the buffer so that previus peek
+    # operation looks like didn't changed read positon.
+    # When stream read operation that returns N byte data is called and the size is smaller than buffer size,
+    # first N elements are removed from buffer.
+    # Deque type can do such operation more efficiently than seq type.
+    buffer: Deque[char]
+    baseReadLineImpl: typeof(StreamObj.readLineImpl)
+    baseReadDataImpl: typeof(StreamObj.readDataImpl)
+
+proc posReadLine[T](s: Stream, line: var TaintedString): bool =
+  var s = PipeOutStream[T](s)
+  assert s.baseReadLineImpl != nil
+
+  let n = s.buffer.len
+  line.string.setLen(0)
+  for i in 0..<n:
+    var c = s.buffer.popFirst
+    if c == '\c':
+      c = readChar(s)
+      return true
+    elif c == '\L': return true
+    elif c == '\0':
+      return line.len > 0
+    line.string.add(c)
+
+  var line2: string
+  result = s.baseReadLineImpl(s, line2)
+  line.add line2
+
+proc posReadData[T](s: Stream, buffer: pointer, bufLen: int): int =
+  var s = PipeOutStream[T](s)
+  assert s.baseReadDataImpl != nil
+
+  let
+    dest = cast[ptr UncheckedArray[char]](buffer)
+    n = min(s.buffer.len, bufLen)
+  result = n
+  for i in 0..<n:
+    dest[i] = s.buffer.popFirst
+  if bufLen > n:
+    result += s.baseReadDataImpl(s, addr dest[n], bufLen - n)
+
+proc posReadDataStr[T](s: Stream, buffer: var string, slice: Slice[int]): int =
+  posReadData[T](s, addr buffer[slice.a], slice.len)
+
+proc posPeekData[T](s: Stream, buffer: pointer, bufLen: int): int =
+  var s = PipeOutStream[T](s)
+  assert s.baseReadDataImpl != nil
+
+  let
+    dest = cast[ptr UncheckedArray[char]](buffer)
+    n = min(s.buffer.len, bufLen)
+
+  result = n
+  for i in 0..<n:
+    dest[i] = s.buffer[i]
+
+  if bufLen > n:
+    let
+      newDataNeeded = bufLen - n
+      numRead = s.baseReadDataImpl(s, addr dest[n], newDataNeeded)
+    result += numRead
+    for i in 0..<numRead:
+      s.buffer.addLast dest[n + i]
+
+proc newPipeOutStream*[T](s: sink (ref T)): owned PipeOutStream[T] =
+  ## Wrap pipe for reading with PipeOutStream so that you can use peek* procs and generate runtime error
+  ## when setPosition/getPosition is called or write operation is performed.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: Nim
+  ##   import osproc, streamwrapper
+  ##   var
+  ##     p = startProcess(exePath)
+  ##     outStream = p.outputStream().newPipeOutStream()
+  ##   echo outStream.peekChar
+  ##   p.close()
+
+  assert s.readDataImpl != nil
+
+  new(result)
+  for dest, src in fields((ref T)(result)[], s[]):
+    dest = src
+  wasMoved(s[])
+  if result.readLineImpl != nil:
+    result.baseReadLineImpl = result.readLineImpl
+    result.readLineImpl = posReadLine[T]
+  result.baseReadDataImpl = result.readDataImpl
+  result.readDataImpl = posReadData[T]
+  result.readDataStrImpl = posReadDataStr[T]
+  result.peekDataImpl = posPeekData[T]
+
+  # Set nil to anything you may not call.
+  result.setPositionImpl = nil
+  result.getPositionImpl = nil
+  result.writeDataImpl = nil
+  result.flushImpl = nil
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim
index 5f7ef380d..c37b6b1c0 100644
--- a/lib/pure/strformat.nim
+++ b/lib/pure/strformat.nim
@@ -63,7 +63,6 @@ Formatting floats
 .. code-block:: nim
 
     import strformat
-
     doAssert fmt"{-12345:08}" == "-0012345"
     doAssert fmt"{-1:3}" == " -1"
     doAssert fmt"{-1:03}" == "-01"
@@ -81,6 +80,52 @@ Formatting floats
     doAssert fmt"{123.456:13e}" == " 1.234560e+02"
 
 
+Debugging strings
+=================
+
+``fmt"{expr=}"`` expands to ``fmt"expr={expr}"`` namely the text of the expression, 
+an equal sign and the results of evaluated expression.
+
+.. code-block:: nim
+
+    import strformat
+    doAssert fmt"{123.456=}" == "123.456=123.456"
+    doAssert fmt"{123.456=:>9.3f}" == "123.456=  123.456"
+
+    let x = "hello"
+    doAssert fmt"{x=}" == "x=hello" 
+    doAssert fmt"{x =}" == "x =hello"
+
+    let y = 3.1415926
+    doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}"
+    doAssert fmt"{y=}" == fmt"y={y}"
+    doAssert fmt"{y = : <8}" == fmt"y = 3.14159 "
+
+    proc hello(a: string, b: float): int = 12
+    let a = "hello"
+    let b = 3.1415926
+    doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12"
+    doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12"
+    doAssert fmt"{hello x, y = }" == "hello x, y = 12"
+
+
+Note that it is space sensitive:
+
+.. code-block:: nim
+
+    import strformat
+    let x = "12"
+    doAssert fmt"{x=}" == "x=12"
+    doAssert fmt"{x =:}" == "x =12"
+    doAssert fmt"{x =}" == "x =12"
+    doAssert fmt"{x= :}" == "x= 12"
+    doAssert fmt"{x= }" == "x= 12"
+    doAssert fmt"{x = :}" == "x = 12"
+    doAssert fmt"{x = }" == "x = 12"
+    doAssert fmt"{x   =  :}" == "x   =  12"
+    doAssert fmt"{x   =  }" == "x   =  12"
+
+
 Implementation details
 ======================
 
@@ -552,8 +597,18 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
 
         var subexpr = ""
         while i < f.len and f[i] != closeChar and f[i] != ':':
-          subexpr.add f[i]
-          inc i
+          if f[i] == '=':
+            let start = i
+            inc i
+            i += f.skipWhitespace(i)
+            if f[i] == closeChar or f[i] == ':':
+              result.add newCall(bindSym"add", res, newLit(subexpr & f[start ..< i]))
+            else:
+              subexpr.add f[start ..< i]
+          else:
+            subexpr.add f[i]
+            inc i
+
         var x: NimNode
         try:
           x = parseExpr(subexpr)
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 1ffc8bf22..32d6ede1e 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -2935,11 +2935,6 @@ proc isEmptyOrWhitespace*(s: string): bool {.noSideEffect, rtl,
   ## Checks if `s` is empty or consists entirely of whitespace characters.
   result = s.allCharsInSet(Whitespace)
 
-proc isNilOrWhitespace*(s: string): bool {.noSideEffect, rtl,
-    extern: "nsuIsNilOrWhitespace",
-    deprecated: "use isEmptyOrWhitespace instead".} =
-  ## Alias for isEmptyOrWhitespace
-  result = isEmptyOrWhitespace(s)
 
 when isMainModule:
   proc nonStaticTests =
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index e798bbaf1..99a333cc6 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -37,7 +37,7 @@ type
 
 var gTerm {.threadvar.}: owned(PTerminal)
 
-proc newTerminal(): owned(PTerminal) {.gcsafe.}
+proc newTerminal(): owned(PTerminal) {.gcsafe, raises: [].}
 
 proc getTerminal(): PTerminal {.inline.} =
   if isNil(gTerm):
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index 317376405..0e23077ac 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -93,6 +93,18 @@ since (1, 1):
   type StaticParam*[value: static type] = object
     ## used to wrap a static value in `genericParams`
 
+since (1, 3, 5):
+  template elementType*(a: untyped): typedesc =
+    ## return element type of `a`, which can be any iterable (over which you
+    ## can iterate)
+    runnableExamples:
+      iterator myiter(n: int): auto =
+        for i in 0..<n: yield i
+      doAssert elementType(@[1,2]) is int
+      doAssert elementType("asdf") is char
+      doAssert elementType(myiter(3)) is int
+    typeof(block: (for ai in a: ai))
+
 import std/macros
 
 macro genericParamsImpl(T: typedesc): untyped =
@@ -109,6 +121,9 @@ macro genericParamsImpl(T: typedesc): untyped =
       of nnkTypeDef:
         impl = impl[2]
         continue
+      of nnkTypeOfExpr:
+        impl = getTypeInst(impl[0])
+        continue
       of nnkBracketExpr:
         for i in 1..<impl.len:
           let ai = impl[i]
@@ -118,8 +133,32 @@ macro genericParamsImpl(T: typedesc): untyped =
             ret = ai
           of ntyStatic: doAssert false
           else:
-            since (1, 1):
-              ret = newTree(nnkBracketExpr, @[bindSym"StaticParam", ai])
+            # getType from a resolved symbol might return a typedesc symbol.
+            # If so, use it directly instead of wrapping it in StaticParam.
+            if (ai.kind == nnkSym and ai.symKind == nskType) or
+               (ai.kind == nnkBracketExpr and ai[0].kind == nnkSym and
+                ai[0].symKind == nskType):
+              ret = ai
+            elif ai.kind == nnkInfix and ai[0].kind == nnkIdent and
+                 ai[0].strVal == "..":
+              # For built-in array types, the "2" is translated to "0..1" then
+              # automagically translated to "range[0..1]". However this is not
+              # reflected in the AST, thus requiring manual transformation here.
+              #
+              # We will also be losing some context here:
+              #   var a: array[10, int]
+              # will be translated to:
+              #   var a: array[0..9, int]
+              # after typecheck. This means that we can't get the exact
+              # definition as typed by the user, which will cause confusion for
+              # users expecting:
+              #   genericParams(typeof(a)) is (StaticParam(10), int)
+              # to be true while in fact the result will be:
+              #   genericParams(typeof(a)) is (range[0..9], int)
+              ret = newTree(nnkBracketExpr, @[bindSym"range", ai])
+            else:
+              since (1, 1):
+                ret = newTree(nnkBracketExpr, @[bindSym"StaticParam", ai])
           result.add ret
         break
       else:
@@ -129,32 +168,20 @@ since (1, 1):
   template genericParams*(T: typedesc): untyped =
     ## return tuple of generic params for generic `T`
     runnableExamples:
-      type Foo[T1, T2]=object
+      type Foo[T1, T2] = object
       doAssert genericParams(Foo[float, string]) is (float, string)
       type Bar[N: static float, T] = object
       doAssert genericParams(Bar[1.0, string]) is (StaticParam[1.0], string)
       doAssert genericParams(Bar[1.0, string]).get(0).value == 1.0
+      doAssert genericParams(seq[Bar[2.0, string]]).get(0) is Bar[2.0, string]
+      var s: seq[Bar[3.0, string]]
+      doAssert genericParams(typeof(s)) is (Bar[3.0, string],)
+
+      # NOTE: For the builtin array type, the index generic param will
+      #       **always** become a range type after it's bound to a variable.
+      doAssert genericParams(array[10, int]) is (StaticParam[10], int)
+      var a: array[10, int]
+      doAssert genericParams(typeof(a)) is (range[0..9], int)
 
     type T2 = T
     genericParamsImpl(T2)
-
-when isMainModule:
-  static:
-    doAssert $type(42) == "int"
-    doAssert int.name == "int"
-
-  const a1 = name(int)
-  const a2 = $(int)
-  const a3 = $int
-  doAssert a1 == "int"
-  doAssert a2 == "int"
-  doAssert a3 == "int"
-
-  proc fun[T: typedesc](t: T) =
-    const a1 = name(t)
-    const a2 = $(t)
-    const a3 = $t
-    doAssert a1 == "int"
-    doAssert a2 == "int"
-    doAssert a3 == "int"
-  fun(int)
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index bea7d9c44..98be959e3 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -94,6 +94,7 @@
 ##     echo "suite teardown: run once after the tests"
 
 import std/private/since
+import std/exitprocs
 
 import
   macros, strutils, streams, times, sets, sequtils
@@ -498,7 +499,7 @@ template test*(name, body) {.dirty.} =
   ## .. code-block::
   ##
   ##  [OK] roses are red
-  bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded, exceptionTypeName
+  bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded, exceptionTypeName, setProgramResult
 
   ensureInitialized()
 
@@ -524,7 +525,7 @@ template test*(name, body) {.dirty.} =
 
     finally:
       if testStatusIMPL == TestStatus.FAILED:
-        programResult = 1
+        setProgramResult 1
       let testResult = TestResult(
         suiteName: when declared(testSuiteName): testSuiteName else: "",
         testName: name,
@@ -560,12 +561,11 @@ template fail* =
   ##  fail()
   ##
   ## outputs "Checkpoint A" before quitting.
-  bind ensureInitialized
-
+  bind ensureInitialized, setProgramResult
   when declared(testStatusIMPL):
     testStatusIMPL = TestStatus.FAILED
   else:
-    programResult = 1
+    setProgramResult 1
 
   ensureInitialized()
 
@@ -576,8 +576,7 @@ template fail* =
     else:
       formatter.failureOccurred(checkpoints, "")
 
-  when declared(programResult):
-    if abortOnError: quit(programResult)
+  if abortOnError: quit(1)
 
   checkpoints = @[]
 
@@ -679,7 +678,7 @@ macro check*(conditions: untyped): untyped =
     result = newNimNode(nnkStmtList)
     for node in checked:
       if node.kind != nnkCommentStmt:
-        result.add(newCall(!"check", node))
+        result.add(newCall(newIdentNode("check"), node))
 
   else:
     let lineinfo = newStrLitNode(checked.lineInfo)
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index b163a2ab4..04a9d97bd 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -47,6 +47,8 @@
 import std/private/since
 
 import strutils, parseutils, base64
+include includes/decode_helpers
+
 type
   Url* = distinct string
 
@@ -90,6 +92,7 @@ proc decodeUrl*(s: string, decodePlus = true): string =
   ## This means that any ``%xx`` (where ``xx`` denotes a hexadecimal
   ## value) are converted to the character with ordinal number ``xx``,
   ## and every other character is carried over.
+  ## If ``xx`` is not a valid hexadecimal value, it is left intact.
   ##
   ## As a special rule, when the value of ``decodePlus`` is true, ``+``
   ## characters are converted to a space.
@@ -101,12 +104,7 @@ proc decodeUrl*(s: string, decodePlus = true): string =
     assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis+is+a+test") == "https://nim-lang.org/this is a test"
     assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis%20is%20a%20test",
         false) == "https://nim-lang.org/this is a test"
-  proc handleHexChar(c: char, x: var int) {.inline.} =
-    case c
-    of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
-    of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
-    of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
-    else: assert(false)
+    assert decodeUrl("abc%xyz") == "abc%xyz"
 
   result = newString(s.len)
   var i = 0
@@ -114,11 +112,7 @@ proc decodeUrl*(s: string, decodePlus = true): string =
   while i < s.len:
     case s[i]
     of '%':
-      var x = 0
-      handleHexChar(s[i+1], x)
-      handleHexChar(s[i+2], x)
-      inc(i, 2)
-      result[j] = chr(x)
+      result[j] = decodePercent(s, i)
     of '+':
       if decodePlus:
         result[j] = ' '
diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim
index b2811735c..c6537f7f8 100644
--- a/lib/std/exitprocs.nim
+++ b/lib/std/exitprocs.nim
@@ -63,3 +63,25 @@ proc addExitProc*(cl: proc() {.noconv.}) =
   withLock gFunsLock:
     fun()
     gFuns.add Fun(kind: kNoconv, fun2: cl)
+
+when not defined(nimscript):
+  proc getProgramResult*(): int =
+    when defined(js) and defined(nodejs):
+      asm """
+`result` = process.exitCode;
+"""
+    elif not defined(js):
+      result = programResult
+    else:
+      doAssert false
+
+  proc setProgramResult*(a: int) =
+    # pending https://github.com/nim-lang/Nim/issues/14674
+    when defined(js) and defined(nodejs):
+      asm """
+process.exitCode = `a`;
+"""
+    elif not defined(js):
+      programResult = a
+    else:
+      doAssert false
diff --git a/lib/std/isolation.nim b/lib/std/isolation.nim
new file mode 100644
index 000000000..7ca5f0080
--- /dev/null
+++ b/lib/std/isolation.nim
@@ -0,0 +1,31 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the `Isolated[T]` type for
+## safe construction of isolated subgraphs that can be
+## passed efficiently to different channels and threads.
+
+type
+  Isolated*[T] = object ## Isolated data can only be moved, not copied.
+    value: T
+
+proc `=`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.}
+
+proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} =
+  # delegate to value's sink operation
+  `=sink`(dest.value, src.value)
+
+proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} =
+  # delegate to value's destroy operation
+  `=destroy`(dest.value)
+
+func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".}
+  ## Create an isolated subgraph from the expression `value`.
+  ## Please read https://github.com/nim-lang/RFCs/issues/244
+  ## for more details.
diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim
index be3d7e7c8..22f2a7a89 100644
--- a/lib/std/jsonutils.nim
+++ b/lib/std/jsonutils.nim
@@ -30,10 +30,59 @@ add a way to customize serialization, for eg:
 * handle cyclic references, using a cache of already visited addresses
 ]#
 
+import std/macros
+
 proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
 proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".}
 template distinctBase[T](a: T): untyped = distinctBase(type(a))(a)
 
+macro getDiscriminants(a: typedesc): seq[string] =
+  ## return the discriminant keys
+  # candidate for std/typetraits
+  var a = a.getTypeImpl
+  doAssert a.kind == nnkBracketExpr
+  let sym = a[1]
+  let t = sym.getTypeImpl
+  let t2 = t[2]
+  doAssert t2.kind == nnkRecList
+  result = newTree(nnkBracket)
+  for ti in t2:
+    if ti.kind == nnkRecCase:
+      let key = ti[0][0]
+      let typ = ti[0][1]
+      result.add newLit key.strVal
+  if result.len > 0:
+    result = quote do:
+      @`result`
+  else:
+    result = quote do:
+      seq[string].default
+
+macro initCaseObject(a: typedesc, fun: untyped): untyped =
+  ## does the minimum to construct a valid case object, only initializing
+  ## the discriminant fields; see also `getDiscriminants`
+  # maybe candidate for std/typetraits
+  var a = a.getTypeImpl
+  doAssert a.kind == nnkBracketExpr
+  let sym = a[1]
+  let t = sym.getTypeImpl
+  var t2: NimNode
+  case t.kind
+  of nnkObjectTy: t2 = t[2]
+  of nnkRefTy: t2 = t[0].getTypeImpl[2]
+  else: doAssert false, $t.kind # xxx `nnkPtrTy` could be handled too
+  doAssert t2.kind == nnkRecList
+  result = newTree(nnkObjConstr)
+  result.add sym
+  for ti in t2:
+    if ti.kind == nnkRecCase:
+      let key = ti[0][0]
+      let typ = ti[0][1]
+      let key2 = key.strVal
+      let val = quote do:
+        `fun`(`key2`, typedesc[`typ`])
+      result.add newTree(nnkExprColonExpr, key, val)
+
 proc checkJsonImpl(cond: bool, condStr: string, msg = "") =
   if not cond:
     # just pick 1 exception type for simplicity; other choices would be:
@@ -43,6 +92,19 @@ proc checkJsonImpl(cond: bool, condStr: string, msg = "") =
 template checkJson(cond: untyped, msg = "") =
   checkJsonImpl(cond, astToStr(cond), msg)
 
+template fromJsonFields(a, b, T, keys) =
+  checkJson b.kind == JObject, $(b.kind) # we could customize whether to allow JNull
+  var num = 0
+  for key, val in fieldPairs(a):
+    num.inc
+    when key notin keys:
+      if b.hasKey key:
+        fromJson(val, b[key])
+      else:
+        # we could customize to allow this
+        checkJson false, $($T, key, b)
+  checkJson b.len == num, $(b.len, num, $T, b) # could customize
+
 proc fromJson*[T](a: var T, b: JsonNode) =
   ## inplace version of `jsonTo`
   #[
@@ -85,25 +147,22 @@ proc fromJson*[T](a: var T, b: JsonNode) =
     a.setLen b.len
     for i, val in b.getElems:
       fromJson(a[i], val)
-  elif T is object | tuple:
-    const isNamed = T is object or isNamedTuple(T)
-    when isNamed:
-      checkJson b.kind == JObject, $(b.kind) # we could customize whether to allow JNull
-      var num = 0
-      for key, val in fieldPairs(a):
-        num.inc
-        if b.hasKey key:
-          fromJson(val, b[key])
-        else:
-          # we could customize to allow this
-          checkJson false, $($T, key, b)
-      checkJson b.len == num, $(b.len, num, $T, b) # could customize
+  elif T is object:
+    template fun(key, typ): untyped =
+      jsonTo(b[key], typ)
+    a = initCaseObject(T, fun)
+    const keys = getDiscriminants(T)
+    fromJsonFields(a, b, T, keys)
+  elif T is tuple:
+    when isNamedTuple(T):
+      fromJsonFields(a, b, T, seq[string].default)
     else:
       checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull
       var i = 0
       for val in fields(a):
         fromJson(val, b[i])
         i.inc
+      checkJson b.len == i, $(b.len, i, $T, b) # could customize
   else:
     # checkJson not appropriate here
     static: doAssert false, "not yet implemented: " & $T
@@ -120,8 +179,7 @@ proc toJson*[T](a: T): JsonNode =
     result = newJObject()
     for k, v in pairs(a): result[k] = toJson(v)
   elif T is object | tuple:
-    const isNamed = T is object or isNamedTuple(T)
-    when isNamed:
+    when T is object or isNamedTuple(T):
       result = newJObject()
       for k, v in a.fieldPairs: result[k] = toJson(v)
     else:
diff --git a/lib/std/private/nimbleutils.nim b/lib/std/private/nimbleutils.nim
deleted file mode 100644
index 297fb9695..000000000
--- a/lib/std/private/nimbleutils.nim
+++ /dev/null
@@ -1,32 +0,0 @@
-##[
-internal API for now, API subject to change
-]##
-
-import std/[os,osproc,sugar,strutils]
-
-proc actionRetry*(maxRetry: int, backoffDuration: float, action: proc(): bool): bool =
-  ## retry `action` up to `maxRetry` times with exponential backoff and initial
-  ## duraton of `backoffDuration` seconds
-  var t = backoffDuration
-  for i in 0..<maxRetry:
-    if action(): return true
-    if i == maxRetry - 1: break
-    sleep(int(t * 1000))
-    t = t * 2 # exponential backoff
-  return false
-
-proc nimbleInstall*(name: string, message: var string): bool =
-  let cmd = "nimble install -y " & name
-  let (outp, status) = execCmdEx(cmd)
-  if status != 0:
-    message = "'$1' failed:\n$2" % [cmd, outp]
-    result = false
-  else: result = true
-
-when isMainModule:
-  block:
-    var msg: string
-    let ok = actionRetry(maxRetry = 2, backoffDuration = 0.1):
-      (proc(): bool = nimbleInstall("nonexistant", msg))
-    doAssert "Package not found" in msg
-    doAssert not ok
diff --git a/lib/system.nim b/lib/system.nim
index 822454626..71eed5bee 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1127,12 +1127,9 @@ const
     ## is the value that should be passed to `quit <#quit,int>`_ to indicate
     ## failure.
 
-when defined(js) and defined(nodejs) and not defined(nimscript):
-  var programResult* {.importc: "process.exitCode".}: int
-  programResult = 0
-elif hostOS != "standalone":
+when not defined(js) and hostOS != "standalone":
   var programResult* {.compilerproc, exportc: "nim_program_result".}: int
-    ## deprecated, prefer ``quit``
+    ## deprecated, prefer `quit` or `exitprocs.getProgramResult`, `exitprocs.setProgramResult`.
 
 import std/private/since
 
diff --git a/lib/system/io.nim b/lib/system/io.nim
index 9f12a1767..3d5cf981a 100644
--- a/lib/system/io.nim
+++ b/lib/system/io.nim
@@ -282,7 +282,11 @@ elif defined(windows):
   proc getOsfhandle(fd: cint): int {.
     importc: "_get_osfhandle", header: "<io.h>".}
 
-  proc setHandleInformation(handle: int, mask, flags: culong): cint {.
+  type
+    IoHandle = distinct pointer
+      ## Windows' HANDLE type. Defined as an untyped pointer but is **not**
+      ## one. Named like this to avoid collision with other `system` modules.
+  proc setHandleInformation(handle: IoHandle, mask, flags: culong): cint {.
     importc: "SetHandleInformation", header: "<handleapi.h>".}
 
 const
@@ -339,7 +343,8 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w
       flags = if inheritable: flags and not FD_CLOEXEC else: flags or FD_CLOEXEC
       result = c_fcntl(f, F_SETFD, flags) != -1
     else:
-      result = setHandleInformation(f.int, HANDLE_FLAG_INHERIT, culong inheritable) != 0
+      result = setHandleInformation(cast[IoHandle](f), HANDLE_FLAG_INHERIT,
+                                    culong inheritable) != 0
 
 proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect],
               benign.} =
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index ceaaef013..9e19fcfc4 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -136,13 +136,13 @@ proc dirExists*(dir: string): bool {.
   ## Checks if the directory `dir` exists.
   builtin
 
-proc existsFile*(filename: string): bool =
-  ## An alias for ``fileExists``.
-  fileExists(filename)
+template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} =
+  # xxx: warning won't be shown for nimsscript because of current logic handling
+  # `foreignPackageNotes`
+  fileExists(args)
 
-proc existsDir*(dir: string): bool =
-  ## An alias for ``dirExists``.
-  dirExists(dir)
+template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} =
+  dirExists(args)
 
 proc selfExe*(): string =
   ## Returns the currently running nim or nimble executable.
diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim
index fcc187a42..d456f4454 100644
--- a/lib/system/repr_v2.nim
+++ b/lib/system/repr_v2.nim
@@ -29,21 +29,35 @@ proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
 
 proc repr*(x: char): string {.noSideEffect.} =
   ## repr for a character argument. Returns `x`
-  ## converted to a string.
+  ## converted to an escaped string.
   ##
   ## .. code-block:: Nim
   ##   assert repr('c') == "'c'"
-  '\'' & $x & '\''
-
-proc repr*(x: cstring): string {.noSideEffect.} =
-  ## repr for a CString argument. Returns `x`
-  ## converted to a quoted string.
-  '"' & $x & '"'
+  result.add '\''
+  # Elides string creations if not needed
+  if x in {'\\', '\0'..'\31', '\127'..'\255'}:
+    result.add '\\'
+  if x in {'\0'..'\31', '\127'..'\255'}:
+    result.add $x.uint8
+  else:
+    result.add x
+  result.add '\''
 
-proc repr*(x: string): string {.noSideEffect.} =
+proc repr*(x: string | cstring): string {.noSideEffect.} =
   ## repr for a string argument. Returns `x`
-  ## but quoted.
-  '"' & x & '"'
+  ## converted to a quoted and escaped string.
+  result.add '\"'
+  for i in 0..<x.len:
+    if x[i] in {'"', '\\', '\0'..'\31', '\127'..'\255'}:
+      result.add '\\'
+    case x[i]:
+    of '\n':
+      result.add "n\n"
+    of '\0'..'\9', '\11'..'\31', '\127'..'\255':
+      result.add $x[i].uint8
+    else:
+      result.add x[i]
+  result.add '\"'
 
 proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.}
   ## repr for an enumeration argument. This works for
@@ -68,6 +82,10 @@ proc repr*(p: pointer): string =
         result[j] = HexChars[n and 0xF]
         n = n shr 4
 
+proc repr*(p: proc): string =
+  ## repr of a proc as its address
+  repr(cast[pointer](p))
+
 template repr*(x: distinct): string =
   repr(distinctBase(typeof(x))(x))
 
diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim
index 1b40c00ab..3c94a03f9 100644
--- a/lib/system/seqs_v2.nim
+++ b/lib/system/seqs_v2.nim
@@ -73,16 +73,16 @@ proc shrink*[T](x: var seq[T]; newLen: Natural) =
   when nimvm:
     setLen(x, newLen)
   else:
-    mixin `=destroy`
     #sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
     when not supportsCopyMem(T):
       for i in countdown(x.len - 1, newLen):
-        `=destroy`(x[i])
+        reset x[i]
     # XXX This is wrong for const seqs that were moved into 'x'!
     cast[ptr NimSeqV2[T]](addr x).len = newLen
 
 proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
   let oldLen = x.len
+  #sysAssert newLen >= x.len, "invalid newLen parameter for 'grow'"
   if newLen <= oldLen: return
   var xu = cast[ptr NimSeqV2[T]](addr x)
   if xu.p == nil or xu.p.cap < newLen:
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index cb26833ac..43a769b5f 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -66,10 +66,6 @@ proc addInt*(result: var string; x: int64) =
   for j in 0..i div 2 - 1:
     swap(result[base+j], result[base+i-j-1])
 
-proc add*(result: var string; x: int64) {.deprecated:
-  "Deprecated since v0.20, use 'addInt'".} =
-  addInt(result, x)
-
 proc nimIntToStr(x: int): string {.compilerRtl.} =
   result = newStringOfCap(sizeof(x)*4)
   result.addInt x
@@ -98,10 +94,6 @@ proc addFloat*(result: var string; x: float) =
     let n = writeFloatToBuffer(buffer, x)
     result.addCstringN(cstring(buffer[0].addr), n)
 
-proc add*(result: var string; x: float) {.deprecated:
-  "Deprecated since v0.20, use 'addFloat'".} =
-  addFloat(result, x)
-
 proc nimFloatToStr(f: float): string {.compilerproc.} =
   result = newStringOfCap(8)
   result.addFloat f
diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim
index aa644522f..85c0af190 100644
--- a/lib/system/strs_v2.nim
+++ b/lib/system/strs_v2.nim
@@ -132,10 +132,13 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
     a.len = b.len
     copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
 
-proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl.} =
+proc nimPrepareStrMutationImpl(s: var NimStringV2) =
+  let oldP = s.p
+  # can't mutate a literal, so we need a fresh copy here:
+  s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len)))
+  s.p.cap = s.len
+  copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)
+
+proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inline.} =
   if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag:
-    let oldP = s.p
-    # can't mutate a literal, so we need a fresh copy here:
-    s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len)))
-    s.p.cap = s.len
-    copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)
+    nimPrepareStrMutationImpl(s)
diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim
index aabcbdc90..83c11eb79 100644
--- a/lib/system/widestrs.nim
+++ b/lib/system/widestrs.nim
@@ -28,7 +28,6 @@ when defined(nimv2):
   proc `=destroy`(a: var WideCStringObj) =
     if a.data != nil:
       deallocShared(a.data)
-      a.data = nil
 
   proc `=`(a: var WideCStringObj; b: WideCStringObj) {.error.}
 
diff --git a/lib/system_overview.rst b/lib/system_overview.rst
index 23284dabf..f0945873c 100644
--- a/lib/system_overview.rst
+++ b/lib/system_overview.rst
@@ -45,24 +45,27 @@ Proc                              Usage
 Seqs
 ----
 
-========================================   ==========================================
-Proc                                       Usage
-========================================   ==========================================
-`newSeq<#newSeq>`_                         Create a new sequence of a given length
-`newSeqOfCap<#newSeqOfCap,Natural>`_       Create a new sequence with zero length
-                                           and a given capacity
-`setLen<#setLen,seq[T][T],Natural>`_       Set the length of a sequence
-`len<#len,seq[T][T]>`_                     Return the length of a sequence
-`@<#@,array[IDX,T]>`_                      Turn an array into a sequence
-`add<#add,seq[T][T],T>`_                   Add an item to the sequence
-`insert<#insert,seq[T][T],T>`_             Insert an item at a specific position
-`delete<#delete,seq[T][T],Natural>`_       Delete an item while preserving the
-                                           order of elements (`O(n)` operation)
-`del<#del,seq[T][T],Natural>`_             `O(1)` removal, doesn't preserve the order
-`pop<#pop,seq[T][T]>`_                     Remove and return last item of a sequence
-`x & y<#&,seq[T][T],seq[T][T]>`_           Concatenate two sequences
-`x[a..b]<#[],openArray[T],HSlice[U,V]>`_   Slice of a sequence (both ends included)
-========================================   ==========================================
+===========================================   ==========================================
+Proc                                          Usage
+===========================================   ==========================================
+`newSeq<#newSeq>`_                            Create a new sequence of a given length
+`newSeqOfCap<#newSeqOfCap,Natural>`_          Create a new sequence with zero length
+                                              and a given capacity
+`setLen<#setLen,seq[T][T],Natural>`_          Set the length of a sequence
+`len<#len,seq[T][T]>`_                        Return the length of a sequence
+`@<#@,array[IDX,T]>`_                         Turn an array into a sequence
+`add<#add,seq[T][T],T>`_                      Add an item to the sequence
+`insert<#insert,seq[T][T],T>`_                Insert an item at a specific position
+`delete<#delete,seq[T][T],Natural>`_          Delete an item while preserving the
+                                              order of elements (`O(n)` operation)
+`del<#del,seq[T][T],Natural>`_                `O(1)` removal, doesn't preserve the order
+`pop<#pop,seq[T][T]>`_                        Remove and return last item of a sequence
+`x & y<#&,seq[T][T],seq[T][T]>`_              Concatenate two sequences
+`x[a..b]<#[],openArray[T],HSlice[U,V]>`_      Slice of a sequence (both ends included)
+`x[a..^b]<#[],openArray[T],HSlice[U,V]>`_     Slice of a sequence but `b` is a 
+                                              reversed index (both ends included)
+`x[a..\<b]<#[],openArray[T],HSlice[U,V]>`_    Slice of a sequence (excluded upper bound)
+===========================================   ==========================================
 
 **See also:**
 * `sequtils module <sequtils.html>`_ for operations on container
@@ -160,8 +163,10 @@ Proc                                           Usage
 `T or F<#or,bool,bool>`_                       Boolean `or`
 `T xor F<#xor,bool,bool>`_                     Boolean `xor` (exclusive or)
 `not T<#not,bool>`_                            Boolean `not`
+`a[^x]<#^.t,int>`_                             Take the element at the reversed index `x`
 `a .. b<#..,T,U>`_                             Binary slice that constructs an interval
                                                `[a, b]`
+`a ..^ b<#..^.t,untyped,untyped>`_             Interval `[a, b]` but `b` as reversed index
 [a ..< b](#..<.t,untyped,untyped)              Interval `[a, b)` (excluded upper bound)
 [runnableExamples](#runnableExamples,untyped)  Create testable documentation
 =============================================  ============================================
diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim
index 393f29ad0..7836f087d 100644
--- a/lib/wrappers/odbcsql.nim
+++ b/lib/wrappers/odbcsql.nim
@@ -47,8 +47,8 @@ type
   SqlHDesc* = SqlHandle
   TSqlInteger* = int32
   SqlUInteger* = int32
-  TSqlLen* = int64
-  TSqlULen* = uint64
+  TSqlLen* = int
+  TSqlULen* = uint
   SqlPointer* = pointer
   TSqlReal* = cfloat
   TSqlDouble* = cdouble