Merge branch 'master' of github.com:Araq/Nimrod
This commit is contained in:
commit
d914963539
21 changed files with 838 additions and 635 deletions
|
|
@ -955,15 +955,22 @@ proc enumSpecifier(p: var TParser): PNode =
|
||||||
result = newNodeP(nkConstSection, p)
|
result = newNodeP(nkConstSection, p)
|
||||||
getTok(p, result)
|
getTok(p, result)
|
||||||
var i = 0
|
var i = 0
|
||||||
|
var hasUnknown = false
|
||||||
while true:
|
while true:
|
||||||
var name = skipIdentExport(p)
|
var name = skipIdentExport(p)
|
||||||
var val: PNode
|
var val: PNode
|
||||||
if p.tok.xkind == pxAsgn:
|
if p.tok.xkind == pxAsgn:
|
||||||
getTok(p, name)
|
getTok(p, name)
|
||||||
val = constantExpression(p)
|
val = constantExpression(p)
|
||||||
if val.kind == nkIntLit: i = int(val.intVal)+1
|
if val.kind == nkIntLit:
|
||||||
else: parMessage(p, errXExpected, "int literal")
|
i = int(val.intVal)+1
|
||||||
|
hasUnknown = false
|
||||||
|
else:
|
||||||
|
hasUnknown = true
|
||||||
else:
|
else:
|
||||||
|
if hasUnknown:
|
||||||
|
parMessage(p, warnUser, "computed const value may be wrong: " &
|
||||||
|
name.renderTree)
|
||||||
val = newIntNodeP(nkIntLit, i, p)
|
val = newIntNodeP(nkIntLit, i, p)
|
||||||
inc(i)
|
inc(i)
|
||||||
var c = createConst(name, ast.emptyNode, val, p)
|
var c = createConst(name, ast.emptyNode, val, p)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,20 @@ extern "C" {
|
||||||
# endif
|
# endif
|
||||||
#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 void (*callback_t) (int rc);
|
||||||
typedef const char* (*callback2)(int rc, long L, const char* buffer);
|
typedef const char* (*callback2)(int rc, long L, const char* buffer);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -892,6 +892,12 @@ proc getFileHeader(cfilenoext: string): PRope =
|
||||||
result = getCopyright(cfilenoext)
|
result = getCopyright(cfilenoext)
|
||||||
addIntTypes(result)
|
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) =
|
proc genMainProc(m: BModule) =
|
||||||
const
|
const
|
||||||
CommonMainBody =
|
CommonMainBody =
|
||||||
|
|
@ -950,6 +956,8 @@ proc genMainProc(m: BModule) =
|
||||||
nimMain = PosixNimMain
|
nimMain = PosixNimMain
|
||||||
otherMain = PosixCMain
|
otherMain = PosixCMain
|
||||||
if gBreakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
|
if gBreakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
|
||||||
|
if optEndb in gOptions:
|
||||||
|
gBreakpoints.app(m.genFilenames)
|
||||||
|
|
||||||
let initStackBottomCall = if emulatedThreadVars() or
|
let initStackBottomCall = if emulatedThreadVars() or
|
||||||
platform.targetOS == osStandalone: "".toRope
|
platform.targetOS == osStandalone: "".toRope
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ type
|
||||||
TBlock{.final.} = object
|
TBlock{.final.} = object
|
||||||
id: int # the ID of the label; positive means that it
|
id: int # the ID of the label; positive means that it
|
||||||
# has been used (i.e. the label should be emitted)
|
# 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'
|
isLoop: bool # whether it's a 'block' or 'while'
|
||||||
|
|
||||||
TGlobals{.final.} = object
|
TGlobals{.final.} = object
|
||||||
|
|
@ -62,7 +61,6 @@ type
|
||||||
module: BModule
|
module: BModule
|
||||||
g: PGlobals
|
g: PGlobals
|
||||||
BeforeRetNeeded: bool
|
BeforeRetNeeded: bool
|
||||||
nestedTryStmts: int
|
|
||||||
unique: int
|
unique: int
|
||||||
blocks: seq[TBlock]
|
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)):
|
((p.prc == nil) or not (sfPure in p.prc.flags)):
|
||||||
appf(r.com, "F.line = $1;$n", [toRope(line)])
|
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) =
|
proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
var
|
var
|
||||||
cond, stmt: TCompRes
|
cond, stmt: TCompRes
|
||||||
|
|
@ -498,7 +492,6 @@ proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
length = len(p.blocks)
|
length = len(p.blocks)
|
||||||
setlen(p.blocks, length + 1)
|
setlen(p.blocks, length + 1)
|
||||||
p.blocks[length].id = - p.unique
|
p.blocks[length].id = - p.unique
|
||||||
p.blocks[length].nestedTryStmts = p.nestedTryStmts
|
|
||||||
p.blocks[length].isLoop = true
|
p.blocks[length].isLoop = true
|
||||||
labl = p.unique
|
labl = p.unique
|
||||||
gen(p, n.sons[0], cond)
|
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)
|
if optStackTrace in p.Options: app(r.com, "framePtr = F;" & tnl)
|
||||||
app(r.com, "try {" & tnl)
|
app(r.com, "try {" & tnl)
|
||||||
length = sonsLen(n)
|
length = sonsLen(n)
|
||||||
inc(p.nestedTryStmts)
|
|
||||||
genStmt(p, n.sons[0], a)
|
genStmt(p, n.sons[0], a)
|
||||||
app(r.com, mergeStmt(a))
|
app(r.com, mergeStmt(a))
|
||||||
i = 1
|
i = 1
|
||||||
|
|
@ -571,8 +563,6 @@ proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
appf(epart, "$1}$n", [mergeStmt(a)])
|
appf(epart, "$1}$n", [mergeStmt(a)])
|
||||||
inc(i)
|
inc(i)
|
||||||
if epart != nil: appf(r.com, "} catch (EXC) {$n$1", [epart])
|
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)
|
app(r.com, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
|
||||||
if (i < length) and (n.sons[i].kind == nkFinally):
|
if (i < length) and (n.sons[i].kind == nkFinally):
|
||||||
genStmt(p, n.sons[i].sons[0], a)
|
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
|
sym.loc.a = idx
|
||||||
setlen(p.blocks, idx + 1)
|
setlen(p.blocks, idx + 1)
|
||||||
p.blocks[idx].id = - p.unique # negative because it isn't used yet
|
p.blocks[idx].id = - p.unique # negative because it isn't used yet
|
||||||
p.blocks[idx].nestedTryStmts = p.nestedTryStmts
|
|
||||||
labl = p.unique
|
labl = p.unique
|
||||||
if n.kind == nkBlockExpr: genStmtListExpr(p, n.sons[1], r)
|
if n.kind == nkBlockExpr: genStmtListExpr(p, n.sons[1], r)
|
||||||
else: genStmt(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:
|
if idx < 0 or not p.blocks[idx].isLoop:
|
||||||
InternalError(n.info, "no loop to break")
|
InternalError(n.info, "no loop to break")
|
||||||
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
|
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)])
|
appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)])
|
||||||
|
|
||||||
proc genAsmStmt(p: var TProc, n: PNode, r: var TCompRes) =
|
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))
|
if a.com != nil: appf(r.com, "$1;$n", mergeStmt(a))
|
||||||
else:
|
else:
|
||||||
genLineDir(p, n, r)
|
genLineDir(p, n, r)
|
||||||
finishTryStmt(p, r, p.nestedTryStmts)
|
|
||||||
app(r.com, "break BeforeRet;" & tnl)
|
app(r.com, "break BeforeRet;" & tnl)
|
||||||
|
|
||||||
proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope =
|
proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope =
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,6 @@ Files: "compiler/*.nim"
|
||||||
Files: "build/empty.txt"
|
Files: "build/empty.txt"
|
||||||
Files: "bin/empty.txt"
|
Files: "bin/empty.txt"
|
||||||
|
|
||||||
Files: "packages/docutils/*.nim"
|
|
||||||
|
|
||||||
|
|
||||||
[Lib]
|
[Lib]
|
||||||
Files: "lib/nimbase.h"
|
Files: "lib/nimbase.h"
|
||||||
|
|
@ -85,6 +83,7 @@ Files: "lib/wrappers/zip/libzip_all.c"
|
||||||
Files: "lib/windows/*.nim"
|
Files: "lib/windows/*.nim"
|
||||||
Files: "lib/posix/*.nim"
|
Files: "lib/posix/*.nim"
|
||||||
Files: "lib/js/*.nim"
|
Files: "lib/js/*.nim"
|
||||||
|
Files: "lib/packages/docutils/*.nim"
|
||||||
|
|
||||||
|
|
||||||
[Other]
|
[Other]
|
||||||
|
|
|
||||||
|
|
@ -490,7 +490,7 @@ proc getArrayConstr(m: PSym, n: PNode): PNode =
|
||||||
|
|
||||||
proc foldArrayAccess(m: PSym, n: PNode): PNode =
|
proc foldArrayAccess(m: PSym, n: PNode): PNode =
|
||||||
var x = getConstExpr(m, n.sons[0])
|
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])
|
var y = getConstExpr(m, n.sons[1])
|
||||||
if y == nil: return
|
if y == nil: return
|
||||||
|
|
@ -542,6 +542,11 @@ proc foldConStrStr(m: PSym, n: PNode): PNode =
|
||||||
if a == nil: return nil
|
if a == nil: return nil
|
||||||
result.strVal.add(getStrOrChar(a))
|
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 =
|
proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||||
result = nil
|
result = nil
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -569,6 +574,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||||
if sfFakeConst notin s.flags: result = copyTree(s.ast)
|
if sfFakeConst notin s.flags: result = copyTree(s.ast)
|
||||||
elif s.kind in {skProc, skMethod}: # BUGFIX
|
elif s.kind in {skProc, skMethod}: # BUGFIX
|
||||||
result = n
|
result = n
|
||||||
|
elif s.kind in {skType, skGenericParam}:
|
||||||
|
result = newSymNodeTypeDesc(s, n.info)
|
||||||
of nkCharLit..nkNilLit:
|
of nkCharLit..nkNilLit:
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
of nkIfExpr:
|
of nkIfExpr:
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,6 @@ proc getIdentNode(n: PNode): PNode =
|
||||||
illFormedAst(n)
|
illFormedAst(n)
|
||||||
result = 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,
|
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||||
ctx: var TIntSet): PNode
|
ctx: var TIntSet): PNode
|
||||||
proc semGenericStmtScope(c: PContext, n: PNode,
|
proc semGenericStmtScope(c: PContext, n: PNode,
|
||||||
|
|
|
||||||
12
doc/endb.txt
12
doc/endb.txt
|
|
@ -61,8 +61,8 @@ Executing Commands
|
||||||
Breakpoint Commands
|
Breakpoint Commands
|
||||||
===================
|
===================
|
||||||
|
|
||||||
``b``, ``setbreak`` <identifier> [fromline [toline]] [file]
|
``b``, ``setbreak`` [fromline [toline]] [file]
|
||||||
Set a new breakpoint named 'identifier' for the given file
|
Set a new breakpoint for the given file
|
||||||
and line numbers. If no file is given, the current execution point's
|
and line numbers. If no file is given, the current execution point's
|
||||||
filename is used. If the filename has no extension, ``.nim`` is
|
filename is used. If the filename has no extension, ``.nim`` is
|
||||||
appended for your convenience.
|
appended for your convenience.
|
||||||
|
|
@ -71,16 +71,16 @@ Breakpoint Commands
|
||||||
breakpoint contains a line number range. Some examples if it is still
|
breakpoint contains a line number range. Some examples if it is still
|
||||||
unclear:
|
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
|
will be triggered if the instruction pointer reaches one of the
|
||||||
lines 12-15 in the file ``thallo.nim``.
|
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
|
will be triggered if the instruction pointer reaches the
|
||||||
line 12 in the file ``thallo.nim``.
|
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
|
will be triggered if the instruction pointer reaches the
|
||||||
line 12 in the current file.
|
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
|
will be triggered if the instruction pointer reaches the
|
||||||
current line in the current file again.
|
current line in the current file again.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
while impure libraries do. A wrapper is an impure library that is a very
|
||||||
low-level interface to a C library.
|
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
|
Pure libraries
|
||||||
|
|
|
||||||
19
install.sh
19
install.sh
|
|
@ -70,6 +70,7 @@ if [ $# -eq 1 ] ; then
|
||||||
mkdir -p $libdir/windows
|
mkdir -p $libdir/windows
|
||||||
mkdir -p $libdir/posix
|
mkdir -p $libdir/posix
|
||||||
mkdir -p $libdir/js
|
mkdir -p $libdir/js
|
||||||
|
mkdir -p $libdir/packages/docutils
|
||||||
|
|
||||||
cp bin/nimrod $bindir/nimrod
|
cp bin/nimrod $bindir/nimrod
|
||||||
chmod 755 $bindir/nimrod
|
chmod 755 $bindir/nimrod
|
||||||
|
|
@ -711,20 +712,26 @@ if [ $# -eq 1 ] ; then
|
||||||
chmod 644 $libdir/system/debugger.nim
|
chmod 644 $libdir/system/debugger.nim
|
||||||
cp lib/system/dyncalls.nim $libdir/system/dyncalls.nim
|
cp lib/system/dyncalls.nim $libdir/system/dyncalls.nim
|
||||||
chmod 644 $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
|
cp lib/system/embedded.nim $libdir/system/embedded.nim
|
||||||
chmod 644 $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
|
cp lib/system/excpt.nim $libdir/system/excpt.nim
|
||||||
chmod 644 $libdir/system/excpt.nim
|
chmod 644 $libdir/system/excpt.nim
|
||||||
cp lib/system/gc.nim $libdir/system/gc.nim
|
cp lib/system/gc.nim $libdir/system/gc.nim
|
||||||
chmod 644 $libdir/system/gc.nim
|
chmod 644 $libdir/system/gc.nim
|
||||||
cp lib/system/gc2.nim $libdir/system/gc2.nim
|
cp lib/system/gc2.nim $libdir/system/gc2.nim
|
||||||
chmod 644 $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
|
cp lib/system/hti.nim $libdir/system/hti.nim
|
||||||
chmod 644 $libdir/system/hti.nim
|
chmod 644 $libdir/system/hti.nim
|
||||||
cp lib/system/inclrtl.nim $libdir/system/inclrtl.nim
|
cp lib/system/inclrtl.nim $libdir/system/inclrtl.nim
|
||||||
chmod 644 $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
|
cp lib/system/mmdisp.nim $libdir/system/mmdisp.nim
|
||||||
chmod 644 $libdir/system/mmdisp.nim
|
chmod 644 $libdir/system/mmdisp.nim
|
||||||
cp lib/system/profiler.nim $libdir/system/profiler.nim
|
cp lib/system/profiler.nim $libdir/system/profiler.nim
|
||||||
|
|
@ -1103,6 +1110,14 @@ if [ $# -eq 1 ] ; then
|
||||||
chmod 644 $libdir/posix/posix.nim
|
chmod 644 $libdir/posix/posix.nim
|
||||||
cp lib/js/dom.nim $libdir/js/dom.nim
|
cp lib/js/dom.nim $libdir/js/dom.nim
|
||||||
chmod 644 $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"
|
echo "installation successful"
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,16 @@ proc newAny(value: pointer, rawType: PNimType): TAny =
|
||||||
result.value = value
|
result.value = value
|
||||||
result.rawType = rawType
|
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.} =
|
proc toAny*[T](x: var T): TAny {.inline.} =
|
||||||
## constructs a ``TAny`` object from `x`. This captures `x`'s address, so
|
## 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
|
## `x` can be modified with its ``TAny`` wrapper! The client needs to ensure
|
||||||
|
|
|
||||||
|
|
@ -415,7 +415,15 @@ proc parseEnum*[T: enum](s: string, default: T): T =
|
||||||
proc repeatChar*(count: int, c: Char = ' '): string {.noSideEffect,
|
proc repeatChar*(count: int, c: Char = ' '): string {.noSideEffect,
|
||||||
rtl, extern: "nsuRepeatChar".} =
|
rtl, extern: "nsuRepeatChar".} =
|
||||||
## Returns a string of length `count` consisting only of
|
## 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)
|
result = newString(count)
|
||||||
for i in 0..count-1: result[i] = c
|
for i in 0..count-1: result[i] = c
|
||||||
|
|
||||||
|
|
@ -429,7 +437,8 @@ proc align*(s: string, count: int): string {.
|
||||||
noSideEffect, rtl, extern: "nsuAlignString".} =
|
noSideEffect, rtl, extern: "nsuAlignString".} =
|
||||||
## Aligns a string `s` with spaces, so that is of length `count`. Spaces are
|
## 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
|
## 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:
|
if s.len < count:
|
||||||
result = newString(count)
|
result = newString(count)
|
||||||
var spaces = count - s.len
|
var spaces = count - s.len
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,11 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
|
||||||
result = newString(1)
|
result = newString(1)
|
||||||
result[0] = chr(i)
|
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
|
const
|
||||||
alphaRanges = [
|
alphaRanges = [
|
||||||
0x00d8, 0x00f6, # -
|
0x00d8, 0x00f6, # -
|
||||||
|
|
@ -1208,3 +1213,10 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
|
||||||
result = irune(toLower(ar)) - irune(toLower(br))
|
result = irune(toLower(ar)) - irune(toLower(br))
|
||||||
if result != 0: return
|
if result != 0: return
|
||||||
result = a.len - b.len
|
result = a.len - b.len
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
let
|
||||||
|
someString = "öÑ"
|
||||||
|
someRunes = @[runeAt(someString, 0), runeAt(someString, 2)]
|
||||||
|
compared = (someString == $someRunes)
|
||||||
|
assert compared == true
|
||||||
|
|
|
||||||
|
|
@ -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``.
|
## creates a new sequence of type ``seq[T]`` with length ``len``.
|
||||||
## This is equivalent to ``s = @[]; setlen(s, len)``, but more
|
## This is equivalent to ``s = @[]; setlen(s, len)``, but more
|
||||||
## efficient since no reallocation is needed.
|
## 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] =
|
proc newSeq*[T](len = 0): seq[T] =
|
||||||
## creates a new sequence of type ``seq[T]`` with length ``len``.
|
## 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)
|
newSeq(result, len)
|
||||||
|
|
||||||
proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
|
proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
|
||||||
|
|
@ -1650,10 +1675,6 @@ const nimrodStackTrace = compileOption("stacktrace")
|
||||||
# of the code
|
# of the code
|
||||||
|
|
||||||
var
|
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.}
|
globalRaiseHook*: proc (e: ref E_Base): bool {.nimcall.}
|
||||||
## with this hook you can influence exception handling on a global level.
|
## with this hook you can influence exception handling on a global level.
|
||||||
## If not nil, every 'raise' statement ends up calling this hook. Ordinary
|
## If not nil, every 'raise' statement ends up calling this hook. Ordinary
|
||||||
|
|
@ -1691,13 +1712,14 @@ var
|
||||||
## continues and the program is terminated.
|
## continues and the program is terminated.
|
||||||
|
|
||||||
type
|
type
|
||||||
PFrame = ptr TFrame
|
PFrame* = ptr TFrame ## represents a runtime frame of the call stack;
|
||||||
TFrame {.importc, nodecl, final.} = object
|
## part of the debugger API.
|
||||||
prev: PFrame
|
TFrame* {.importc, nodecl, final.} = object ## the frame itself
|
||||||
procname: CString
|
prev*: PFrame ## previous frame; used for chaining the call stack
|
||||||
line: int # current line number
|
procname*: cstring ## name of the proc that is currently executing
|
||||||
filename: CString
|
line*: int ## line number of the proc that is currently executing
|
||||||
len: int # length of slots (when not debugging always zero)
|
filename*: cstring ## filename of the proc that is currently executing
|
||||||
|
len*: int ## length of the inspectable slots
|
||||||
|
|
||||||
when not defined(JS):
|
when not defined(JS):
|
||||||
{.push stack_trace:off, profiler:off.}
|
{.push stack_trace:off, profiler:off.}
|
||||||
|
|
@ -2374,8 +2396,33 @@ proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
|
||||||
proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {.
|
proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {.
|
||||||
magic: "InstantiationInfo", noSideEffect.}
|
magic: "InstantiationInfo", noSideEffect.}
|
||||||
## provides access to the compiler's instantiation stack line information.
|
## 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.} =
|
proc raiseAssert*(msg: string) {.noinline.} =
|
||||||
raise newException(EAssertionFailed, msg)
|
raise newException(EAssertionFailed, msg)
|
||||||
|
|
|
||||||
|
|
@ -7,187 +7,95 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
# This file implements the embedded debugger that can be linked
|
## This file implements basic features for any debugger.
|
||||||
# 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.
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TStaticStr {.pure, final.} = object
|
TVarSlot* {.compilerproc, final.} = object ## a slot in a frame
|
||||||
len: int
|
address*: pointer ## the variable's address
|
||||||
data: array[0..100, char]
|
typ*: PNimType ## the variable's type
|
||||||
|
name*: cstring ## the variable's name; for globals this is "module.name"
|
||||||
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"
|
|
||||||
|
|
||||||
PExtendedFrame = ptr TExtendedFrame
|
PExtendedFrame = ptr TExtendedFrame
|
||||||
TExtendedFrame {.final.} = object # If the debugger is enabled the compiler
|
TExtendedFrame = object # If the debugger is enabled the compiler
|
||||||
# provides an extended frame. Of course
|
# provides an extended frame. Of course
|
||||||
# only slots that are
|
# only slots that are
|
||||||
# needed are allocated and not 10_000,
|
# needed are allocated and not 10_000,
|
||||||
# except for the global data description.
|
# except for the global data description.
|
||||||
f: TFrame
|
f: TFrame
|
||||||
slots: array[0..10_000, TVarSlot]
|
slots: array[0..10_000, TVarSlot]
|
||||||
|
|
||||||
var
|
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
|
dbgGlobalData: TExtendedFrame # this reserves much space, but
|
||||||
# for now it is the most practical way
|
# 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) =
|
proc getLocal*(frame: PFrame; slot: int): TVarSlot {.inline.} =
|
||||||
s.len = newLen
|
## retrieves the meta data for the local variable at `slot`. CAUTION: An
|
||||||
s.data[newLen] = '\0'
|
## invalid `slot` value causes a corruption!
|
||||||
|
result = cast[PExtendedFrame](frame).slots[slot]
|
||||||
|
|
||||||
proc add(s: var TStaticStr, c: char) =
|
proc getGlobalLen*(): int {.inline.} =
|
||||||
if s.len < high(s.data)-1:
|
## gets the number of registered globals.
|
||||||
s.data[s.len] = c
|
result = dbgGlobalData.f.len
|
||||||
s.data[s.len+1] = '\0'
|
|
||||||
inc s.len
|
|
||||||
|
|
||||||
proc add(s: var TStaticStr, c: cstring) =
|
proc getGlobal*(slot: int): TVarSlot {.inline.} =
|
||||||
var i = 0
|
## retrieves the meta data for the global variable at `slot`. CAUTION: An
|
||||||
while c[i] != '\0':
|
## invalid `slot` value causes a corruption!
|
||||||
add s, c[i]
|
result = dbgGlobalData.slots[slot]
|
||||||
inc i
|
|
||||||
|
|
||||||
proc assign(s: var TStaticStr, c: cstring) =
|
# ------------------- breakpoint support ------------------------------------
|
||||||
setLen(s)
|
|
||||||
add s, c
|
|
||||||
|
|
||||||
proc `==`(a, b: TStaticStr): bool =
|
type
|
||||||
if a.len == b.len:
|
TBreakpoint* = object ## represents a break point
|
||||||
for i in 0 .. a.len-1:
|
low*, high*: int ## range from low to high; if disabled
|
||||||
if a.data[i] != b.data[i]: return false
|
## both low and high are set to their negative values
|
||||||
return true
|
filename*: cstring ## the filename of the breakpoint
|
||||||
|
|
||||||
proc `==`(a: TStaticStr, b: cstring): bool =
|
var
|
||||||
result = c_strcmp(a.data, b) == 0
|
dbgBP: array[0..127, TBreakpoint] # breakpoints
|
||||||
|
dbgBPlen: int
|
||||||
|
dbgBPbloom: int64 # we use a bloom filter to speed up breakpoint checking
|
||||||
|
|
||||||
proc findBreakpoint(name: TStaticStr): int =
|
dbgFilenames*: array[0..300, cstring] ## registered filenames;
|
||||||
# returns -1 if not found
|
## 'nil' terminated
|
||||||
for i in countdown(dbgBPlen-1, 0):
|
dbgFilenameLen: int
|
||||||
if name == dbgBP[i].name: return i
|
|
||||||
return -1
|
|
||||||
|
|
||||||
proc write(f: TFile, s: TStaticStr) =
|
proc dbgRegisterFilename(filename: cstring) {.compilerproc.} =
|
||||||
write(f, cstring(s.data))
|
# XXX we could check for duplicates here for DLL support
|
||||||
|
dbgFilenames[dbgFilenameLen] = filename
|
||||||
|
inc dbgFilenameLen
|
||||||
|
|
||||||
proc ListBreakPoints() =
|
proc dbgRegisterBreakpoint(line: int,
|
||||||
write(stdout, "*** endb| Breakpoints:\n")
|
filename, name: cstring) {.compilerproc.} =
|
||||||
for i in 0 .. dbgBPlen-1:
|
let x = dbgBPlen
|
||||||
write(stdout, dbgBP[i].name)
|
if x >= high(dbgBP):
|
||||||
write(stdout, ": ")
|
#debugOut("[Warning] cannot register breakpoint")
|
||||||
write(stdout, abs(dbgBP[i].low))
|
return
|
||||||
write(stdout, "..")
|
inc(dbgBPlen)
|
||||||
write(stdout, abs(dbgBP[i].high))
|
dbgBP[x].filename = filename
|
||||||
write(stdout, dbgBP[i].filename)
|
dbgBP[x].low = line
|
||||||
if dbgBP[i].low < 0:
|
dbgBP[x].high = line
|
||||||
write(stdout, " [disabled]\n")
|
dbgBPbloom = dbgBPbloom or line
|
||||||
else:
|
|
||||||
write(stdout, "\n")
|
|
||||||
write(stdout, "***\n")
|
|
||||||
|
|
||||||
proc openAppend(filename: cstring): TFile =
|
proc addBreakpoint*(filename: cstring, lo, hi: int): bool =
|
||||||
var p: pointer = fopen(filename, "ab")
|
let x = dbgBPlen
|
||||||
if p != nil:
|
if x >= high(dbgBP): return false
|
||||||
result = cast[TFile](p)
|
inc(dbgBPlen)
|
||||||
write(result, "----------------------------------------\n")
|
result = true
|
||||||
|
dbgBP[x].filename = filename
|
||||||
proc dbgRepr(p: pointer, typ: PNimType): string =
|
dbgBP[x].low = lo
|
||||||
var cl: TReprClosure
|
dbgBP[x].high = hi
|
||||||
initReprClosure(cl)
|
for line in lo..hi: dbgBPbloom = dbgBPbloom or line
|
||||||
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 dbgShowExecutionPoint() =
|
|
||||||
write(stdout, "*** endb| ")
|
|
||||||
write(stdout, framePtr.filename)
|
|
||||||
write(stdout, "(")
|
|
||||||
write(stdout, framePtr.line)
|
|
||||||
write(stdout, ") ")
|
|
||||||
write(stdout, framePtr.procname)
|
|
||||||
write(stdout, " ***\n")
|
|
||||||
|
|
||||||
const
|
const
|
||||||
FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2)
|
FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2)
|
||||||
|
|
@ -216,337 +124,29 @@ proc fileMatches(c, bp: cstring): bool =
|
||||||
inc(i)
|
inc(i)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc dbgBreakpointReached(line: int): int =
|
proc canonFilename*(filename: cstring): cstring =
|
||||||
for i in 0..dbgBPlen-1:
|
## returns 'nil' if the filename cannot be found.
|
||||||
if line >= dbgBP[i].low and line <= dbgBP[i].high and
|
for i in 0 .. <dbgFilenameLen:
|
||||||
fileMatches(framePtr.filename, dbgBP[i].filename): return i
|
result = dbgFilenames[i]
|
||||||
return -1
|
if fileMatches(result, filename): return result
|
||||||
|
result = nil
|
||||||
|
|
||||||
proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
|
iterator listBreakpoints*(): ptr TBreakpoint =
|
||||||
result = start
|
## lists all breakpoints.
|
||||||
# skip whitespace:
|
for i in 0..dbgBPlen-1: yield addr(dbgBP[i])
|
||||||
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 =
|
proc isActive*(b: ptr TBreakpoint): bool = b.low > 0
|
||||||
setlen(a)
|
proc flip*(b: ptr TBreakpoint) =
|
||||||
result = scanAndAppendWord(src, a, start)
|
## enables or disables 'b' depending on its current state.
|
||||||
|
b.low = -b.low; b.high = -b.high
|
||||||
|
|
||||||
proc scanFilename(src: cstring, a: var TStaticStr, start: int): int =
|
proc checkBreakpoints*(filename: cstring, line: int): ptr TBreakpoint =
|
||||||
result = start
|
## in which breakpoint (if any) we are.
|
||||||
setLen a
|
if (dbgBPbloom and line) != line: return nil
|
||||||
# skip whitespace:
|
for b in listBreakpoints():
|
||||||
while src[result] in {'\t', ' '}: inc(result)
|
if line >= b.low and line <= b.high and filename == b.filename: return b
|
||||||
while src[result] notin {'\t', ' ', '\0'}:
|
|
||||||
add(a, src[result])
|
|
||||||
inc(result)
|
|
||||||
|
|
||||||
proc scanNumber(src: cstring, a: var int, start: int): int =
|
# ------------------- watchpoint support ------------------------------------
|
||||||
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 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
|
|
||||||
|
|
||||||
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 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 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)
|
|
||||||
|
|
||||||
type
|
type
|
||||||
THash = int
|
THash = int
|
||||||
|
|
@ -665,7 +265,7 @@ proc dbgRegisterWatchpoint(address: pointer, name: cstring,
|
||||||
Watchpoints[i].address = address
|
Watchpoints[i].address = address
|
||||||
return
|
return
|
||||||
if L >= watchPoints.high:
|
if L >= watchPoints.high:
|
||||||
debugOut("[Warning] cannot register watchpoint")
|
#debugOut("[Warning] cannot register watchpoint")
|
||||||
return
|
return
|
||||||
Watchpoints[L].name = name
|
Watchpoints[L].name = name
|
||||||
Watchpoints[L].address = address
|
Watchpoints[L].address = address
|
||||||
|
|
@ -676,99 +276,28 @@ proc dbgRegisterWatchpoint(address: pointer, name: cstring,
|
||||||
proc dbgUnregisterWatchpoints*() =
|
proc dbgUnregisterWatchpoints*() =
|
||||||
WatchpointsLen = 0
|
WatchpointsLen = 0
|
||||||
|
|
||||||
proc dbgWriteStackTrace(f: PFrame) =
|
var
|
||||||
const
|
dbgLineHook*: proc () {.nimcall.}
|
||||||
firstCalls = 32
|
## set this variable to provide a procedure that should be called before
|
||||||
var
|
## each executed instruction. This should only be used by debuggers!
|
||||||
it = f
|
## Only code compiled with the ``debugger:on`` switch calls this hook.
|
||||||
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>".}
|
dbgWatchpointHook*: proc (watchpointName: cstring) {.nimcall.}
|
||||||
|
|
||||||
proc interestingFilename(filename: cstring): bool =
|
|
||||||
#result = strstr(filename, "/rst.nim") == nil
|
|
||||||
result = true
|
|
||||||
|
|
||||||
proc checkWatchpoints =
|
proc checkWatchpoints =
|
||||||
let L = WatchpointsLen
|
let L = WatchpointsLen
|
||||||
for i in 0.. <L:
|
for i in 0.. <L:
|
||||||
let newHash = genericHash(Watchpoints[i].address, Watchpoints[i].typ)
|
let newHash = genericHash(Watchpoints[i].address, Watchpoints[i].typ)
|
||||||
if newHash != Watchpoints[i].oldValue:
|
if newHash != Watchpoints[i].oldValue:
|
||||||
if interestingFilename(framePtr.filename):
|
dbgWatchpointHook(Watchpoints[i].name)
|
||||||
dbgWriteStackTrace(framePtr)
|
|
||||||
debugOut(Watchpoints[i].name)
|
|
||||||
Watchpoints[i].oldValue = newHash
|
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!
|
# 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
|
if framePtr == nil: return
|
||||||
let oldState = dbgState
|
if dbgWatchpointHook != nil: checkWatchpoints()
|
||||||
checkWatchpoints()
|
|
||||||
framePtr.line = line # this is done here for smaller code size!
|
framePtr.line = line # this is done here for smaller code size!
|
||||||
framePtr.filename = file
|
framePtr.filename = file
|
||||||
if dbgLineHook != nil: dbgLineHook()
|
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"
|
||||||
|
|
|
||||||
538
lib/system/endb.nim
Normal file
538
lib/system/endb.nim
Normal file
|
|
@ -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
|
||||||
|
|
@ -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 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
|
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
||||||
|
|
||||||
|
type NimString = string # hack for hti.nim
|
||||||
include "system/hti"
|
include "system/hti"
|
||||||
|
|
||||||
proc isFatPointer(ti: PNimType): bool =
|
proc isFatPointer(ti: PNimType): bool =
|
||||||
|
|
|
||||||
21
tests/run/tfieldindex.nim
Normal file
21
tests/run/tfieldindex.nim
Normal file
|
|
@ -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")
|
||||||
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tfinally.nim"
|
file: "tfinally.nim"
|
||||||
output: "came here 3"
|
output: '''came
|
||||||
|
here
|
||||||
|
3'''
|
||||||
"""
|
"""
|
||||||
# Test return in try statement:
|
# Test return in try statement:
|
||||||
|
|
||||||
|
|
@ -9,10 +11,10 @@ proc main: int =
|
||||||
try:
|
try:
|
||||||
return 1
|
return 1
|
||||||
finally:
|
finally:
|
||||||
stdout.write("came ")
|
echo("came")
|
||||||
return 2
|
return 2
|
||||||
finally:
|
finally:
|
||||||
stdout.write("here ")
|
echo("here ")
|
||||||
return 3
|
return 3
|
||||||
|
|
||||||
echo main() #OUT came here 3
|
echo main() #OUT came here 3
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tfinally2.nim"
|
file: "tfinally2.nim"
|
||||||
output: "ABCD"
|
output: '''A
|
||||||
|
B
|
||||||
|
C
|
||||||
|
D'''
|
||||||
"""
|
"""
|
||||||
# Test break in try statement:
|
# Test break in try statement:
|
||||||
|
|
||||||
|
|
@ -11,15 +14,15 @@ proc main: int =
|
||||||
try:
|
try:
|
||||||
break AB
|
break AB
|
||||||
finally:
|
finally:
|
||||||
stdout.write("A")
|
echo("A")
|
||||||
stdout.write("skipped")
|
echo("skipped")
|
||||||
finally:
|
finally:
|
||||||
block B:
|
block B:
|
||||||
stdout.write("B")
|
echo("B")
|
||||||
stdout.write("skipped")
|
echo("skipped")
|
||||||
stdout.write("C")
|
echo("C")
|
||||||
finally:
|
finally:
|
||||||
stdout.writeln("D")
|
echo("D")
|
||||||
|
|
||||||
discard main() #OUT ABCD
|
discard main() #OUT ABCD
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,14 +176,10 @@ proc runJsTests(r: var TResults, options: string) =
|
||||||
runSingleTest(r, filename, options & " -d:nodejs", targetJS)
|
runSingleTest(r, filename, options & " -d:nodejs", targetJS)
|
||||||
runSingleTest(r, filename, options & " -d:nodejs -d:release", 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"):
|
for t in os.walkFiles("tests/js/t*.nim"):
|
||||||
test(t)
|
test(t)
|
||||||
test "tests/run/tactiontable"
|
for testfile in ["texceptions", "texcpt1", "texcsub", "tfinally", "tfinally2", "tfinally3", "tactiontable", "tmultim1", "tmultim3", "tmultim4"]:
|
||||||
test "tests/run/tmultim1"
|
test "tests/run/" & testfile & ".nim"
|
||||||
test "tests/run/tmultim3"
|
|
||||||
test "tests/run/tmultim4"
|
|
||||||
|
|
||||||
# ------------------------- register special tests here -----------------------
|
# ------------------------- register special tests here -----------------------
|
||||||
proc runSpecialTests(r: var TResults, options: string) =
|
proc runSpecialTests(r: var TResults, options: string) =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue