summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/c2nim/cparse.nim11
-rwxr-xr-xcompiler/c2nim/tests/systest.c14
-rwxr-xr-xcompiler/cgen.nim8
-rwxr-xr-xcompiler/jsgen.nim13
-rwxr-xr-xcompiler/nimrod.ini3
-rwxr-xr-xcompiler/semfold.nim11
-rwxr-xr-xcompiler/semgnrc.nim5
-rwxr-xr-xdoc/endb.txt12
-rwxr-xr-xdoc/lib.txt5
-rwxr-xr-xinstall.sh19
-rwxr-xr-xlib/core/typeinfo.nim10
-rwxr-xr-xlib/pure/strutils.nim13
-rw-r--r--lib/pure/unicode.nim12
-rwxr-xr-xlib/system.nim75
-rwxr-xr-xlib/system/debugger.nim679
-rw-r--r--lib/system/endb.nim538
-rwxr-xr-xlib/system/jssys.nim1
-rw-r--r--tests/run/tfieldindex.nim21
-rwxr-xr-xtests/run/tfinally.nim8
-rwxr-xr-xtests/run/tfinally2.nim17
-rw-r--r--tests/specials.nim8
21 files changed, 843 insertions, 640 deletions
diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim
index 9f7a7bf36..b964ed976 100755
--- a/compiler/c2nim/cparse.nim
+++ b/compiler/c2nim/cparse.nim
@@ -955,15 +955,22 @@ proc enumSpecifier(p: var TParser): PNode =
     result = newNodeP(nkConstSection, p)
     getTok(p, result)
     var i = 0
+    var hasUnknown = false
     while true:
       var name = skipIdentExport(p)
       var val: PNode
       if p.tok.xkind == pxAsgn: 
         getTok(p, name)
         val = constantExpression(p)
-        if val.kind == nkIntLit: i = int(val.intVal)+1
-        else: parMessage(p, errXExpected, "int literal")
+        if val.kind == nkIntLit:  
+          i = int(val.intVal)+1
+          hasUnknown = false
+        else:
+          hasUnknown = true
       else:
+        if hasUnknown:
+          parMessage(p, warnUser, "computed const value may be wrong: " &
+            name.renderTree)
         val = newIntNodeP(nkIntLit, i, p)
         inc(i)
       var c = createConst(name, ast.emptyNode, val, p)
diff --git a/compiler/c2nim/tests/systest.c b/compiler/c2nim/tests/systest.c
index 2a9dd6c28..b73eb5bee 100755
--- a/compiler/c2nim/tests/systest.c
+++ b/compiler/c2nim/tests/systest.c
@@ -9,6 +9,20 @@ extern "C" {
 #  endif
 #endif
 
+enum
+{
+/* 8bit, color or not */
+    CV_LOAD_IMAGE_UNCHANGED  =-1,
+/* 8bit, gray */
+    CV_LOAD_IMAGE_GRAYSCALE  =0,
+/* ?, color */
+    CV_LOAD_IMAGE_COLOR      =1,
+/* any depth, ? */
+    CV_LOAD_IMAGE_ANYDEPTH   =2,
+/* ?, any color */
+    CV_LOAD_IMAGE_ANYCOLOR   =4
+};
+
 typedef void (*callback_t) (int rc);
 typedef const char* (*callback2)(int rc, long L, const char* buffer);
 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index d06a6cb6f..b7849f074 100755
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -892,6 +892,12 @@ proc getFileHeader(cfilenoext: string): PRope =
   result = getCopyright(cfilenoext)
   addIntTypes(result)
 
+proc genFilenames(m: BModule): PRope =
+  discard cgsym(m, "dbgRegisterFilename")
+  result = nil
+  for i in 0.. <fileInfos.len:
+    result.appf("dbgRegisterFilename($1);$n", fileInfos[i].projPath.makeCString)
+
 proc genMainProc(m: BModule) = 
   const 
     CommonMainBody =
@@ -950,6 +956,8 @@ proc genMainProc(m: BModule) =
     nimMain = PosixNimMain
     otherMain = PosixCMain
   if gBreakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
+  if optEndb in gOptions:
+    gBreakpoints.app(m.genFilenames)
   
   let initStackBottomCall = if emulatedThreadVars() or
                               platform.targetOS == osStandalone: "".toRope
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 64175dc93..1f95f7955 100755
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -44,7 +44,6 @@ type
   TBlock{.final.} = object 
     id: int                  # the ID of the label; positive means that it
                              # has been used (i.e. the label should be emitted)
-    nestedTryStmts: int      # how many try statements is it nested into
     isLoop: bool             # whether it's a 'block' or 'while'
   
   TGlobals{.final.} = object 
@@ -62,7 +61,6 @@ type
     module: BModule
     g: PGlobals
     BeforeRetNeeded: bool
-    nestedTryStmts: int
     unique: int
     blocks: seq[TBlock]
 
@@ -485,10 +483,6 @@ proc genLineDir(p: var TProc, n: PNode, r: var TCompRes) =
       ((p.prc == nil) or not (sfPure in p.prc.flags)): 
     appf(r.com, "F.line = $1;$n", [toRope(line)])
   
-proc finishTryStmt(p: var TProc, r: var TCompRes, howMany: int) = 
-  for i in countup(1, howMany):
-    app(r.com, "excHandler = excHandler.prev;" & tnl)
-  
 proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) = 
   var 
     cond, stmt: TCompRes
@@ -498,7 +492,6 @@ proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) =
   length = len(p.blocks)
   setlen(p.blocks, length + 1)
   p.blocks[length].id = - p.unique
-  p.blocks[length].nestedTryStmts = p.nestedTryStmts
   p.blocks[length].isLoop = true
   labl = p.unique
   gen(p, n.sons[0], cond)
@@ -543,7 +536,6 @@ proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) =
   if optStackTrace in p.Options: app(r.com, "framePtr = F;" & tnl)
   app(r.com, "try {" & tnl)
   length = sonsLen(n)
-  inc(p.nestedTryStmts)
   genStmt(p, n.sons[0], a)
   app(r.com, mergeStmt(a))
   i = 1
@@ -571,8 +563,6 @@ proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) =
       appf(epart, "$1}$n", [mergeStmt(a)])
     inc(i)
   if epart != nil: appf(r.com, "} catch (EXC) {$n$1", [epart])
-  finishTryStmt(p, r, p.nestedTryStmts)
-  dec(p.nestedTryStmts)
   app(r.com, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
   if (i < length) and (n.sons[i].kind == nkFinally): 
     genStmt(p, n.sons[i].sons[0], a)
@@ -655,7 +645,6 @@ proc genBlock(p: var TProc, n: PNode, r: var TCompRes) =
     sym.loc.a = idx
   setlen(p.blocks, idx + 1)
   p.blocks[idx].id = - p.unique # negative because it isn't used yet
-  p.blocks[idx].nestedTryStmts = p.nestedTryStmts
   labl = p.unique
   if n.kind == nkBlockExpr: genStmtListExpr(p, n.sons[1], r)
   else: genStmt(p, n.sons[1], r)
@@ -682,7 +671,6 @@ proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) =
     if idx < 0 or not p.blocks[idx].isLoop:
       InternalError(n.info, "no loop to break")
   p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
-  finishTryStmt(p, r, p.nestedTryStmts - p.blocks[idx].nestedTryStmts)
   appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)])
 
 proc genAsmStmt(p: var TProc, n: PNode, r: var TCompRes) = 
@@ -1433,7 +1421,6 @@ proc genReturnStmt(p: var TProc, n: PNode, r: var TCompRes) =
     if a.com != nil: appf(r.com, "$1;$n", mergeStmt(a))
   else: 
     genLineDir(p, n, r)
-  finishTryStmt(p, r, p.nestedTryStmts)
   app(r.com, "break BeforeRet;" & tnl)
 
 proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope = 
diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini
index 74cf534e4..9febe24aa 100755
--- a/compiler/nimrod.ini
+++ b/compiler/nimrod.ini
@@ -57,8 +57,6 @@ Files: "compiler/*.nim"
 Files: "build/empty.txt"
 Files: "bin/empty.txt"
 
-Files: "packages/docutils/*.nim"
-
 
 [Lib]
 Files: "lib/nimbase.h"
@@ -85,6 +83,7 @@ Files: "lib/wrappers/zip/libzip_all.c"
 Files: "lib/windows/*.nim"
 Files: "lib/posix/*.nim"
 Files: "lib/js/*.nim"
+Files: "lib/packages/docutils/*.nim"
 
 
 [Other]
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index e26700e27..d304ddd3c 100755
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -490,7 +490,7 @@ proc getArrayConstr(m: PSym, n: PNode): PNode =
   
 proc foldArrayAccess(m: PSym, n: PNode): PNode = 
   var x = getConstExpr(m, n.sons[0])
-  if x == nil: return
+  if x == nil or x.typ.skipTypes({tyGenericInst}).kind == tyTypeDesc: return
   
   var y = getConstExpr(m, n.sons[1])
   if y == nil: return
@@ -541,7 +541,12 @@ proc foldConStrStr(m: PSym, n: PNode): PNode =
     let a = getConstExpr(m, n.sons[i])
     if a == nil: return nil
     result.strVal.add(getStrOrChar(a))
-  
+
+proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
+  result = newSymNode(s, info)
+  result.typ = newType(tyTypeDesc, s.owner)
+  result.typ.addSonSkipIntLit(s.typ)
+
 proc getConstExpr(m: PSym, n: PNode): PNode = 
   result = nil
   case n.kind
@@ -569,6 +574,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
         if sfFakeConst notin s.flags: result = copyTree(s.ast)
     elif s.kind in {skProc, skMethod}: # BUGFIX
       result = n
+    elif s.kind in {skType, skGenericParam}:
+      result = newSymNodeTypeDesc(s, n.info)
   of nkCharLit..nkNilLit: 
     result = copyNode(n)
   of nkIfExpr: 
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 2e1eccda2..8c3cef027 100755
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -31,11 +31,6 @@ proc getIdentNode(n: PNode): PNode =
     illFormedAst(n)
     result = n
   
-proc newSymNodeTypeDesc(s: PSym; info: TLineInfo): PNode =
-  result = newSymNode(s, info)
-  result.typ = newType(tyTypeDesc, s.owner)
-  result.typ.addSonSkipIntLit(s.typ)
-
 proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
                     ctx: var TIntSet): PNode
 proc semGenericStmtScope(c: PContext, n: PNode, 
diff --git a/doc/endb.txt b/doc/endb.txt
index 900553f0e..d4437e5d9 100755
--- a/doc/endb.txt
+++ b/doc/endb.txt
@@ -61,8 +61,8 @@ Executing Commands
 Breakpoint Commands

 ===================

 

-``b``, ``setbreak`` <identifier> [fromline [toline]] [file]

-    Set a new breakpoint named 'identifier' for the given file

+``b``, ``setbreak`` [fromline [toline]] [file]

+    Set a new breakpoint for the given file

     and line numbers. If no file is given, the current execution point's

     filename is used. If the filename has no extension, ``.nim`` is

     appended for your convenience.

@@ -71,16 +71,16 @@ Breakpoint Commands
     breakpoint contains a line number range. Some examples if it is still

     unclear:

 

-    * ``b br1 12 15 thallo`` creates a breakpoint named ``br1`` that

+    * ``b 12 15 thallo`` creates a breakpoint that

       will be triggered if the instruction pointer reaches one of the

       lines 12-15 in the file ``thallo.nim``.

-    * ``b br1 12 thallo`` creates a breakpoint named ``br1`` that

+    * ``b 12 thallo`` creates a breakpoint that

       will be triggered if the instruction pointer reaches the

       line 12 in the file ``thallo.nim``.

-    * ``b br1 12`` creates a breakpoint named ``br1`` that

+    * ``b 12`` creates a breakpoint that

       will be triggered if the instruction pointer reaches the

       line 12 in the current file.

-    * ``b br1`` creates a breakpoint named ``br1`` that

+    * ``b`` creates a breakpoint that

       will be triggered if the instruction pointer reaches the

       current line in the current file again.

 

diff --git a/doc/lib.txt b/doc/lib.txt
index f18693614..2f781f375 100755
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -16,7 +16,10 @@ Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
 while impure libraries do. A wrapper is an impure library that is a very
 low-level interface to a C library.
 
-Read this `document <apis.html>`_ for a quick overview of the API design.
+Read this `document <apis.html>`_ for a quick overview of the API design. If
+you can't find here some functionality you are looking for you could try using
+the 3rd party `package manager Babel <https://github.com/nimrod-code/babel>`_
+and its list of packages.
 
 
 Pure libraries
diff --git a/install.sh b/install.sh
index 886cc8800..f283a4ae8 100755
--- a/install.sh
+++ b/install.sh
@@ -70,6 +70,7 @@ if [ $# -eq 1 ] ; then
   mkdir -p $libdir/windows
   mkdir -p $libdir/posix
   mkdir -p $libdir/js
+  mkdir -p $libdir/packages/docutils
 
   cp bin/nimrod $bindir/nimrod
   chmod 755 $bindir/nimrod
@@ -711,20 +712,26 @@ if [ $# -eq 1 ] ; then
   chmod 644 $libdir/system/debugger.nim
   cp lib/system/dyncalls.nim $libdir/system/dyncalls.nim
   chmod 644 $libdir/system/dyncalls.nim
-  cp lib/system/jssys.nim $libdir/system/jssys.nim
-  chmod 644 $libdir/system/jssys.nim
   cp lib/system/embedded.nim $libdir/system/embedded.nim
   chmod 644 $libdir/system/embedded.nim
+  cp lib/system/endb.nim $libdir/system/endb.nim
+  chmod 644 $libdir/system/endb.nim
   cp lib/system/excpt.nim $libdir/system/excpt.nim
   chmod 644 $libdir/system/excpt.nim
   cp lib/system/gc.nim $libdir/system/gc.nim
   chmod 644 $libdir/system/gc.nim
   cp lib/system/gc2.nim $libdir/system/gc2.nim
   chmod 644 $libdir/system/gc2.nim
+  cp lib/system/gc_genms.nim $libdir/system/gc_genms.nim
+  chmod 644 $libdir/system/gc_genms.nim
+  cp lib/system/gc_ms.nim $libdir/system/gc_ms.nim
+  chmod 644 $libdir/system/gc_ms.nim
   cp lib/system/hti.nim $libdir/system/hti.nim
   chmod 644 $libdir/system/hti.nim
   cp lib/system/inclrtl.nim $libdir/system/inclrtl.nim
   chmod 644 $libdir/system/inclrtl.nim
+  cp lib/system/jssys.nim $libdir/system/jssys.nim
+  chmod 644 $libdir/system/jssys.nim
   cp lib/system/mmdisp.nim $libdir/system/mmdisp.nim
   chmod 644 $libdir/system/mmdisp.nim
   cp lib/system/profiler.nim $libdir/system/profiler.nim
@@ -1103,6 +1110,14 @@ if [ $# -eq 1 ] ; then
   chmod 644 $libdir/posix/posix.nim
   cp lib/js/dom.nim $libdir/js/dom.nim
   chmod 644 $libdir/js/dom.nim
+  cp lib/packages/docutils/highlite.nim $libdir/packages/docutils/highlite.nim
+  chmod 644 $libdir/packages/docutils/highlite.nim
+  cp lib/packages/docutils/rst.nim $libdir/packages/docutils/rst.nim
+  chmod 644 $libdir/packages/docutils/rst.nim
+  cp lib/packages/docutils/rstast.nim $libdir/packages/docutils/rstast.nim
+  chmod 644 $libdir/packages/docutils/rstast.nim
+  cp lib/packages/docutils/rstgen.nim $libdir/packages/docutils/rstgen.nim
+  chmod 644 $libdir/packages/docutils/rstgen.nim
   
   echo "installation successful"
 else
diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index eb2a0c9e5..6f8081b5f 100755
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -102,6 +102,16 @@ proc newAny(value: pointer, rawType: PNimType): TAny =
   result.value = value
   result.rawType = rawType
 
+when defined(system.TVarSlot):
+  proc toAny*(x: TVarSlot): TAny {.inline.} =
+    ## constructs a ``TAny`` object from a variable slot ``x``. 
+    ## This captures `x`'s address, so `x` can be modified with its
+    ## ``TAny`` wrapper! The client needs to ensure that the wrapper
+    ## **does not** live longer than `x`!
+    ## This is provided for easier reflection capabilities of a debugger.
+    result.value = x.address
+    result.rawType = x.typ
+
 proc toAny*[T](x: var T): TAny {.inline.} =
   ## constructs a ``TAny`` object from `x`. This captures `x`'s address, so
   ## `x` can be modified with its ``TAny`` wrapper! The client needs to ensure
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 090ad640c..b5f5a41eb 100755
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -415,7 +415,15 @@ proc parseEnum*[T: enum](s: string, default: T): T =
 proc repeatChar*(count: int, c: Char = ' '): string {.noSideEffect,

   rtl, extern: "nsuRepeatChar".} =

   ## Returns a string of length `count` consisting only of

-  ## the character `c`.

+  ## the character `c`. You can use this proc to left align strings. Example:

+  ##

+  ## .. code-block:: nimrod

+  ##   let

+  ##     width = 15

+  ##     text1 = "Hello user!"

+  ##     text2 = "This is a very long string"

+  ##   echo text1 & repeatChar(max(0, width - text1.len)) & "|"

+  ##   echo text2 & repeatChar(max(0, width - text2.len)) & "|"

   result = newString(count)

   for i in 0..count-1: result[i] = c

 

@@ -429,7 +437,8 @@ proc align*(s: string, count: int): string {.
   noSideEffect, rtl, extern: "nsuAlignString".} =

   ## Aligns a string `s` with spaces, so that is of length `count`. Spaces are

   ## added before `s` resulting in right alignment. If ``s.len >= count``, no

-  ## spaces are added and `s` is returned unchanged.

+  ## spaces are added and `s` is returned unchanged. If you need to left align

+  ## a string use the repeatChar proc.

   if s.len < count:

     result = newString(count)

     var spaces = count - s.len

diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index f76573788..142178a86 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -132,6 +132,11 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
     result = newString(1)
     result[0] = chr(i)
 
+proc `$`*(runes: seq[TRune]): string =
+  ## converts a sequence of runes to a string
+  result = ""
+  for rune in runes: result.add(rune.toUTF8)
+
 const
   alphaRanges = [
     0x00d8,  0x00f6,  #  - 
@@ -1208,3 +1213,10 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
     result = irune(toLower(ar)) - irune(toLower(br))
     if result != 0: return
   result = a.len - b.len
+
+when isMainModule:
+  let
+    someString = "öÑ"
+    someRunes = @[runeAt(someString, 0), runeAt(someString, 2)]
+    compared = (someString == $someRunes)
+  assert compared == true
diff --git a/lib/system.nim b/lib/system.nim
index db78d2740..5bcd7b02d 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -370,9 +370,34 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
   ## creates a new sequence of type ``seq[T]`` with length ``len``.
   ## This is equivalent to ``s = @[]; setlen(s, len)``, but more
   ## efficient since no reallocation is needed.
+  ##
+  ## Note that the sequence will be filled with uninitialized entries, which
+  ## can be a problem for sequences containing strings. After the creation of
+  ## the sequence you should assign entries to the sequence instead of adding
+  ## them. Example:
+  ##
+  ## .. code-block:: nimrod
+  ##   var inputStrings : seq[string]
+  ##   newSeq(inputStrings, 3)
+  ##   inputStrings[0] = "The fourth"
+  ##   inputStrings[1] = "assignment"
+  ##   inputStrings[2] = "would crash"
+  ##   #inputStrings[3] = "out of bounds"
 
 proc newSeq*[T](len = 0): seq[T] =
   ## creates a new sequence of type ``seq[T]`` with length ``len``.
+  ##
+  ## Note that the sequence will be filled with uninitialized entries, which
+  ## can be a problem for sequences containing strings. After the creation of
+  ## the sequence you should assign entries to the sequence instead of adding
+  ## them. Example:
+  ##
+  ## .. code-block:: nimrod
+  ##   var inputStrings = newSeq[string](3)
+  ##   inputStrings[0] = "The fourth"
+  ##   inputStrings[1] = "assignment"
+  ##   inputStrings[2] = "would crash"
+  ##   #inputStrings[3] = "out of bounds"
   newSeq(result, len)
 
 proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
@@ -1650,10 +1675,6 @@ const nimrodStackTrace = compileOption("stacktrace")
 # of the code
 
 var
-  dbgLineHook*: proc () {.nimcall.}
-    ## set this variable to provide a procedure that should be called before
-    ## each executed instruction. This should only be used by debuggers!
-    ## Only code compiled with the ``debugger:on`` switch calls this hook.
   globalRaiseHook*: proc (e: ref E_Base): bool {.nimcall.}
     ## with this hook you can influence exception handling on a global level.
     ## If not nil, every 'raise' statement ends up calling this hook. Ordinary
@@ -1691,13 +1712,14 @@ var
     ## continues and the program is terminated.
 
 type
-  PFrame = ptr TFrame
-  TFrame {.importc, nodecl, final.} = object
-    prev: PFrame
-    procname: CString
-    line: int # current line number
-    filename: CString
-    len: int  # length of slots (when not debugging always zero)
+  PFrame* = ptr TFrame  ## represents a runtime frame of the call stack;
+                        ## part of the debugger API.
+  TFrame* {.importc, nodecl, final.} = object ## the frame itself
+    prev*: PFrame       ## previous frame; used for chaining the call stack
+    procname*: cstring  ## name of the proc that is currently executing
+    line*: int          ## line number of the proc that is currently executing
+    filename*: cstring  ## filename of the proc that is currently executing
+    len*: int           ## length of the inspectable slots
 
 when not defined(JS):
   {.push stack_trace:off, profiler:off.}
@@ -2374,9 +2396,34 @@ proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
 proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {.
   magic: "InstantiationInfo", noSideEffect.}
   ## provides access to the compiler's instantiation stack line information.
-  ## This is only useful for advanced meta programming. See the implementation
-  ## of `assert` for an example.
-  
+  ##
+  ## This proc is mostly useful for meta programming (eg. ``assert`` template)
+  ## to retrieve information about the current filename and line number.
+  ## Example:
+  ##
+  ## .. code-block:: nimrod
+  ##   import strutils
+  ##
+  ##   template testException(exception, code: expr): stmt =
+  ##     try:
+  ##       let pos = instantiationInfo()
+  ##       discard(code)
+  ##       echo "Test failure at $1:$2 with '$3'" % [pos.filename,
+  ##         $pos.line, astToStr(code)]
+  ##       assert false, "A test expecting failure succeeded?"
+  ##     except exception:
+  ##       nil
+  ##
+  ##   proc tester(pos: int): int =
+  ##     let
+  ##       a = @[1, 2, 3]
+  ##     result = a[pos]
+  ##
+  ##   when isMainModule:
+  ##     testException(EInvalidIndex, tester(30))
+  ##     testException(EInvalidIndex, tester(1))
+  ##     # --> Test failure at example.nim:20 with 'tester(1)'
+
 proc raiseAssert*(msg: string) {.noinline.} =
   raise newException(EAssertionFailed, msg)
 
diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim
index 62d667285..eade1707f 100755
--- a/lib/system/debugger.nim
+++ b/lib/system/debugger.nim
@@ -7,187 +7,95 @@
 #    distribution, for details about the copyright.
 #
 
-# This file implements the embedded debugger that can be linked
-# with the application. Mostly we do not use dynamic memory here as that
-# would interfere with the GC and trigger ON/OFF errors if the
-# user program corrupts memory. Unfortunately, for dispaying
-# variables we use the ``system.repr()`` proc which uses Nimrod
-# strings and thus allocates memory from the heap. Pity, but
-# I do not want to implement ``repr()`` twice.
+## This file implements basic features for any debugger.
 
 type
-  TStaticStr {.pure, final.} = object
-    len: int
-    data: array[0..100, char]
-
-  TDbgState = enum
-    dbOff,        # debugger is turned off
-    dbStepInto,   # debugger is in tracing mode
-    dbStepOver,
-    dbSkipCurrent,
-    dbQuiting,    # debugger wants to quit
-    dbBreakpoints # debugger is only interested in breakpoints
-
-  TDbgBreakpoint {.final.} = object
-    low, high: int   # range from low to high; if disabled
-                     # both low and high are set to their negative values
-                     # this makes the check faster and safes memory
-    filename: cstring
-    name: TStaticStr     # name of breakpoint
-
-  TVarSlot {.compilerproc, final.} = object # variable slots used for debugger:
-    address: pointer
-    typ: PNimType
-    name: cstring   # for globals this is "module.name"
+  TVarSlot* {.compilerproc, final.} = object ## a slot in a frame
+    address*: pointer ## the variable's address
+    typ*: PNimType    ## the variable's type
+    name*: cstring    ## the variable's name; for globals this is "module.name"
 
   PExtendedFrame = ptr TExtendedFrame
-  TExtendedFrame {.final.} = object  # If the debugger is enabled the compiler
-                                     # provides an extended frame. Of course
-                                     # only slots that are
-                                     # needed are allocated and not 10_000,
-                                     # except for the global data description.
+  TExtendedFrame = object  # If the debugger is enabled the compiler
+                           # provides an extended frame. Of course
+                           # only slots that are
+                           # needed are allocated and not 10_000,
+                           # except for the global data description.
     f: TFrame
     slots: array[0..10_000, TVarSlot]
 
 var
-  dbgUser: TStaticStr   # buffer for user input; first command is ``step_into``
-                        # needs to be global cause we store the last command
-                        # in it
-  dbgState: TDbgState   # state of debugger
-  dbgBP: array[0..127, TDbgBreakpoint] # breakpoints
-  dbgBPlen: int
-  dbgBPbloom: int64  # we use a bloom filter to speed up breakpoint checking
-  dbgSkipToFrame: PFrame # frame to be skipped to
-
   dbgGlobalData: TExtendedFrame # this reserves much space, but
                                 # for now it is the most practical way
 
-  maxDisplayRecDepth: int = 5 # do not display too much data!
+proc dbgRegisterGlobal(name: cstring, address: pointer,
+                       typ: PNimType) {.compilerproc.} =
+  let i = dbgGlobalData.f.len
+  if i >= high(dbgGlobalData.slots):
+    #debugOut("[Warning] cannot register global ")
+    return
+  dbgGlobalData.slots[i].name = name
+  dbgGlobalData.slots[i].typ = typ
+  dbgGlobalData.slots[i].address = address
+  inc(dbgGlobalData.f.len)
 
-proc setLen(s: var TStaticStr, newLen=0) =
-  s.len = newLen
-  s.data[newLen] = '\0'
+proc getLocal*(frame: PFrame; slot: int): TVarSlot {.inline.} =
+  ## retrieves the meta data for the local variable at `slot`. CAUTION: An
+  ## invalid `slot` value causes a corruption!
+  result = cast[PExtendedFrame](frame).slots[slot]
 
-proc add(s: var TStaticStr, c: char) =
-  if s.len < high(s.data)-1:
-    s.data[s.len] = c
-    s.data[s.len+1] = '\0'
-    inc s.len
+proc getGlobalLen*(): int {.inline.} =
+  ## gets the number of registered globals.
+  result = dbgGlobalData.f.len
 
-proc add(s: var TStaticStr, c: cstring) =
-  var i = 0
-  while c[i] != '\0':
-    add s, c[i]
-    inc i
-
-proc assign(s: var TStaticStr, c: cstring) =
-  setLen(s)
-  add s, c
-
-proc `==`(a, b: TStaticStr): bool =
-  if a.len == b.len:
-    for i in 0 .. a.len-1:
-      if a.data[i] != b.data[i]: return false
-    return true
-
-proc `==`(a: TStaticStr, b: cstring): bool =
-  result = c_strcmp(a.data, b) == 0
-
-proc findBreakpoint(name: TStaticStr): int =
-  # returns -1 if not found
-  for i in countdown(dbgBPlen-1, 0):
-    if name == dbgBP[i].name: return i
-  return -1
-
-proc write(f: TFile, s: TStaticStr) =
-  write(f, cstring(s.data))
-
-proc ListBreakPoints() =
-  write(stdout, "*** endb| Breakpoints:\n")
-  for i in 0 .. dbgBPlen-1:
-    write(stdout, dbgBP[i].name)
-    write(stdout, ": ")
-    write(stdout, abs(dbgBP[i].low))
-    write(stdout, "..")
-    write(stdout, abs(dbgBP[i].high))
-    write(stdout, dbgBP[i].filename)
-    if dbgBP[i].low < 0:
-      write(stdout, " [disabled]\n")
-    else:
-      write(stdout, "\n")
-  write(stdout, "***\n")
-
-proc openAppend(filename: cstring): TFile =
-  var p: pointer = fopen(filename, "ab")
-  if p != nil:
-    result = cast[TFile](p)
-    write(result, "----------------------------------------\n")
-
-proc dbgRepr(p: pointer, typ: PNimType): string =
-  var cl: TReprClosure
-  initReprClosure(cl)
-  cl.recDepth = maxDisplayRecDepth
-  # locks for the GC turned out to be a bad idea...
-  # inc(recGcLock)
-  result = ""
-  reprAux(result, p, typ, cl)
-  # dec(recGcLock)
-  deinitReprClosure(cl)
-
-proc writeVariable(stream: TFile, slot: TVarSlot) =
-  write(stream, slot.name)
-  write(stream, " = ")
-  writeln(stream, dbgRepr(slot.address, slot.typ))
-
-proc ListFrame(stream: TFile, f: PExtendedFrame) =
-  write(stream, "*** endb| Frame (")
-  write(stream, f.f.len)
-  write(stream, " slots):\n")
-  for i in 0 .. f.f.len-1:
-    writeVariable(stream, f.slots[i])
-  write(stream, "***\n")
-
-proc ListVariables(stream: TFile, f: PExtendedFrame) =
-  write(stream, "*** endb| Frame (")
-  write(stream, f.f.len)
-  write(stream, " slots):\n")
-  for i in 0 .. f.f.len-1:
-    writeln(stream, f.slots[i].name)
-  write(stream, "***\n")
-
-proc debugOut(msg: cstring) =
-  # the *** *** markers are for easy recognition of debugger
-  # output for external frontends.
-  write(stdout, "*** endb| ")
-  write(stdout, msg)
-  write(stdout, "***\n")
-
-proc dbgFatal(msg: cstring) =
-  debugOut(msg)
-  dbgAborting = True # the debugger wants to abort
-  quit(1)
-
-proc findVariable(frame: PExtendedFrame, varname: cstring): int =
-  for i in 0 .. frame.f.len - 1:
-    if c_strcmp(frame.slots[i].name, varname) == 0: return i
-  return -1
-
-proc dbgShowCurrentProc(dbgFramePointer: PFrame) =
-  if dbgFramePointer != nil:
-    write(stdout, "*** endb| now in proc: ")
-    write(stdout, dbgFramePointer.procname)
-    write(stdout, " ***\n")
-  else:
-    write(stdout, "*** endb| (proc name not available) ***\n")
+proc getGlobal*(slot: int): TVarSlot {.inline.} =
+  ## retrieves the meta data for the global variable at `slot`. CAUTION: An
+  ## invalid `slot` value causes a corruption!
+  result = dbgGlobalData.slots[slot]
+
+# ------------------- breakpoint support ------------------------------------
 
-proc dbgShowExecutionPoint() =
-  write(stdout, "*** endb| ")
-  write(stdout, framePtr.filename)
-  write(stdout, "(")
-  write(stdout, framePtr.line)
-  write(stdout, ") ")
-  write(stdout, framePtr.procname)
-  write(stdout, " ***\n")
+type
+  TBreakpoint* = object  ## represents a break point
+    low*, high*: int     ## range from low to high; if disabled
+                         ## both low and high are set to their negative values
+    filename*: cstring   ## the filename of the breakpoint
+
+var
+  dbgBP: array[0..127, TBreakpoint] # breakpoints
+  dbgBPlen: int
+  dbgBPbloom: int64  # we use a bloom filter to speed up breakpoint checking
+  
+  dbgFilenames*: array[0..300, cstring] ## registered filenames;
+                                        ## 'nil' terminated
+  dbgFilenameLen: int
+
+proc dbgRegisterFilename(filename: cstring) {.compilerproc.} =
+  # XXX we could check for duplicates here for DLL support
+  dbgFilenames[dbgFilenameLen] = filename
+  inc dbgFilenameLen
+
+proc dbgRegisterBreakpoint(line: int,
+                           filename, name: cstring) {.compilerproc.} =
+  let x = dbgBPlen
+  if x >= high(dbgBP):
+    #debugOut("[Warning] cannot register breakpoint")
+    return
+  inc(dbgBPlen)
+  dbgBP[x].filename = filename
+  dbgBP[x].low = line
+  dbgBP[x].high = line
+  dbgBPbloom = dbgBPbloom or line
+
+proc addBreakpoint*(filename: cstring, lo, hi: int): bool =
+  let x = dbgBPlen
+  if x >= high(dbgBP): return false
+  inc(dbgBPlen)
+  result = true
+  dbgBP[x].filename = filename
+  dbgBP[x].low = lo
+  dbgBP[x].high = hi
+  for line in lo..hi: dbgBPbloom = dbgBPbloom or line
 
 const
   FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2)
@@ -216,337 +124,29 @@ proc fileMatches(c, bp: cstring): bool =
     inc(i)
   return true
 
-proc dbgBreakpointReached(line: int): int =
-  for i in 0..dbgBPlen-1:
-    if line >= dbgBP[i].low and line <= dbgBP[i].high and
-        fileMatches(framePtr.filename, dbgBP[i].filename): return i
-  return -1
-
-proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
-  result = start
-  # skip whitespace:
-  while src[result] in {'\t', ' '}: inc(result)
-  while True:
-    case src[result]
-    of 'a'..'z', '0'..'9': add(a, src[result])
-    of '_': nil # just skip it
-    of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a')))
-    else: break
-    inc(result)
-
-proc scanWord(src: cstring, a: var TStaticStr, start: int): int =
-  setlen(a)
-  result = scanAndAppendWord(src, a, start)
-
-proc scanFilename(src: cstring, a: var TStaticStr, start: int): int =
-  result = start
-  setLen a
-  # skip whitespace:
-  while src[result] in {'\t', ' '}: inc(result)
-  while src[result] notin {'\t', ' ', '\0'}:
-    add(a, src[result])
-    inc(result)
-
-proc scanNumber(src: cstring, a: var int, start: int): int =
-  result = start
-  a = 0
-  while src[result] in {'\t', ' '}: inc(result)
-  while true:
-    case src[result]
-    of '0'..'9': a = a * 10 + ord(src[result]) - ord('0')
-    of '_': nil # skip underscores (nice for long line numbers)
-    else: break
-    inc(result)
-
-proc dbgHelp() =
-  debugOut("""
-list of commands (see the manual for further help):
-              GENERAL
-h, help                 display this help message
-q, quit                 quit the debugger and the program
-<ENTER>                 repeat the previous debugger command
-              EXECUTING
-s, step                 single step, stepping into routine calls
-n, next                 single step, without stepping into routine calls
-f, skipcurrent          continue execution until the current routine finishes
-c, continue, r, run     continue execution until the next breakpoint
-i, ignore               continue execution, ignore all breakpoints
-              BREAKPOINTS
-b, break <name> [fromline [toline]] [file]
-                        set a new breakpoint named 'name' for line and file
-                        if line or file are omitted the current one is used
-breakpoints             display the entire breakpoint list
-disable <name>          disable a breakpoint
-enable  <name>          enable a breakpoint
-              DATA DISPLAY
-e, eval <expr>          evaluate the expression <expr>
-o, out <file> <expr>    evaluate <expr> and write it to <file>
-w, where                display the current execution point
-stackframe [file]       display current stack frame [and write it to file]
-u, up                   go up in the call stack
-d, down                 go down in the call stack
-bt, backtrace           display the entire call stack
-l, locals               display available local variables
-g, globals              display available global variables
-maxdisplay <integer>    set the display's recursion maximum
-""")
-
-proc InvalidCommand() =
-  debugOut("[Warning] invalid command ignored (type 'h' for help) ")
-
-proc hasExt(s: cstring): bool =
-  # returns true if s has a filename extension
-  var i = 0
-  while s[i] != '\0':
-    if s[i] == '.': return true
-    inc i
-
-proc setBreakPoint(s: cstring, start: int) =
-  var dbgTemp: TStaticStr
-  var i = scanWord(s, dbgTemp, start)
-  if i <= start:
-    InvalidCommand()
-    return
-  if dbgBPlen >= high(dbgBP):
-    debugOut("[Warning] no breakpoint could be set; out of breakpoint space ")
-    return
-  var x = dbgBPlen
-  inc(dbgBPlen)
-  dbgBP[x].name = dbgTemp
-  i = scanNumber(s, dbgBP[x].low, i)
-  if dbgBP[x].low == 0:
-    # set to current line:
-    dbgBP[x].low = framePtr.line
-  i = scanNumber(s, dbgBP[x].high, i)
-  if dbgBP[x].high == 0: # set to low:
-    dbgBP[x].high = dbgBP[x].low
-  for line in dbgBP[x].low .. dbgBP[x].high: dbgBPbloom = dbgBPbloom or line
-  i = scanFilename(s, dbgTemp, i)
-  if dbgTemp.len != 0:
-    debugOut("[Warning] explicit filename for breakpoint not supported")
-    when false:
-      if not hasExt(dbgTemp.data): add(dbgTemp, ".nim")
-      dbgBP[x].filename = dbgTemp
-    dbgBP[x].filename = framePtr.filename
-  else: # use current filename
-    dbgBP[x].filename = framePtr.filename
-  # skip whitespace:
-  while s[i] in {' ', '\t'}: inc(i)
-  if s[i] != '\0':
-    dec(dbgBPLen) # remove buggy breakpoint
-    InvalidCommand()
-
-proc BreakpointSetEnabled(s: cstring, start, enabled: int) =
-  var dbgTemp: TStaticStr
-  var i = scanWord(s, dbgTemp, start)
-  if i <= start:
-    InvalidCommand()
-    return
-  var x = findBreakpoint(dbgTemp)
-  if x < 0: debugOut("[Warning] breakpoint does not exist ")
-  elif enabled * dbgBP[x].low < 0: # signs are different?
-    dbgBP[x].low = -dbgBP[x].low
-    dbgBP[x].high = -dbgBP[x].high
-
-proc dbgEvaluate(stream: TFile, s: cstring, start: int,
-                 currFrame: PExtendedFrame) =
-  var dbgTemp: tstaticstr
-  var i = scanWord(s, dbgTemp, start)
-  while s[i] in {' ', '\t'}: inc(i)
-  var f = currFrame
-  if s[i] == '.':
-    inc(i) # skip '.'
-    add(dbgTemp, '.')
-    i = scanAndAppendWord(s, dbgTemp, i)
-    # search for global var:
-    f = addr(dbgGlobalData)
-  if s[i] != '\0':
-    debugOut("[Warning] could not parse expr ")
-    return
-  var j = findVariable(f, dbgTemp.data)
-  if j < 0:
-    debugOut("[Warning] could not find variable ")
-    return
-  writeVariable(stream, f.slots[j])
+proc canonFilename*(filename: cstring): cstring =
+  ## returns 'nil' if the filename cannot be found.
+  for i in 0 .. <dbgFilenameLen:
+    result = dbgFilenames[i]
+    if fileMatches(result, filename): return result
+  result = nil
 
-proc dbgOut(s: cstring, start: int, currFrame: PExtendedFrame) =
-  var dbgTemp: tstaticstr
-  var i = scanFilename(s, dbgTemp, start)
-  if dbgTemp.len == 0:
-    InvalidCommand()
-    return
-  var stream = openAppend(dbgTemp.data)
-  if stream == nil:
-    debugOut("[Warning] could not open or create file ")
-    return
-  dbgEvaluate(stream, s, i, currFrame)
-  close(stream)
-
-proc dbgStackFrame(s: cstring, start: int, currFrame: PExtendedFrame) =
-  var dbgTemp: TStaticStr
-  var i = scanFilename(s, dbgTemp, start)
-  if dbgTemp.len == 0:
-    # just write it to stdout:
-    ListFrame(stdout, currFrame)
-  else:
-    var stream = openAppend(dbgTemp.data)
-    if stream == nil:
-      debugOut("[Warning] could not open or create file ")
-      return
-    ListFrame(stream, currFrame)
-    close(stream)
-
-proc readLine(f: TFile, line: var TStaticStr): bool =
-  while True:
-    var c = fgetc(f)
-    if c < 0'i32:
-      if line.len > 0: break
-      else: return false
-    if c == 10'i32: break # LF
-    if c == 13'i32:  # CR
-      c = fgetc(f) # is the next char LF?
-      if c != 10'i32: ungetc(c, f) # no, put the character back
-      break
-    add line, chr(int(c))
-  result = true
+iterator listBreakpoints*(): ptr TBreakpoint =
+  ## lists all breakpoints.
+  for i in 0..dbgBPlen-1: yield addr(dbgBP[i])
 
-proc dbgWriteStackTrace(f: PFrame)
-proc CommandPrompt() =
-  # if we return from this routine, user code executes again
-  var
-    again = True
-    dbgFramePtr = framePtr # for going down and up the stack
-    dbgDown = 0 # how often we did go down
-    dbgTemp: TStaticStr
-
-  while again:
-    write(stdout, "*** endb| >>")
-    let oldLen = dbgUser.len
-    dbgUser.len = 0
-    if not readLine(stdin, dbgUser): break
-    if dbgUser.len == 0: dbgUser.len = oldLen
-    # now look what we have to do:
-    var i = scanWord(dbgUser.data, dbgTemp, 0)
-    template `?`(x: expr): expr = dbgTemp == cstring(x)
-    if ?"s" or ?"step":
-      dbgState = dbStepInto
-      again = false
-    elif ?"n" or ?"next":
-      dbgState = dbStepOver
-      dbgSkipToFrame = framePtr
-      again = false
-    elif ?"f" or ?"skipcurrent":
-      dbgState = dbSkipCurrent
-      dbgSkipToFrame = framePtr.prev
-      again = false
-    elif ?"c" or ?"continue" or ?"r" or ?"run":
-      dbgState = dbBreakpoints
-      again = false
-    elif ?"i" or ?"ignore":
-      dbgState = dbOff
-      again = false
-    elif ?"h" or ?"help":
-      dbgHelp()
-    elif ?"q" or ?"quit":
-      dbgState = dbQuiting
-      dbgAborting = True
-      again = false
-      quit(1) # BUGFIX: quit with error code > 0
-    elif ?"e" or ?"eval":
-      dbgEvaluate(stdout, dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
-    elif ?"o" or ?"out":
-      dbgOut(dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
-    elif ?"stackframe":
-      dbgStackFrame(dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
-    elif ?"w" or ?"where":
-      dbgShowExecutionPoint()
-    elif ?"l" or ?"locals":
-      ListVariables(stdout, cast[PExtendedFrame](dbgFramePtr))
-    elif ?"g" or ?"globals":
-      ListVariables(stdout, addr(dbgGlobalData))
-    elif ?"u" or ?"up":
-      if dbgDown <= 0:
-        debugOut("[Warning] cannot go up any further ")
-      else:
-        dbgFramePtr = framePtr
-        for j in 0 .. dbgDown-2: # BUGFIX
-          dbgFramePtr = dbgFramePtr.prev
-        dec(dbgDown)
-      dbgShowCurrentProc(dbgFramePtr)
-    elif ?"d" or ?"down":
-      if dbgFramePtr != nil:
-        inc(dbgDown)
-        dbgFramePtr = dbgFramePtr.prev
-        dbgShowCurrentProc(dbgFramePtr)
-      else:
-        debugOut("[Warning] cannot go down any further ")
-    elif ?"bt" or ?"backtrace":
-      dbgWriteStackTrace(framePtr)
-    elif ?"b" or ?"break":
-      setBreakPoint(dbgUser.data, i)
-    elif ?"breakpoints":
-      ListBreakPoints()
-    elif ?"disable":
-      BreakpointSetEnabled(dbgUser.data, i, -1)
-    elif ?"enable":
-      BreakpointSetEnabled(dbgUser.data, i, +1)
-    elif ?"maxdisplay":
-      var parsed: int
-      i = scanNumber(dbgUser.data, parsed, i)
-      if dbgUser.data[i-1] in {'0'..'9'}:
-        if parsed == 0: maxDisplayRecDepth = -1
-        else: maxDisplayRecDepth = parsed
-      else:
-        InvalidCommand()
-    else: InvalidCommand()
-
-proc endbStep() =
-  # we get into here if an unhandled exception has been raised
-  # XXX: do not allow the user to run the program any further?
-  # XXX: BUG: the frame is lost here!
-  dbgShowExecutionPoint()
-  CommandPrompt()
-
-proc checkForBreakpoint(line: int) =
-  if (dbgBPbloom and line) != line: return
-  let i = dbgBreakpointReached(line)
-  if i >= 0:
-    write(stdout, "*** endb| reached ")
-    write(stdout, dbgBP[i].name)
-    write(stdout, " in ")
-    write(stdout, framePtr.filename)
-    write(stdout, "(")
-    write(stdout, framePtr.line)
-    write(stdout, ") ")
-    write(stdout, framePtr.procname)
-    write(stdout, " ***\n")
-    CommandPrompt()
-
-# interface to the user program:
+proc isActive*(b: ptr TBreakpoint): bool = b.low > 0
+proc flip*(b: ptr TBreakpoint) =
+  ## enables or disables 'b' depending on its current state.
+  b.low = -b.low; b.high = -b.high
 
-proc dbgRegisterBreakpoint(line: int,
-                           filename, name: cstring) {.compilerproc.} =
-  let x = dbgBPlen
-  if x >= high(dbgBP):
-    debugOut("[Warning] cannot register breakpoint")
-    return
-  inc(dbgBPlen)
-  dbgBP[x].name.assign(name)
-  dbgBP[x].filename = filename
-  dbgBP[x].low = line
-  dbgBP[x].high = line
-  dbgBPbloom = dbgBPbloom or line
+proc checkBreakpoints*(filename: cstring, line: int): ptr TBreakpoint =
+  ## in which breakpoint (if any) we are.
+  if (dbgBPbloom and line) != line: return nil
+  for b in listBreakpoints():
+    if line >= b.low and line <= b.high and filename == b.filename: return b
 
-proc dbgRegisterGlobal(name: cstring, address: pointer,
-                       typ: PNimType) {.compilerproc.} =
-  let i = dbgGlobalData.f.len
-  if i >= high(dbgGlobalData.slots):
-    debugOut("[Warning] cannot register global ")
-    return
-  dbgGlobalData.slots[i].name = name
-  dbgGlobalData.slots[i].typ = typ
-  dbgGlobalData.slots[i].address = address
-  inc(dbgGlobalData.f.len)
+# ------------------- watchpoint support ------------------------------------
 
 type
   THash = int
@@ -665,7 +265,7 @@ proc dbgRegisterWatchpoint(address: pointer, name: cstring,
       Watchpoints[i].address = address
       return
   if L >= watchPoints.high:
-    debugOut("[Warning] cannot register watchpoint")
+    #debugOut("[Warning] cannot register watchpoint")
     return
   Watchpoints[L].name = name
   Watchpoints[L].address = address
@@ -676,99 +276,28 @@ proc dbgRegisterWatchpoint(address: pointer, name: cstring,
 proc dbgUnregisterWatchpoints*() =
   WatchpointsLen = 0
 
-proc dbgWriteStackTrace(f: PFrame) =
-  const
-    firstCalls = 32
-  var
-    it = f
-    i = 0
-    total = 0
-    tempFrames: array [0..127, PFrame]
-  # setup long head:
-  while it != nil and i <= high(tempFrames)-firstCalls:
-    tempFrames[i] = it
-    inc(i)
-    inc(total)
-    it = it.prev
-  # go up the stack to count 'total':
-  var b = it
-  while it != nil:
-    inc(total)
-    it = it.prev
-  var skipped = 0
-  if total > len(tempFrames):
-    # skip N
-    skipped = total-i-firstCalls+1
-    for j in 1..skipped:
-      if b != nil: b = b.prev
-    # create '...' entry:
-    tempFrames[i] = nil
-    inc(i)
-  # setup short tail:
-  while b != nil and i <= high(tempFrames):
-    tempFrames[i] = b
-    inc(i)
-    b = b.prev
-  for j in countdown(i-1, 0):
-    if tempFrames[j] == nil: 
-      write(stdout, "(")
-      write(stdout, skipped)
-      write(stdout, " calls omitted) ...")
-    else:
-      write(stdout, tempFrames[j].filename)
-      if tempFrames[j].line > 0:
-        write(stdout, '(')
-        write(stdout, tempFrames[j].line)
-        write(stdout, ')')
-      write(stdout, ' ')
-      write(stdout, tempFrames[j].procname)
-    write(stdout, "\n")
-  
-proc strstr(s1, s2: cstring): cstring {.importc, header: "<string.h>".}
+var
+  dbgLineHook*: proc () {.nimcall.}
+    ## set this variable to provide a procedure that should be called before
+    ## each executed instruction. This should only be used by debuggers!
+    ## Only code compiled with the ``debugger:on`` switch calls this hook.
 
-proc interestingFilename(filename: cstring): bool =
-  #result = strstr(filename, "/rst.nim") == nil
-  result = true
+  dbgWatchpointHook*: proc (watchpointName: cstring) {.nimcall.}
   
 proc checkWatchpoints =
   let L = WatchpointsLen
   for i in 0.. <L:
     let newHash = genericHash(Watchpoints[i].address, Watchpoints[i].typ)
     if newHash != Watchpoints[i].oldValue:
-      if interestingFilename(framePtr.filename):
-        dbgWriteStackTrace(framePtr)
-        debugOut(Watchpoints[i].name)
+      dbgWatchpointHook(Watchpoints[i].name)
       Watchpoints[i].oldValue = newHash
 
-proc endb(line: int, file: cstring) {.compilerproc.} =
+proc endb(line: int, file: cstring) {.compilerproc, noinline.} =
   # This proc is called before every Nimrod code line!
-  # Thus, it must have as few parameters as possible to keep the
-  # code size small!
-  # Check if we are at an enabled breakpoint or "in the mood"
   if framePtr == nil: return
-  let oldState = dbgState
-  checkWatchpoints()
+  if dbgWatchpointHook != nil: checkWatchpoints()
   framePtr.line = line # this is done here for smaller code size!
   framePtr.filename = file
   if dbgLineHook != nil: dbgLineHook()
-  case oldState
-  of dbStepInto:
-    # we really want the command prompt here:
-    dbgShowExecutionPoint()
-    CommandPrompt()
-  of dbSkipCurrent, dbStepOver: # skip current routine
-    if framePtr == dbgSkipToFrame:
-      dbgShowExecutionPoint()
-      CommandPrompt()
-    else: # breakpoints are wanted though (I guess)
-      checkForBreakpoint(line)
-  of dbBreakpoints:
-    # debugger is only interested in breakpoints
-    checkForBreakpoint(line)
-  else: nil
-
-proc initDebugger {.inline.} =
-  dbgState = dbStepInto
-  dbgUser.len = 1
-  dbgUser.data[0] = 's'
 
+include "system/endb"
diff --git a/lib/system/endb.nim b/lib/system/endb.nim
new file mode 100644
index 000000000..2d6a25824
--- /dev/null
+++ b/lib/system/endb.nim
@@ -0,0 +1,538 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# This file implements the embedded debugger that can be linked
+# with the application. Mostly we do not use dynamic memory here as that
+# would interfere with the GC and trigger ON/OFF errors if the
+# user program corrupts memory. Unfortunately, for dispaying
+# variables we use the ``system.repr()`` proc which uses Nimrod
+# strings and thus allocates memory from the heap. Pity, but
+# I do not want to implement ``repr()`` twice.
+
+const
+  EndbBeg = "*** endb"
+  EndbEnd = "***\n"
+
+type
+  TStaticStr = object
+    len: int
+    data: array[0..100, char]
+
+  TBreakpointFilename = object
+    b: ptr TBreakpoint
+    filename: TStaticStr
+
+  TDbgState = enum
+    dbOff,        # debugger is turned off
+    dbStepInto,   # debugger is in tracing mode
+    dbStepOver,
+    dbSkipCurrent,
+    dbQuiting,    # debugger wants to quit
+    dbBreakpoints # debugger is only interested in breakpoints
+
+var
+  dbgUser: TStaticStr   # buffer for user input; first command is ``step_into``
+                        # needs to be global cause we store the last command
+                        # in it
+  dbgState: TDbgState   # state of debugger
+  dbgSkipToFrame: PFrame # frame to be skipped to
+
+  maxDisplayRecDepth: int = 5 # do not display too much data!
+
+  brkPoints: array[0..127, TBreakpointFilename]
+
+proc setLen(s: var TStaticStr, newLen=0) =
+  s.len = newLen
+  s.data[newLen] = '\0'
+
+proc add(s: var TStaticStr, c: char) =
+  if s.len < high(s.data)-1:
+    s.data[s.len] = c
+    s.data[s.len+1] = '\0'
+    inc s.len
+
+proc add(s: var TStaticStr, c: cstring) =
+  var i = 0
+  while c[i] != '\0':
+    add s, c[i]
+    inc i
+
+proc assign(s: var TStaticStr, c: cstring) =
+  setLen(s)
+  add s, c
+
+proc `==`(a, b: TStaticStr): bool =
+  if a.len == b.len:
+    for i in 0 .. a.len-1:
+      if a.data[i] != b.data[i]: return false
+    return true
+
+proc `==`(a: TStaticStr, b: cstring): bool =
+  result = c_strcmp(a.data, b) == 0
+
+proc write(f: TFile, s: TStaticStr) =
+  write(f, cstring(s.data))
+
+proc ListBreakPoints() =
+  write(stdout, EndbBeg)
+  write(stdout, "| Breakpoints:\n")
+  for b in listBreakpoints():
+    write(stdout, abs(b.low))
+    if b.high != b.low:
+      write(stdout, "..")
+      write(stdout, abs(b.high))
+    write(stdout, " ")
+    write(stdout, b.filename)
+    if b.isActive:
+      write(stdout, " [disabled]\n")
+    else:
+      write(stdout, "\n")
+  write(stdout, EndbEnd)
+
+proc openAppend(filename: cstring): TFile =
+  var p: pointer = fopen(filename, "ab")
+  if p != nil:
+    result = cast[TFile](p)
+    write(result, "----------------------------------------\n")
+
+proc dbgRepr(p: pointer, typ: PNimType): string =
+  var cl: TReprClosure
+  initReprClosure(cl)
+  cl.recDepth = maxDisplayRecDepth
+  # locks for the GC turned out to be a bad idea...
+  # inc(recGcLock)
+  result = ""
+  reprAux(result, p, typ, cl)
+  # dec(recGcLock)
+  deinitReprClosure(cl)
+
+proc writeVariable(stream: TFile, slot: TVarSlot) =
+  write(stream, slot.name)
+  write(stream, " = ")
+  writeln(stream, dbgRepr(slot.address, slot.typ))
+
+proc ListFrame(stream: TFile, f: PFrame) =
+  write(stream, EndbBeg)
+  write(stream, "| Frame (")
+  write(stream, f.len)
+  write(stream, " slots):\n")
+  for i in 0 .. f.len-1:
+    writeln(stream, getLocal(f, i).name)
+  write(stream, EndbEnd)
+
+proc ListLocals(stream: TFile, f: PFrame) =
+  write(stream, EndbBeg)
+  write(stream, "| Frame (")
+  write(stream, f.len)
+  write(stream, " slots):\n")
+  for i in 0 .. f.len-1:
+    writeVariable(stream, getLocal(f, i))
+  write(stream, EndbEnd)
+
+proc ListGlobals(stream: TFile) =
+  write(stream, EndbBeg)
+  write(stream, "| Globals:\n")
+  for i in 0 .. getGlobalLen()-1:
+    writeln(stream, getGlobal(i).name)
+  write(stream, EndbEnd)
+
+proc debugOut(msg: cstring) =
+  # the *** *** markers are for easy recognition of debugger
+  # output for external frontends.
+  write(stdout, EndbBeg)
+  write(stdout, "| ")
+  write(stdout, msg)
+  write(stdout, EndbEnd)
+
+proc dbgFatal(msg: cstring) =
+  debugOut(msg)
+  dbgAborting = True # the debugger wants to abort
+  quit(1)
+
+proc dbgShowCurrentProc(dbgFramePointer: PFrame) =
+  if dbgFramePointer != nil:
+    write(stdout, "*** endb| now in proc: ")
+    write(stdout, dbgFramePointer.procname)
+    write(stdout, " ***\n")
+  else:
+    write(stdout, "*** endb| (proc name not available) ***\n")
+
+proc dbgShowExecutionPoint() =
+  write(stdout, "*** endb| ")
+  write(stdout, framePtr.filename)
+  write(stdout, "(")
+  write(stdout, framePtr.line)
+  write(stdout, ") ")
+  write(stdout, framePtr.procname)
+  write(stdout, " ***\n")
+
+proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
+  result = start
+  # skip whitespace:
+  while src[result] in {'\t', ' '}: inc(result)
+  while True:
+    case src[result]
+    of 'a'..'z', '0'..'9': add(a, src[result])
+    of '_': nil # just skip it
+    of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a')))
+    else: break
+    inc(result)
+
+proc scanWord(src: cstring, a: var TStaticStr, start: int): int =
+  setlen(a)
+  result = scanAndAppendWord(src, a, start)
+
+proc scanFilename(src: cstring, a: var TStaticStr, start: int): int =
+  result = start
+  setLen a
+  while src[result] in {'\t', ' '}: inc(result)
+  while src[result] notin {'\t', ' ', '\0'}:
+    add(a, src[result])
+    inc(result)
+
+proc scanNumber(src: cstring, a: var int, start: int): int =
+  result = start
+  a = 0
+  while src[result] in {'\t', ' '}: inc(result)
+  while true:
+    case src[result]
+    of '0'..'9': a = a * 10 + ord(src[result]) - ord('0')
+    of '_': nil # skip underscores (nice for long line numbers)
+    else: break
+    inc(result)
+
+proc dbgHelp() =
+  debugOut("""
+list of commands (see the manual for further help):
+              GENERAL
+h, help                 display this help message
+q, quit                 quit the debugger and the program
+<ENTER>                 repeat the previous debugger command
+              EXECUTING
+s, step                 single step, stepping into routine calls
+n, next                 single step, without stepping into routine calls
+f, skipcurrent          continue execution until the current routine finishes
+c, continue, r, run     continue execution until the next breakpoint
+i, ignore               continue execution, ignore all breakpoints
+              BREAKPOINTS
+b, break [fromline [toline]] [file]
+                        set a new breakpoint for line and file
+                        if line or file are omitted the current one is used
+breakpoints             display the entire breakpoint list
+toggle fromline [file]  enable or disable a breakpoint
+filenames               list all valid filenames
+              DATA DISPLAY
+e, eval <expr>          evaluate the expression <expr>
+o, out <file> <expr>    evaluate <expr> and write it to <file>
+w, where                display the current execution point
+stackframe [file]       display current stack frame [and write it to file]
+u, up                   go up in the call stack
+d, down                 go down in the call stack
+bt, backtrace           display the entire call stack
+l, locals               display available local variables
+g, globals              display available global variables
+maxdisplay <integer>    set the display's recursion maximum
+""")
+
+proc InvalidCommand() =
+  debugOut("[Warning] invalid command ignored (type 'h' for help) ")
+
+proc hasExt(s: cstring): bool =
+  # returns true if s has a filename extension
+  var i = 0
+  while s[i] != '\0':
+    if s[i] == '.': return true
+    inc i
+
+proc parseBreakpoint(s: cstring, start: int): TBreakpoint =
+  var dbgTemp: TStaticStr
+  var i = scanNumber(s, result.low, start)
+  if result.low == 0: result.low = framePtr.line
+  i = scanNumber(s, result.high, i)
+  if result.high == 0: result.high = result.low
+  i = scanFilename(s, dbgTemp, i)
+  if dbgTemp.len != 0:
+    if not hasExt(dbgTemp.data): add(dbgTemp, ".nim")
+    result.filename = canonFilename(dbgTemp.data.cstring)
+    if result.filename.isNil:
+      debugOut("[Warning] no breakpoint could be set; unknown filename ")
+      return
+  else:
+    result.filename = framePtr.filename
+
+proc createBreakPoint(s: cstring, start: int) =
+  let br = parseBreakpoint(s, start)
+  if not br.filename.isNil:
+    if not addBreakpoint(br.filename, br.low, br.high):
+      debugOut("[Warning] no breakpoint could be set; out of breakpoint space ")
+
+proc BreakpointToggle(s: cstring, start: int) =
+  var a = parseBreakpoint(s, start)
+  if not a.filename.isNil:
+    var b = checkBreakpoints(a.filename, a.low)
+    if not b.isNil: b.flip
+    else: debugOut("[Warning] unknown breakpoint ")
+
+proc dbgEvaluate(stream: TFile, s: cstring, start: int, f: PFrame) =
+  var dbgTemp: tstaticstr
+  var i = scanWord(s, dbgTemp, start)
+  while s[i] in {' ', '\t'}: inc(i)
+  var v: TVarSlot
+  if s[i] == '.':
+    inc(i)
+    add(dbgTemp, '.')
+    i = scanAndAppendWord(s, dbgTemp, i)
+    for i in 0 .. getGlobalLen()-1:
+      let v = getGlobal(i)
+      if c_strcmp(v.name, dbgTemp.data) == 0:
+        writeVariable(stream, v)
+  else:
+    for i in 0 .. f.len-1:
+      let v = getLocal(f, i)
+      if c_strcmp(v.name, dbgTemp.data) == 0:
+        writeVariable(stream, v)  
+
+proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
+  var dbgTemp: tstaticstr
+  var i = scanFilename(s, dbgTemp, start)
+  if dbgTemp.len == 0:
+    InvalidCommand()
+    return
+  var stream = openAppend(dbgTemp.data)
+  if stream == nil:
+    debugOut("[Warning] could not open or create file ")
+    return
+  dbgEvaluate(stream, s, i, currFrame)
+  close(stream)
+
+proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
+  var dbgTemp: TStaticStr
+  var i = scanFilename(s, dbgTemp, start)
+  if dbgTemp.len == 0:
+    # just write it to stdout:
+    ListFrame(stdout, currFrame)
+  else:
+    var stream = openAppend(dbgTemp.data)
+    if stream == nil:
+      debugOut("[Warning] could not open or create file ")
+      return
+    ListFrame(stream, currFrame)
+    close(stream)
+
+proc readLine(f: TFile, line: var TStaticStr): bool =
+  while True:
+    var c = fgetc(f)
+    if c < 0'i32:
+      if line.len > 0: break
+      else: return false
+    if c == 10'i32: break # LF
+    if c == 13'i32:  # CR
+      c = fgetc(f) # is the next char LF?
+      if c != 10'i32: ungetc(c, f) # no, put the character back
+      break
+    add line, chr(int(c))
+  result = true
+
+proc ListFilenames() =
+  write(stdout, EndbBeg)
+  write(stdout, "| Files:\n")
+  var i = 0
+  while true:
+    let x = dbgFilenames[i]
+    if x.isNil: break
+    write(stdout, x)
+    write(stdout, "\n")
+    inc i
+  write(stdout, EndbEnd)
+
+proc dbgWriteStackTrace(f: PFrame)
+proc CommandPrompt() =
+  # if we return from this routine, user code executes again
+  var
+    again = True
+    dbgFramePtr = framePtr # for going down and up the stack
+    dbgDown = 0 # how often we did go down
+    dbgTemp: TStaticStr
+
+  while again:
+    write(stdout, "*** endb| >>")
+    let oldLen = dbgUser.len
+    dbgUser.len = 0
+    if not readLine(stdin, dbgUser): break
+    if dbgUser.len == 0: dbgUser.len = oldLen
+    # now look what we have to do:
+    var i = scanWord(dbgUser.data, dbgTemp, 0)
+    template `?`(x: expr): expr = dbgTemp == cstring(x)
+    if ?"s" or ?"step":
+      dbgState = dbStepInto
+      again = false
+    elif ?"n" or ?"next":
+      dbgState = dbStepOver
+      dbgSkipToFrame = framePtr
+      again = false
+    elif ?"f" or ?"skipcurrent":
+      dbgState = dbSkipCurrent
+      dbgSkipToFrame = framePtr.prev
+      again = false
+    elif ?"c" or ?"continue" or ?"r" or ?"run":
+      dbgState = dbBreakpoints
+      again = false
+    elif ?"i" or ?"ignore":
+      dbgState = dbOff
+      again = false
+    elif ?"h" or ?"help":
+      dbgHelp()
+    elif ?"q" or ?"quit":
+      dbgState = dbQuiting
+      dbgAborting = True
+      again = false
+      quit(1) # BUGFIX: quit with error code > 0
+    elif ?"e" or ?"eval":
+      dbgEvaluate(stdout, dbgUser.data, i, dbgFramePtr)
+    elif ?"o" or ?"out":
+      dbgOut(dbgUser.data, i, dbgFramePtr)
+    elif ?"stackframe":
+      dbgStackFrame(dbgUser.data, i, dbgFramePtr)
+    elif ?"w" or ?"where":
+      dbgShowExecutionPoint()
+    elif ?"l" or ?"locals":
+      ListLocals(stdout, dbgFramePtr)
+    elif ?"g" or ?"globals":
+      ListGlobals(stdout)
+    elif ?"u" or ?"up":
+      if dbgDown <= 0:
+        debugOut("[Warning] cannot go up any further ")
+      else:
+        dbgFramePtr = framePtr
+        for j in 0 .. dbgDown-2: # BUGFIX
+          dbgFramePtr = dbgFramePtr.prev
+        dec(dbgDown)
+      dbgShowCurrentProc(dbgFramePtr)
+    elif ?"d" or ?"down":
+      if dbgFramePtr != nil:
+        inc(dbgDown)
+        dbgFramePtr = dbgFramePtr.prev
+        dbgShowCurrentProc(dbgFramePtr)
+      else:
+        debugOut("[Warning] cannot go down any further ")
+    elif ?"bt" or ?"backtrace":
+      dbgWriteStackTrace(framePtr)
+    elif ?"b" or ?"break":
+      createBreakPoint(dbgUser.data, i)
+    elif ?"breakpoints":
+      ListBreakPoints()
+    elif ?"toggle":
+      BreakpointToggle(dbgUser.data, i)
+    elif ?"filenames":
+      ListFilenames()
+    elif ?"maxdisplay":
+      var parsed: int
+      i = scanNumber(dbgUser.data, parsed, i)
+      if dbgUser.data[i-1] in {'0'..'9'}:
+        if parsed == 0: maxDisplayRecDepth = -1
+        else: maxDisplayRecDepth = parsed
+      else:
+        InvalidCommand()
+    else: InvalidCommand()
+
+proc endbStep() =
+  # we get into here if an unhandled exception has been raised
+  # XXX: do not allow the user to run the program any further?
+  # XXX: BUG: the frame is lost here!
+  dbgShowExecutionPoint()
+  CommandPrompt()
+
+proc dbgWriteStackTrace(f: PFrame) =
+  const
+    firstCalls = 32
+  var
+    it = f
+    i = 0
+    total = 0
+    tempFrames: array [0..127, PFrame]
+  # setup long head:
+  while it != nil and i <= high(tempFrames)-firstCalls:
+    tempFrames[i] = it
+    inc(i)
+    inc(total)
+    it = it.prev
+  # go up the stack to count 'total':
+  var b = it
+  while it != nil:
+    inc(total)
+    it = it.prev
+  var skipped = 0
+  if total > len(tempFrames):
+    # skip N
+    skipped = total-i-firstCalls+1
+    for j in 1..skipped:
+      if b != nil: b = b.prev
+    # create '...' entry:
+    tempFrames[i] = nil
+    inc(i)
+  # setup short tail:
+  while b != nil and i <= high(tempFrames):
+    tempFrames[i] = b
+    inc(i)
+    b = b.prev
+  for j in countdown(i-1, 0):
+    if tempFrames[j] == nil: 
+      write(stdout, "(")
+      write(stdout, skipped)
+      write(stdout, " calls omitted) ...")
+    else:
+      write(stdout, tempFrames[j].filename)
+      if tempFrames[j].line > 0:
+        write(stdout, '(')
+        write(stdout, tempFrames[j].line)
+        write(stdout, ')')
+      write(stdout, ' ')
+      write(stdout, tempFrames[j].procname)
+    write(stdout, "\n")
+
+proc checkForBreakpoint =
+  let b = checkBreakpoints(framePtr.filename, framePtr.line)
+  if b != nil:
+    write(stdout, "*** endb| reached ")
+    write(stdout, framePtr.filename)
+    write(stdout, "(")
+    write(stdout, framePtr.line)
+    write(stdout, ") ")
+    write(stdout, framePtr.procname)
+    write(stdout, " ***\n")
+    CommandPrompt()
+
+proc lineHookImpl() {.nimcall.} =
+  case dbgState
+  of dbStepInto:
+    # we really want the command prompt here:
+    dbgShowExecutionPoint()
+    CommandPrompt()
+  of dbSkipCurrent, dbStepOver: # skip current routine
+    if framePtr == dbgSkipToFrame:
+      dbgShowExecutionPoint()
+      CommandPrompt()
+    else:
+      # breakpoints are wanted though (I guess)
+      checkForBreakpoint()
+  of dbBreakpoints:
+    # debugger is only interested in breakpoints
+    checkForBreakpoint()
+  else: nil
+
+proc watchpointHookImpl(name: cstring) {.nimcall.} =
+  dbgWriteStackTrace(framePtr)
+  debugOut(name)
+
+proc initDebugger {.inline.} =
+  dbgState = dbStepInto
+  dbgUser.len = 1
+  dbgUser.data[0] = 's'
+  dbgWatchpointHook = watchpointHookImpl
+  dbgLineHook = lineHookImpl
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 789e39d6d..1c43bfdc7 100755
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -491,6 +491,7 @@ proc toU32(a: int): int32 {.noStackFrame, compilerproc.} =
 proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
 proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
 
+type NimString = string # hack for hti.nim
 include "system/hti"
 
 proc isFatPointer(ti: PNimType): bool =
diff --git a/tests/run/tfieldindex.nim b/tests/run/tfieldindex.nim
new file mode 100644
index 000000000..3ffa65e58
--- /dev/null
+++ b/tests/run/tfieldindex.nim
@@ -0,0 +1,21 @@
+discard """
+  output: "1"
+"""
+
+type
+  TMyTuple = tuple[a, b: int]
+
+proc indexOf*(t: typedesc, name: string): int {.compiletime.} =
+  ## takes a tuple and looks for the field by name.
+  ## returs index of that field.
+  var
+    d: t
+    i = 0
+  for n, x in fieldPairs(d):
+    if n == name: return i
+    i.inc
+  raise newException(EInvalidValue, "No field " & name & " in type " & 
+    astToStr(t))
+
+echo TMyTuple.indexOf("b")
+
diff --git a/tests/run/tfinally.nim b/tests/run/tfinally.nim
index 29313c3fd..273aac72b 100755
--- a/tests/run/tfinally.nim
+++ b/tests/run/tfinally.nim
@@ -1,6 +1,8 @@
 discard """
   file: "tfinally.nim"
-  output: "came here 3"
+  output: '''came
+here
+3'''
 """
 # Test return in try statement:
 
@@ -9,10 +11,10 @@ proc main: int =
     try:
       return 1
     finally:
-      stdout.write("came ")
+      echo("came")
       return 2
   finally: 
-    stdout.write("here ")
+    echo("here ")
     return 3
     
 echo main() #OUT came here 3
diff --git a/tests/run/tfinally2.nim b/tests/run/tfinally2.nim
index 3ed212a7c..e1e8d4c7e 100755
--- a/tests/run/tfinally2.nim
+++ b/tests/run/tfinally2.nim
@@ -1,6 +1,9 @@
 discard """
   file: "tfinally2.nim"
-  output: "ABCD"
+  output: '''A
+B
+C
+D'''
 """
 # Test break in try statement:
 
@@ -11,15 +14,15 @@ proc main: int =
         try:
           break AB
         finally:
-          stdout.write("A")
-        stdout.write("skipped")
+          echo("A")
+        echo("skipped")
       finally: 
         block B:
-          stdout.write("B")
-      stdout.write("skipped")
-    stdout.write("C")
+          echo("B")
+      echo("skipped")
+    echo("C")
   finally:
-    stdout.writeln("D")
+    echo("D")
     
 discard main() #OUT ABCD
 
diff --git a/tests/specials.nim b/tests/specials.nim
index 1818497a4..b5c49ec3c 100644
--- a/tests/specials.nim
+++ b/tests/specials.nim
@@ -176,14 +176,10 @@ proc runJsTests(r: var TResults, options: string) =
     runSingleTest(r, filename, options & " -d:nodejs", targetJS)
     runSingleTest(r, filename, options & " -d:nodejs -d:release", targetJS)
     
-  # texceptions, texcpt1, texcsub, tfinally, tfinally2,
-  # tfinally3
   for t in os.walkFiles("tests/js/t*.nim"):
     test(t)
-  test "tests/run/tactiontable"
-  test "tests/run/tmultim1"
-  test "tests/run/tmultim3"
-  test "tests/run/tmultim4"
+  for testfile in ["texceptions", "texcpt1", "texcsub", "tfinally", "tfinally2", "tfinally3", "tactiontable", "tmultim1", "tmultim3", "tmultim4"]:
+    test "tests/run/" & testfile & ".nim"
 
 # ------------------------- register special tests here -----------------------
 proc runSpecialTests(r: var TResults, options: string) =