summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/jsgen.nim42
-rw-r--r--compiler/msgs.nim258
-rw-r--r--compiler/sigmatch.nim6
-rw-r--r--lib/pure/math.nim56
-rw-r--r--lib/pure/terminal.nim35
-rw-r--r--lib/system/jssys.nim41
-rw-r--r--tests/typerel/tstr_as_openarray.nim22
7 files changed, 303 insertions, 157 deletions
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index f6ec256d2..d84b0f2f9 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -505,12 +505,12 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
 proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   # code to generate:
   #
-  #  var sp = {prev: excHandler, exc: null};
-  #  excHandler = sp;
+  #  ++excHandler;
   #  try {
   #    stmts;
-  #    TMP = e
-  #  } catch (e) {
+  #  } catch (EXC) {
+  #    var prevJSError = lastJSError; lastJSError = EXC;
+  #    --excHandler;
   #    if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
   #      stmts;
   #    } else if (e.typ && e.typ == NTI32342) {
@@ -518,35 +518,41 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   #    } else {
   #      stmts;
   #    }
+  #    lastJSError = prevJSError;
   #  } finally {
   #    stmts;
-  #    excHandler = excHandler.prev;
   #  }
   genLineDir(p, n)
   if not isEmptyType(n.typ):
     r.kind = resVal
     r.res = getTemp(p)
   inc(p.unique)
+  var i = 1
+  var length = sonsLen(n)
+  var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
+  if catchBranchesExist:
+    add(p.body, "++excHandler;" & tnl)
   var safePoint = "Tmp$1" % [rope(p.unique)]
   addf(p.body,
-       "var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" |
+       "" |
        "local $1 = pcall(",
        [safePoint])
   if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl)
   addf(p.body, "try {$n" | "function()$n", [])
-  var length = sonsLen(n)
   var a: TCompRes
   gen(p, n.sons[0], a)
   moveInto(p, a, r)
-  var i = 1
-  if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch:
-    addf(p.body, "} catch (EXC) {$n  lastJSError = EXC;$n", [])
+  var generalCatchBranchExists = false
+  if p.target == targetJS and catchBranchesExist:
+    addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" &
+        " lastJSError = EXC;$n --excHandler;$n", [])
   elif p.target == targetLua:
     addf(p.body, "end)$n", [])
   while i < length and n.sons[i].kind == nkExceptBranch:
     let blen = sonsLen(n.sons[i])
     if blen == 1:
       # general except section:
+      generalCatchBranchExists = true
       if i > 1: addf(p.body, "else {$n" | "else$n", [])
       gen(p, n.sons[i].sons[0], a)
       moveInto(p, a, r)
@@ -558,17 +564,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
         if n.sons[i].sons[j].kind != nkType:
           internalError(n.info, "genTryStmt")
         if orExpr != nil: add(orExpr, "||" | " or ")
-        addf(orExpr, "isObj($1.exc.m_type, $2)",
-             [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)])
+        addf(orExpr, "isObj(lastJSError.m_type, $1)",
+             [genTypeInfo(p, n.sons[i].sons[j].typ)])
       if i > 1: add(p.body, "else ")
-      addf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n",
+      addf(p.body, "if (lastJSError && ($2)) {$n" | "if $1.exc and ($2) then$n",
         [safePoint, orExpr])
       gen(p, n.sons[i].sons[blen - 1], a)
       moveInto(p, a, r)
       addf(p.body, "}$n" | "end$n", [])
     inc(i)
   if p.target == targetJS:
-    add(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
+    if catchBranchesExist:
+      if not generalCatchBranchExists:
+        useMagic(p, "reraiseException")
+        add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl)
+      add(p.body, "lastJSError = prevJSError;" & tnl)
+    add(p.body, "} finally {" & tnl)
   if i < length and n.sons[i].kind == nkFinally:
     genStmt(p, n.sons[i].sons[0])
   if p.target == targetJS:
@@ -1067,6 +1078,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   r.kind = resExpr
 
 proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
+  useMagic(p, "toJSStr") # Used in rawEcho
   useMagic(p, "rawEcho")
   add(r.res, "rawEcho(")
   let n = n[1].skipConv
@@ -1676,7 +1688,7 @@ proc genHeader(): Rope =
   result = ("/* Generated by the Nim Compiler v$1 */$n" &
             "/*   (c) 2015 Andreas Rumpf */$n$n" &
             "var framePtr = null;$n" &
-            "var excHandler = null;$n" &
+            "var excHandler = 0;$n" &
             "var lastJSError = null;$n") %
            [rope(VersionAsString)]
 
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 4df4430d7..6b71b7159 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -8,7 +8,7 @@
 #
 
 import
-  options, strutils, os, tables, ropes, platform, terminal
+  options, strutils, os, tables, ropes, platform, terminal, macros
 
 type
   TMsgKind* = enum
@@ -367,54 +367,54 @@ const
                                 "of the generic paramers can be inferred from the expected signature.",
     errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
     errUser: "$1",
-    warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
-    warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
-    warnXIsNeverRead: "\'$1\' is never read [XIsNeverRead]",
-    warnXmightNotBeenInit: "\'$1\' might not have been initialized [XmightNotBeenInit]",
-    warnDeprecated: "$1 is deprecated [Deprecated]",
-    warnConfigDeprecated: "config file '$1' is deprecated [ConfigDeprecated]",
-    warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one) [SmallLshouldNotBeUsed]",
-    warnUnknownMagic: "unknown magic \'$1\' might crash the compiler [UnknownMagic]",
-    warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]",
-    warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]",
-    warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]",
-    warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]",
-    warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
-    warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]",
-    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]",
-    warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
-    warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
-    warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]",
-    warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]",
-    warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]",
-    warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]",
-    warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
-    warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
-    warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]",
+    warnCannotOpenFile: "cannot open \'$1\'",
+    warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
+    warnXIsNeverRead: "\'$1\' is never read",
+    warnXmightNotBeenInit: "\'$1\' might not have been initialized",
+    warnDeprecated: "$1 is deprecated",
+    warnConfigDeprecated: "config file '$1' is deprecated",
+    warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one)",
+    warnUnknownMagic: "unknown magic \'$1\' might crash the compiler",
+    warnRedefinitionOfLabel: "redefinition of label \'$1\'",
+    warnUnknownSubstitutionX: "unknown substitution \'$1\'",
+    warnLanguageXNotSupported: "language \'$1\' not supported",
+    warnFieldXNotSupported: "field \'$1\' not supported",
+    warnCommentXIgnored: "comment \'$1\' ignored",
+    warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
+    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
+    warnDifferentHeaps: "possible inconsistency of thread local heaps",
+    warnWriteToForeignHeap: "write to foreign heap",
+    warnUnsafeCode: "unsafe code: '$1'",
+    warnEachIdentIsTuple: "each identifier is a tuple",
+    warnShadowIdent: "shadowed identifier: '$1'",
+    warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
+    warnProveField: "cannot prove that field '$1' is accessible",
+    warnProveIndex: "cannot prove index '$1' is valid",
+    warnGcUnsafe: "not GC-safe: '$1'",
     warnGcUnsafe2: "$1",
-    warnUninit: "'$1' might not have been initialized [Uninit]",
-    warnGcMem: "'$1' uses GC'ed memory [GcMem]",
-    warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]",
-    warnLockLevel: "$1 [LockLevel]",
-    warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]",
-    warnUser: "$1 [User]",
-    hintSuccess: "operation successful [Success]",
-    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#) [SuccessX]",
-    hintLineTooLong: "line too long [LineTooLong]",
-    hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]",
-    hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]",
-    hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless [ConvFromXtoItselfNotNeeded]",
-    hintExprAlwaysX: "expression evaluates always to \'$1\' [ExprAlwaysX]",
-    hintQuitCalled: "quit() called [QuitCalled]",
-    hintProcessing: "$1 [Processing]",
-    hintCodeBegin: "generated code listing: [CodeBegin]",
-    hintCodeEnd: "end of listing [CodeEnd]",
-    hintConf: "used config file \'$1\' [Conf]",
-    hintPath: "added path: '$1' [Path]",
-    hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]",
-    hintName: "name should be: '$1' [Name]",
-    hintPattern: "$1 [Pattern]",
-    hintUser: "$1 [User]"]
+    warnUninit: "'$1' might not have been initialized",
+    warnGcMem: "'$1' uses GC'ed memory",
+    warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
+    warnLockLevel: "$1",
+    warnResultShadowed: "Special variable 'result' is shadowed.",
+    warnUser: "$1",
+    hintSuccess: "operation successful",
+    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
+    hintLineTooLong: "line too long",
+    hintXDeclaredButNotUsed: "\'$1\' is declared but not used",
+    hintConvToBaseNotNeeded: "conversion to base object is not needed",
+    hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
+    hintExprAlwaysX: "expression evaluates always to \'$1\'",
+    hintQuitCalled: "quit() called",
+    hintProcessing: "$1",
+    hintCodeBegin: "generated code listing:",
+    hintCodeEnd: "end of listing",
+    hintConf: "used config file \'$1\'",
+    hintPath: "added path: '$1'",
+    hintConditionAlwaysTrue: "condition is always true: '$1'",
+    hintName: "name should be: '$1'",
+    hintPattern: "$1",
+    hintUser: "$1"]
 
 const
   WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
@@ -605,13 +605,17 @@ proc suggestQuit*() =
 # this format is understood by many text editors: it is the same that
 # Borland and Freepascal use
 const
-  PosErrorFormat* = "$1($2, $3) Error: "
-  PosWarningFormat* = "$1($2, $3) Warning: "
-  PosHintFormat* = "$1($2, $3) Hint: "
-  PosContextFormat = "$1($2, $3) Info: "
-  RawError* = "Error: "
-  RawWarning* = "Warning: "
-  RawHint* = "Hint: "
+  PosFormat    = "$1($2, $3) "
+  KindFormat   = " [$1]"
+  KindColor    = fgCyan
+  ErrorTitle   = "Error: "
+  ErrorColor   = fgRed
+  WarningTitle = "Warning: "
+  WarningColor = fgYellow
+  HintTitle    = "Hint: "
+  HintColor    = fgGreen
+  InfoTitle    = "Info: "
+  InfoColor    = fgCyan
 
 proc getInfoContextLen*(): int = return msgContext.len
 proc setInfoContextLen*(L: int) = setLen(msgContext, L)
@@ -686,27 +690,58 @@ proc outWriteln*(s: string) =
   ## Writes to stdout. Always.
   if eStdOut in errorOutputs: writeln(stdout, s)
 
-proc msgWriteln*(s: string, color: ForegroundColor = fgWhite, coloredText: string = "") =
-  ## Writes to stdout. If --stderr option is given, writes to stderr instead.
+proc msgWriteln*(s: string) =
+  ## Writes to stdout. If --stdout option is given, writes to stderr instead.
 
   #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
 
-  var hasColor = optUseColors in gGlobalOptions
   if not isNil(writelnHook):
-    writelnHook(coloredText & s)
+    writelnHook(s)
+  elif optStdout in gGlobalOptions:
+    if eStdErr in errorOutputs: writeln(stderr, s)
   else:
-    if optStdout in gGlobalOptions:
-      if eStdErr in errorOutputs:
-        if hasColor: setForegroundColor(color)
-        write(stderr, coloredText)
-        if hasColor: resetAttributes()
-        writeln(stderr, s)
-    else:
-      if eStdOut in errorOutputs:
-        if hasColor: setForegroundColor(color)
-        write(stdout, coloredText)
-        if hasColor: resetAttributes()
-        writeln(stdout, s)
+    if eStdOut in errorOutputs: writeln(stdout, s)
+
+macro callIgnoringStyle(theProc: typed, first: typed,
+                        args: varargs[expr]): stmt =
+  let typForegroundColor = bindSym"ForegroundColor".getType
+  let typBackgroundColor = bindSym"BackgroundColor".getType
+  let typStyle = bindSym"Style".getType
+  let typTerminalCmd = bindSym"TerminalCmd".getType
+  result = newCall(theProc)
+  if first.kind != nnkNilLit: result.add(first)
+  for arg in children(args[0][1]):
+    if arg.kind == nnkNilLit: continue
+    let typ = arg.getType
+    if typ.kind != nnkEnumTy or
+       typ != typForegroundColor and
+       typ != typBackgroundColor and
+       typ != typStyle and
+       typ != typTerminalCmd:
+      result.add(arg)
+
+macro callStyledEcho(args: varargs[expr]): stmt =
+  result = newCall(bindSym"styledEcho")
+  for arg in children(args[0][1]):
+    result.add(arg)
+
+template callWritelnHook(args: varargs[string, `$`]) =
+  var s = ""
+  for arg in args:
+    s.add arg
+  writelnHook s
+
+template styledMsgWriteln*(args: varargs[expr]) =
+  if not isNil(writelnHook):
+    callIgnoringStyle(callWritelnHook, nil, args)
+  elif optStdout in gGlobalOptions:
+    if eStdErr in errorOutputs: callIgnoringStyle(writeln, stderr, args)
+  else:
+    if eStdOut in errorOutputs:
+      if optUseColors in gGlobalOptions:
+        callStyledEcho(args)
+      else:
+        callIgnoringStyle(writeln, stdout, args)
 
 proc coordToStr(coord: int): string =
   if coord == -1: result = "???"
@@ -728,7 +763,7 @@ proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
       if stackTraceAvailable() and isNil(writelnHook):
         writeStackTrace()
       else:
-        msgWriteln("", fgRed, "No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
+        styledMsgWriteln(fgRed, "No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
     quit 1
 
   if msg >= fatalMin and msg <= fatalMax:
@@ -750,10 +785,12 @@ proc writeContext(lastinfo: TLineInfo) =
   var info = lastinfo
   for i in countup(0, len(msgContext) - 1):
     if msgContext[i] != lastinfo and msgContext[i] != info:
-      msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
-                                   coordToStr(msgContext[i].line),
-                                   coordToStr(msgContext[i].col+1),
-                                   getMessageStr(errInstantiationFrom, "")])
+      styledMsgWriteln(styleBright,
+                       PosFormat % [toMsgFilename(msgContext[i]),
+                                    coordToStr(msgContext[i].line),
+                                    coordToStr(msgContext[i].col+1)],
+                       styleDim,
+                       getMessageStr(errInstantiationFrom, ""))
     info = msgContext[i]
 
 proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
@@ -761,29 +798,36 @@ proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
 
 proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
   var
-    frmt: string
+    title: string
     color: ForegroundColor
+    kind:  string
   case msg
   of errMin..errMax:
     writeContext(unknownLineInfo())
-    frmt = RawError
-    color = fgRed
+    title = ErrorTitle
+    color = ErrorColor
   of warnMin..warnMax:
     if optWarns notin gOptions: return
     if msg notin gNotes: return
     writeContext(unknownLineInfo())
-    frmt = RawWarning
+    title = WarningTitle
+    color = WarningColor
+    kind = WarningsToStr[ord(msg) - ord(warnMin)]
     inc(gWarnCounter)
-    color = fgYellow
   of hintMin..hintMax:
     if optHints notin gOptions: return
     if msg notin gNotes: return
-    frmt = RawHint
+    title = HintTitle
+    color = HintColor
+    kind = HintsToStr[ord(msg) - ord(hintMin)]
     inc(gHintCounter)
-    color = fgGreen
   let s = `%`(msgKindToString(msg), args)
   if not ignoreMsgBecauseOfIdeTools(msg):
-    msgWriteln(s, color, frmt)
+    if kind != nil:
+      styledMsgWriteln(color, title, resetStyle, s,
+                       KindColor, `%`(KindFormat, kind))
+    else:
+      styledMsgWriteln(color, title, resetStyle, s)
   handleError(msg, doAbort, s)
 
 proc rawMessage*(msg: TMsgKind, arg: string) =
@@ -795,47 +839,56 @@ proc writeSurroundingSrc(info: TLineInfo) =
   msgWriteln(indent & spaces(info.col) & '^')
 
 proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
-  let frmt = case msg
-             of warnMin..warnMax: PosWarningFormat
-             of hintMin..hintMax: PosHintFormat
-             else: PosErrorFormat
-  result = frmt % [toMsgFilename(info), coordToStr(info.line),
-                   coordToStr(info.col+1), getMessageStr(msg, arg)]
+  let title = case msg
+              of warnMin..warnMax: WarningTitle
+              of hintMin..hintMax: HintTitle
+              else: ErrorTitle
+  result = PosFormat % [toMsgFilename(info), coordToStr(info.line),
+                        coordToStr(info.col+1)] &
+           title &
+           getMessageStr(msg, arg)
 
 proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
                eh: TErrorHandling) =
   var
-    frmt: string
-    ignoreMsg = false
+    title: string
     color: ForegroundColor
+    kind:  string
+    ignoreMsg = false
   case msg
   of errMin..errMax:
     writeContext(info)
-    frmt = PosErrorFormat
+    title = ErrorTitle
+    color = ErrorColor
     # we try to filter error messages so that not two error message
     # in the same file and line are produced:
     #ignoreMsg = lastError == info and eh != doAbort
     lastError = info
-    color = fgRed
   of warnMin..warnMax:
     ignoreMsg = optWarns notin gOptions or msg notin gNotes
     if not ignoreMsg: writeContext(info)
-    frmt = PosWarningFormat
+    title = WarningTitle
+    color = WarningColor
+    kind = WarningsToStr[ord(msg) - ord(warnMin)]
     inc(gWarnCounter)
-    color = fgYellow
   of hintMin..hintMax:
     ignoreMsg = optHints notin gOptions or msg notin gNotes
-    frmt = PosHintFormat
+    title = HintTitle
+    color = HintColor
+    kind = HintsToStr[ord(msg) - ord(hintMin)]
     inc(gHintCounter)
-    color = fgGreen
   # NOTE: currently line info line numbers start with 1,
   # but column numbers start with 0, however most editors expect
   # first column to be 1, so we need to +1 here
-  let x = frmt % [toMsgFilename(info), coordToStr(info.line),
-                  coordToStr(info.col+1)]
+  let x = PosFormat % [toMsgFilename(info), coordToStr(info.line),
+                       coordToStr(info.col+1)]
   let s = getMessageStr(msg, arg)
   if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
-    msgWriteln(s, color, x)
+    if kind != nil:
+      styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
+                       KindColor, `%`(KindFormat, kind))
+    else:
+      styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
     if optPrintSurroundingSrc and msg in errMin..errMax:
       info.writeSurroundingSrc
   handleError(msg, eh, s)
@@ -907,3 +960,6 @@ ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
   of rCannotOpenFile:
     rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
 
+# enable colors by default on terminals
+if terminal.isatty(stdout):
+  incl(gGlobalOptions, optUseColors)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 7ea2c3d6f..f506e3ff5 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -748,6 +748,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = isConvertible
       elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
         result = isConvertible
+    of tyString:
+      if f.kind == tyOpenArray:
+        if f.sons[0].kind == tyChar:
+          result = isConvertible
+        elif f.sons[0].kind == tyGenericParam and typeRel(c, base(f), base(a)) >= isGeneric:
+          result = isConvertible
     else: discard
   of tySequence:
     case a.kind
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index a9e9010f6..b91c7f0d8 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -202,28 +202,52 @@ when not defined(JS):
     ## computes x to power raised of y.
     
   # C procs:
-  proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>".}
-  proc rand(): cint {.importc: "rand", header: "<stdlib.h>".}
+  when defined(vcc):
+    # The "secure" random, available from Windows XP
+    # https://msdn.microsoft.com/en-us/library/sxtz2fa8.aspx
+    # Present in some variants of MinGW but not enough to justify
+    # `when defined(windows)` yet
+    proc rand_s(val: var cuint) {.importc: "rand_s", header: "<stdlib.h>".}
+    # To behave like the normal version
+    proc rand(): cuint = rand_s(result)
+  else:
+    proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>".}
+    proc rand(): cint {.importc: "rand", header: "<stdlib.h>".}
   
   when not defined(windows):
     proc srand48(seed: clong) {.importc: "srand48", header: "<stdlib.h>".}
     proc drand48(): float {.importc: "drand48", header: "<stdlib.h>".}
     proc random(max: float): float =
       result = drand48() * max
-  when defined(windows):
-    proc random(max: float): float =
-      # we are hardcodeing this because
-      # importcing macros is extremely problematic
-      # and because the value is publicly documented
-      # on MSDN and very unlikely to change
-      const rand_max = 32767
-      result = (float(rand()) / float(rand_max)) * max
-  proc randomize() =
-    randomize(cast[int](epochTime()))
-
-  proc randomize(seed: int) =
-    srand(cint(seed))
-    when declared(srand48): srand48(seed)
+  else:
+    when defined(vcc): # Windows with Visual C
+      proc random(max: float): float =
+        # we are hardcoding this because
+        # importc-ing macros is extremely problematic
+        # and because the value is publicly documented
+        # on MSDN and very unlikely to change
+        # See https://msdn.microsoft.com/en-us/library/296az74e.aspx
+        const rand_max = 4294967295 # UINT_MAX
+        result = (float(rand()) / float(rand_max)) * max
+      proc randomize() = discard
+      proc randomize(seed: int) = discard
+    else: # Windows with another compiler
+      proc random(max: float): float =
+        # we are hardcoding this because
+        # importc-ing macros is extremely problematic
+        # and because the value is publicly documented
+        # on MSDN and very unlikely to change
+        const rand_max = 32767
+        result = (float(rand()) / float(rand_max)) * max
+  
+  when not defined(vcc): # the above code for vcc uses `discard` instead
+    # this is either not Windows or is Windows without vcc
+    proc randomize() =
+      randomize(cast[int](epochTime()))
+    proc randomize(seed: int) =
+      srand(cint(seed)) # rand_s doesn't use srand
+      when declared(srand48): srand48(seed)
+    
   proc random(max: int): int =
     result = int(rand()) mod max
 
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 15e2eefec..2efdf72d5 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -406,22 +406,43 @@ proc isatty*(f: File): bool =
 
   result = isatty(getFileHandle(f)) != 0'i32
 
-proc styledEchoProcessArg(s: string) = write stdout, s
-proc styledEchoProcessArg(style: Style) = setStyle({style})
-proc styledEchoProcessArg(style: set[Style]) = setStyle style
-proc styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color
-proc styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color
+type
+  TerminalCmd* = enum  ## commands that can be expressed as arguments
+    resetStyle         ## reset attributes
+
+template styledEchoProcessArg(s: string) = write stdout, s
+template styledEchoProcessArg(style: Style) = setStyle({style})
+template styledEchoProcessArg(style: set[Style]) = setStyle style
+template styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color
+template styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color
+template styledEchoProcessArg(cmd: TerminalCmd) =
+  when cmd == resetStyle:
+    resetAttributes()
 
 macro styledEcho*(m: varargs[expr]): stmt =
   ## to be documented.
   let m = callsite()
+  var reset = false
   result = newNimNode(nnkStmtList)
 
   for i in countup(1, m.len - 1):
-    result.add(newCall(bindSym"styledEchoProcessArg", m[i]))
+    let item = m[i]
+    case item.kind
+    of nnkStrLit..nnkTripleStrLit:
+      if i == m.len - 1:
+        # optimize if string literal is last, just call writeln
+        result.add(newCall(bindSym"writeln", bindSym"stdout", item))
+        if reset: result.add(newCall(bindSym"resetAttributes"))
+        return
+      else:
+        # if it is string literal just call write, do not enable reset
+        result.add(newCall(bindSym"write", bindSym"stdout", item))
+    else:
+      result.add(newCall(bindSym"styledEchoProcessArg", item))
+      reset = true
 
   result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n")))
-  result.add(newCall(bindSym"resetAttributes"))
+  if reset: result.add(newCall(bindSym"resetAttributes"))
 
 when defined(nimdoc):
   proc getch*(): char =
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index f082023ee..5a9333a0e 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -37,9 +37,7 @@ type
 
 var
   framePtr {.importc, nodecl, volatile.}: PCallFrame
-  excHandler {.importc, nodecl, volatile.}: PSafePoint = nil
-    # list of exception handlers
-    # a global variable for the root of all try blocks
+  excHandler {.importc, nodecl, volatile.}: int = 0
   lastJSError {.importc, nodecl, volatile.}: PJSError = nil
 
 {.push stacktrace: off, profiler:off.}
@@ -52,9 +50,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
   result[0] = x
 
 proc getCurrentExceptionMsg*(): string =
-  if excHandler != nil and excHandler.exc != nil:
-    return $excHandler.exc.msg
-  elif lastJSError != nil:
+  if lastJSError != nil:
     return $lastJSError.message
   else:
     return ""
@@ -99,32 +95,41 @@ proc rawWriteStackTrace(): string =
   else:
     result = "No stack traceback available\n"
 
-proc raiseException(e: ref Exception, ename: cstring) {.
+proc unhandledException(e: ref Exception) {.
     compilerproc, asmNoStackFrame.} =
-  e.name = ename
-  if excHandler != nil:
-    excHandler.exc = e
+  when NimStackTrace:
+    var buf = rawWriteStackTrace()
   else:
-    when NimStackTrace:
-      var buf = rawWriteStackTrace()
-    else:
-      var buf = ""
+    var buf = ""
     if e.msg != nil and e.msg[0] != '\0':
       add(buf, "Error: unhandled exception: ")
       add(buf, e.msg)
     else:
       add(buf, "Error: unhandled exception")
     add(buf, " [")
-    add(buf, ename)
+    add(buf, e.name)
     add(buf, "]\n")
     alert(buf)
-  asm """throw `e`;"""
+
+proc raiseException(e: ref Exception, ename: cstring) {.
+    compilerproc, asmNoStackFrame.} =
+  e.name = ename
+  when not defined(noUnhandledHandler):
+    if excHandler == 0:
+      unhandledException(e)
+  asm "throw `e`;"
 
 proc reraiseException() {.compilerproc, asmNoStackFrame.} =
-  if excHandler == nil:
+  if lastJSError == nil:
     raise newException(ReraiseError, "no exception to reraise")
   else:
-    asm """throw excHandler.exc;"""
+    when not defined(noUnhandledHandler):
+      if excHandler == 0:
+        var isNimException : bool
+        asm "`isNimException` = lastJSError.m_type;"
+        if isNimException:
+          unhandledException(cast[ref Exception](lastJSError))
+    asm "throw lastJSError;"
 
 proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} =
   raise newException(OverflowError, "over- or underflow")
diff --git a/tests/typerel/tstr_as_openarray.nim b/tests/typerel/tstr_as_openarray.nim
new file mode 100644
index 000000000..fc28d6c93
--- /dev/null
+++ b/tests/typerel/tstr_as_openarray.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''success'''
+"""
+var s = "HI"
+
+proc x (zz: openarray[char]) =
+  discard
+
+x s
+
+proc z [T] (zz: openarray[T]) =
+  discard
+
+z s
+z([s,s,s])
+
+proc y [T] (arg: var openarray[T]) =
+  arg[0] = 'X'
+y s
+doAssert s == "XI"
+
+echo "success"