summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim6
-rw-r--r--compiler/vm.nim28
-rw-r--r--compiler/vmdef.nim3
-rw-r--r--compiler/vmgen.nim6
-rw-r--r--lib/impure/db_postgres.nim11
-rw-r--r--lib/pure/asynchttpserver.nim7
-rw-r--r--lib/pure/collections/critbits.nim65
-rw-r--r--lib/pure/collections/intsets.nim25
-rw-r--r--lib/pure/collections/sets.nim40
-rw-r--r--lib/pure/json.nim29
-rw-r--r--lib/pure/memfiles.nim2
-rw-r--r--lib/pure/strutils.nim3
-rw-r--r--lib/system.nim15
-rw-r--r--nimsuggest/nimsuggest.nim2
-rw-r--r--tests/sets/tsets2.nim12
-rw-r--r--tests/stdlib/tmarshal.nim2
-rw-r--r--tests/stdlib/tstrutil.nim12
-rw-r--r--tests/vm/tstring_openarray.nim34
18 files changed, 229 insertions, 73 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 0cc4daf22..f9b43ed13 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1009,6 +1009,12 @@ proc safeLen*(n: PNode): int {.inline.} =
   if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
   else: result = len(n.sons)
 
+proc safeArrLen*(n: PNode): int {.inline.} =
+  ## works for array-like objects (strings passed as openArray in VM).
+  if n.kind in {nkStrLit..nkTripleStrLit}:result = len(n.strVal)
+  elif n.kind in {nkNone..nkFloat128Lit}: result = 0
+  else: result = len(n)
+
 proc add*(father, son: PNode) =
   assert son != nil
   if isNil(father.sons): father.sons = @[]
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 93cf66c05..8d4359db9 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -508,7 +508,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, errIndexOutOfBounds)
       let idx = regs[rc].intVal.int
       let src = regs[rb].node
-      if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len:
+      if src.kind in {nkStrLit..nkTripleStrLit}:
+        if idx <% src.strVal.len:
+          regs[ra].node = newNodeI(nkCharLit, c.debug[pc])
+          regs[ra].node.intVal = src.strVal[idx].ord
+        else:
+          stackTrace(c, tos, pc, errIndexOutOfBounds)
+      elif src.kind notin {nkEmpty..nkFloat128Lit} and idx <% src.len:
         regs[ra].node = src.sons[idx]
       else:
         stackTrace(c, tos, pc, errIndexOutOfBounds)
@@ -526,8 +532,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       # a[b] = c
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
-      if idx <% regs[ra].node.len:
-        putIntoNode(regs[ra].node.sons[idx], regs[rc])
+      let arr = regs[ra].node
+      if arr.kind in {nkStrLit..nkTripleStrLit}:
+        if idx <% arr.strVal.len:
+          arr.strVal[idx] = chr(regs[rc].intVal)
+        else:
+          stackTrace(c, tos, pc, errIndexOutOfBounds)
+      elif idx <% arr.len:
+        putIntoNode(arr.sons[idx], regs[rc])
       else:
         stackTrace(c, tos, pc, errIndexOutOfBounds)
     of opcLdObj:
@@ -641,8 +653,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLenSeq:
       decodeBImm(rkInt)
       #assert regs[rb].kind == nkBracket
-      # also used by mNLen:
-      regs[ra].intVal = regs[rb].node.safeLen - imm
+      let high = (imm and 1) # discard flags
+      if (imm and nimNodeFlag) != 0:
+        # used by mNLen (NimNode.len)
+        regs[ra].intVal = regs[rb].node.safeLen - high
+      else:
+        # safeArrLen also return string node len
+        # used when string is passed as openArray in VM
+        regs[ra].intVal = regs[rb].node.safeArrLen - high
     of opcLenStr:
       decodeBImm(rkInt)
       assert regs[rb].kind == rkNode
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 7e1309e0a..5395d4bad 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -234,6 +234,9 @@ const
   slotSomeTemp* = slotTempUnknown
   relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
 
+# flag is used to signal opcSeqLen if node is NimNode.
+const nimNodeFlag* = 16
+
 template opcode*(x: TInstr): TOpcode = TOpcode(x.uint32 and 0xff'u32)
 template regA*(x: TInstr): TRegister = TRegister(x.uint32 shr 8'u32 and 0xff'u32)
 template regB*(x: TInstr): TRegister = TRegister(x.uint32 shr 16'u32 and 0xff'u32)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index dbb8c9dcd..3d291d8a2 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -625,10 +625,10 @@ proc genUnaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   c.gABC(n, opc, dest, tmp)
   c.freeTemp(tmp)
 
-proc genUnaryABI(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
+proc genUnaryABI(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) =
   let tmp = c.genx(n.sons[1])
   if dest < 0: dest = c.getTemp(n.typ)
-  c.gABI(n, opc, dest, tmp, 0)
+  c.gABI(n, opc, dest, tmp, imm)
   c.freeTemp(tmp)
 
 proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
@@ -1021,7 +1021,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(tmp)
   of mSlurp: genUnaryABC(c, n, dest, opcSlurp)
   of mStaticExec: genBinaryABCD(c, n, dest, opcGorge)
-  of mNLen: genUnaryABI(c, n, dest, opcLenSeq)
+  of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag)
   of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
   of mNChild: genBinaryABC(c, n, dest, opcNChild)
   of mNSetChild, mNDel:
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index fc587b5df..a42950557 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -516,10 +516,13 @@ proc open*(connection, user, password, database: string): DbConn {.
   ##
   ## See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
   ## for more information.
-  ##
-  ## Note that the connection parameter is not used but exists to maintain
-  ## the nim db api.
-  result = pqsetdbLogin(nil, nil, nil, nil, database, user, password)
+  let
+    colonPos = connection.find(':')
+    host = if colonPos < 0: connection
+           else: substr(connection, 0, colonPos-1)
+    port = if colonPos < 0: ""
+           else: substr(connection, colonPos+1)
+  result = pqsetdbLogin(host, port, nil, nil, database, user, password)
   if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
 
 proc setEncoding*(connection: DbConn, encoding: string): bool {.
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index b7b57a82f..6d4b85145 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -178,7 +178,12 @@ proc processClient(client: AsyncSocket, address: string,
         except ValueError:
           asyncCheck request.respondError(Http400)
           continue
-      of 1: parseUri(linePart, request.url)
+      of 1: 
+        try:
+          parseUri(linePart, request.url)
+        except ValueError:
+          asyncCheck request.respondError(Http400) 
+          continue
       of 2:
         try:
           request.protocol = parseProtocol(linePart)
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index 5f84f3101..f70a12843 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -110,6 +110,42 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
     wherep[] = inner
   inc c.count
 
+proc exclImpl[T](c: var CritBitTree[T], key: string) : int =
+  var p = c.root
+  var wherep = addr(c.root)
+  var whereq: ptr Node[T] = nil
+  if p == nil: return c.count
+  var dir = 0
+  var q: Node[T]
+  while not p.isLeaf:
+    whereq = wherep
+    q = p
+    let ch = if p.byte < key.len: key[p.byte] else: '\0'
+    dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
+    wherep = addr(p.child[dir])
+    p = wherep[]
+  if p.key == key:
+    # else: not in tree at all
+    if whereq == nil:
+      c.root = nil
+    else:
+      whereq[] = q.child[1 - dir]
+    dec c.count
+
+  return c.count
+
+proc excl*[T](c: var CritBitTree[T], key: string) =
+  ## removes `key` (and its associated value) from the set `c`.
+  ## If the `key` does not exist, nothing happens.
+  discard exclImpl(c, key)
+
+proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool =
+  ## Returns true iff `c` does not contain the given `key`. If the key
+  ## does exist, c.excl(key) is performed. 
+  let oldCount = c.count 
+  var n = exclImpl(c, key)
+  result = c.count == oldCount
+
 proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool =
   ## returns true iff `c` contains the given `key`. If the key does not exist
   ## ``c[key] = val`` is performed.
@@ -171,30 +207,6 @@ proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline, deprecated.}
   ## Use ```[]``` instead.
   get(c, key)
 
-proc excl*[T](c: var CritBitTree[T], key: string) =
-  ## removes `key` (and its associated value) from the set `c`.
-  ## If the `key` does not exist, nothing happens.
-  var p = c.root
-  var wherep = addr(c.root)
-  var whereq: ptr Node[T] = nil
-  if p == nil: return
-  var dir = 0
-  var q: Node[T]
-  while not p.isLeaf:
-    whereq = wherep
-    q = p
-    let ch = if p.byte < key.len: key[p.byte] else: '\0'
-    dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
-    wherep = addr(p.child[dir])
-    p = wherep[]
-  if p.key == key:
-    # else: not in tree at all
-    if whereq == nil:
-      c.root = nil
-    else:
-      whereq[] = q.child[1 - dir]
-    dec c.count
-
 iterator leaves[T](n: Node[T]): Node[T] =
   if n != nil:
     # XXX actually we could compute the necessary stack size in advance:
@@ -326,10 +338,15 @@ when isMainModule:
   r.incl "def"
   r.incl "definition"
   r.incl "prefix"
+  r.incl "foo"
 
   doAssert r.contains"def"
 
   r.excl "def"
+  assert r.missingOrExcl("foo") == false
+  assert "foo" notin toSeq(r.items)
+
+  assert r.missingOrExcl("foo") == true
 
   assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
 
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index 334e33f2e..085232564 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -131,8 +131,7 @@ proc incl*(s: var IntSet, key: int) =
     # fall through:
   bitincl(s, key)
 
-proc excl*(s: var IntSet, key: int) =
-  ## excludes `key` from the set `s`.
+proc exclImpl(s: var IntSet, key: int) =
   if s.elems <= s.a.len:
     for i in 0..<s.elems:
       if s.a[i] == key:
@@ -146,6 +145,17 @@ proc excl*(s: var IntSet, key: int) =
       t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and
           not `shl`(1, u and IntMask)
 
+proc excl*(s: var IntSet, key: int) =
+  ## excludes `key` from the set `s`.
+  exclImpl(s, key)
+
+proc missingOrExcl*(s: var IntSet, key: int) : bool =
+  ## returns true if `s` does not contain `key`, otherwise
+  ## `key` is removed from `s` and false is returned.
+  var count = s.elems
+  exclImpl(s, key)
+  result = count == s.elems 
+
 proc containsOrIncl*(s: var IntSet, key: int): bool =
   ## returns true if `s` contains `key`, otherwise `key` is included in `s`
   ## and false is returned.
@@ -270,6 +280,17 @@ when isMainModule:
   x.incl(7)
   x.incl(1056)
 
+  x.incl(1044)
+  x.excl(1044) 
+
+  assert x.containsOrIncl(888) == false
+  assert 888 in x
+  assert x.containsOrIncl(888) == true
+
+  assert x.missingOrExcl(888) == false
+  assert 888 notin x
+  assert x.missingOrExcl(888) == true
+
   var xs = toSeq(items(x))
   xs.sort(cmp[int])
   assert xs == @[1, 2, 7, 1056]
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index c0ffcb19c..d51a5c388 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -278,21 +278,15 @@ template default[T](t: typedesc[T]): T =
   var v: T
   v
 
-proc excl*[A](s: var HashSet[A], key: A) =
-  ## Excludes `key` from the set `s`.
-  ##
-  ## This doesn't do anything if `key` is not found in `s`. Example:
-  ##
-  ## .. code-block::
-  ##   var s = toSet([2, 3, 6, 7])
-  ##   s.excl(2)
-  ##   s.excl(2)
-  ##   assert s.len == 3
+proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
   assert s.isValid, "The set needs to be initialized."
   var hc: Hash
   var i = rawGet(s, key, hc)
   var msk = high(s.data)
+  result = true
+
   if i >= 0:
+    result = false
     s.data[i].hcode = 0
     s.data[i].key = default(type(s.data[i].key))
     dec(s.counter)
@@ -308,6 +302,30 @@ proc excl*[A](s: var HashSet[A], key: A) =
         r = s.data[i].hcode and msk    # "home" location of key@i
       shallowCopy(s.data[j], s.data[i]) # data[j] will be marked EMPTY next loop
 
+proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
+  ## Excludes `key` in the set `s` and tells if `key` was removed from `s`.
+  ##
+  ## The difference with regards to the `excl() <#excl,TSet[A],A>`_ proc is
+  ## that this proc returns `true` if `key` was not present in `s`. Example:
+  ##
+  ## .. code-block::
+  ##  var s = toSet([2, 3, 6, 7])
+  ##  assert s.missingOrExcl(4) == true
+  ##  assert s.missingOrExcl(6) == false
+  exclImpl(s, key)
+
+proc excl*[A](s: var HashSet[A], key: A) =
+  ## Excludes `key` from the set `s`.
+  ##
+  ## This doesn't do anything if `key` is not found in `s`. Example:
+  ##
+  ## .. code-block::
+  ##   var s = toSet([2, 3, 6, 7])
+  ##   s.excl(2)
+  ##   s.excl(2)
+  ##   assert s.len == 3
+  discard exclImpl(s, key)
+
 proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
   ## Excludes everything in `other` from `s`.
   ##
@@ -322,7 +340,7 @@ proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
   ##   # --> {1, 3, 5}
   assert s.isValid, "The set `s` needs to be initialized."
   assert other.isValid, "The set `other` needs to be initialized."
-  for item in other: excl(s, item)
+  for item in other: discard exclImpl(s, item)
 
 proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
   ## Includes `key` in the set `s` and tells if `key` was added to `s`.
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 9dc9b51f3..097952588 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -996,24 +996,17 @@ proc nl(s: var string, ml: bool) =
 proc escapeJson*(s: string; result: var string) =
   ## Converts a string `s` to its JSON representation.
   ## Appends to ``result``.
-  const
-    HexChars = "0123456789ABCDEF"
   result.add("\"")
-  for x in runes(s):
-    var r = int(x)
-    if r >= 32 and r <= 126:
-      var c = chr(r)
-      case c
-      of '"': result.add("\\\"")
-      of '\\': result.add("\\\\")
-      else: result.add(c)
-    else:
-      # toHex inlined for more speed (saves stupid string allocations):
-      result.add("\\u0000")
-      let start = result.len - 4
-      for j in countdown(3, 0):
-        result[j+start] = HexChars[r and 0xF]
-        r = r shr 4
+  for c in s:
+    case c
+    of '\L': result.add("\\n")
+    of '\b': result.add("\\b")
+    of '\f': result.add("\\f")
+    of '\t': result.add("\\t")
+    of '\r': result.add("\\r")
+    of '"': result.add("\\\"")
+    of '\\': result.add("\\\\")
+    else: result.add(c)
   result.add("\"")
 
 proc escapeJson*(s: string): string =
@@ -1925,7 +1918,7 @@ when isMainModule:
     var parsed2 = parseFile("tests/testdata/jsontest2.json")
     doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
 
-  doAssert escapeJson("\10FoobarÄ") == "\"\\u000AFoobar\\u00C4\""
+  doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\""
 
   # Test with extra data
   when not defined(js):
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index d1cf5d9bc..9b2d25267 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -188,7 +188,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
     if low == INVALID_FILE_SIZE:
       fail(osLastError(), "error getting file size")
     else:
-      var fileSize = (int64(hi) shr 32) or low
+      var fileSize = (int64(hi) shl 32) or int64(uint32(low))
       if mappedSize != -1: result.size = min(fileSize, mappedSize).int
       else: result.size = fileSize.int
 
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 2f2b89955..b39d3b691 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -776,7 +776,8 @@ proc countLines*(s: string): int {.noSideEffect,
   ##
   ## In this context, a line is any string seperated by a newline combination.
   ## A line can be an empty string.
-  var i = 1
+  result = 1
+  var i = 0
   while i < s.len:
     case s[i]
     of '\c':
diff --git a/lib/system.nim b/lib/system.nim
index f967fb5f5..ad958d733 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -3401,6 +3401,11 @@ template spliceImpl(s, a, L, b: untyped): untyped =
 when hasAlloc or defined(nimscript):
   proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
     ## slice operation for strings.
+    ## returns the inclusive range [s[x.a], s[x.b]]:
+    ## 
+    ## .. code-block:: nim
+    ##    var s = "abcdef"
+    ##    assert s[1..3] == "bcd" 
     result = s.substr(x.a, x.b)
 
   proc `[]=`*(s: var string, x: Slice[int], b: string) =
@@ -3421,6 +3426,11 @@ when hasAlloc or defined(nimscript):
 
 proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] =
   ## slice operation for arrays.
+  ## returns the inclusive range [a[x.a], a[x.b]]:
+  ## 
+  ## .. code-block:: nim
+  ##    var a = [1,2,3,4]
+  ##    assert a[0..2] == @[1,2,3]
   when low(a) < 0:
     {.error: "Slicing for arrays with negative indices is unsupported.".}
   var L = x.b - x.a + 1
@@ -3455,6 +3465,11 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
 
 proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
   ## slice operation for sequences.
+  ## returns the inclusive range [s[x.a], s[x.b]]:
+  ## 
+  ## .. code-block:: nim
+  ##    var s = @[1,2,3,4]
+  ##    assert s[0..2] == @[1,2,3]
   var a = x.a
   var L = x.b - a + 1
   newSeq(result, L)
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 93a418dd7..09c4f15a7 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -365,7 +365,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
     of "epc-error":
       # an unhandled exception forces down the whole process anyway, so we
       # use 'quit' here instead of 'raise'
-      quit("recieved epc error: " & $messageBuffer)
+      quit("received epc error: " & $messageBuffer)
     else:
       let errMessage = case epcApi
                        of "return", "return-error":
diff --git a/tests/sets/tsets2.nim b/tests/sets/tsets2.nim
index 9c73dbe03..f28822840 100644
--- a/tests/sets/tsets2.nim
+++ b/tests/sets/tsets2.nim
@@ -37,16 +37,26 @@ block setTest2:
   t.incl("111")
   t.incl("123")
   t.excl("111")
-
   t.incl("012")
   t.incl("123") # test duplicates
 
   assert "123" in t
   assert "111" notin t # deleted
 
+  assert t.missingOrExcl("000") == true
+  assert "000" notin t
+  assert t.missingOrExcl("012") == false
+  assert "012" notin t
+
+  assert t.containsOrIncl("012") == false 
+  assert t.containsOrIncl("012") == true
+  assert "012" in t # added back 
+
   for key in items(data): t.incl(key)
   for key in items(data): assert key in t
 
+  for key in items(data): t.excl(key)
+  for key in items(data): assert key notin t
 
 block orderedSetTest1:
   var t = data.toOrderedSet
diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim
index 434caa281..38937590f 100644
--- a/tests/stdlib/tmarshal.nim
+++ b/tests/stdlib/tmarshal.nim
@@ -1,5 +1,5 @@
 discard """
-  output: '''{"age": 12, "bio": "\u042F Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}
+  output: '''{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}
 true
 true
 alpha 100
diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim
index b5e3db4e2..fef1b38c2 100644
--- a/tests/stdlib/tstrutil.nim
+++ b/tests/stdlib/tstrutil.nim
@@ -89,9 +89,21 @@ proc testRFind =
   assert "0123456789ABCDEFGAH".rfind({'A'..'C'}, 13) == 12
   assert "0123456789ABCDEFGAH".rfind({'G'..'H'}, 13) == -1
 
+proc testCountLines =
+  proc assertCountLines(s: string) = assert s.countLines == s.splitLines.len
+  assertCountLines("")
+  assertCountLines("\n")
+  assertCountLines("\n\n")
+  assertCountLines("abc")
+  assertCountLines("abc\n123")
+  assertCountLines("abc\n123\n")
+  assertCountLines("\nabc\n123")
+  assertCountLines("\nabc\n123\n")
+
 testDelete()
 testFind()
 testRFind()
+testCountLines()
 
 assert(insertSep($1000_000) == "1_000_000")
 assert(insertSep($232) == "232")
diff --git a/tests/vm/tstring_openarray.nim b/tests/vm/tstring_openarray.nim
new file mode 100644
index 000000000..1b8a1304c
--- /dev/null
+++ b/tests/vm/tstring_openarray.nim
@@ -0,0 +1,34 @@
+
+# tests various bug when passing string to openArray argument in VM.
+# bug #6086
+proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}):
+                                                            seq[S]{.inline.} =
+# map inlined from sequtils
+  newSeq(result, data.len)
+  for i in 0..data.len-1: result[i] = op(data[i])
+
+
+proc set_all[T](s: var openArray[T]; val: T) =
+  for i in 0..<s.len:
+    s[i] = val
+
+proc test() =
+    var a0 = "hello_world"
+    var a1 = [1,2,3,4,5,6,7,8,9]
+    var a2 = @[1,2,3,4,5,6,7,8,9]
+    a0.set_all('i')
+    a1.set_all(4)
+    a2.set_all(4)
+    doAssert a0 == "iiiiiiiiiii"
+    doAssert a1 == [4,4,4,4,4,4,4,4,4]
+    doAssert a2 == @[4,4,4,4,4,4,4,4,4]
+
+const constval0 = "hello".map(proc(x: char): char = x)
+const constval1 = [1,2,3,4].map(proc(x: int): int = x)
+
+doAssert("hello".map(proc(x: char): char = x) == constval0)
+doAssert([1,2,3,4].map(proc(x: int): int = x) == constval1)
+
+test()
+static:
+    test()