added devel/logging; weakrefs test; next steps for proper unsigned support

This commit is contained in:
Araq 2012-07-05 00:03:33 +02:00
commit 36247e0947
14 changed files with 243 additions and 25 deletions

View file

@ -829,7 +829,7 @@ proc isEmpty(n: PNode): bool =
# Maybe the lexer should mark both the beginning and the end of expressions, # Maybe the lexer should mark both the beginning and the end of expressions,
# then this function could be removed. # then this function could be removed.
proc stringStartingLine(s: PNode): int = proc stringStartingLine(s: PNode): int =
result = s.info.line - countLines(s.strVal) result = s.info.line.int - countLines(s.strVal)
proc evalParseExpr(c: PEvalContext, n: PNode): PNode = proc evalParseExpr(c: PEvalContext, n: PNode): PNode =
var code = evalAux(c, n.sons[1], {}) var code = evalAux(c, n.sons[1], {})

View file

@ -345,7 +345,7 @@ const
warnAnalysisLoophole: "thread analysis incomplete due to unkown call '$1' [AnalysisLoophole]", warnAnalysisLoophole: "thread analysis incomplete due to unkown call '$1' [AnalysisLoophole]",
warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]", warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]", warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
warnImplicitNarrowing: "implicit narrowing conversion: '$1'", warnImplicitNarrowing: "implicit narrowing conversion: '$1' [ImplicitNarrowing]",
warnUser: "$1 [User]", warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]", hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]",

View file

@ -1209,8 +1209,8 @@ proc initTokRender(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) = proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) =
if r.idx < len(r.tokens): if r.idx < len(r.tokens):
kind = r.tokens[r.idx].kind kind = r.tokens[r.idx].kind
var length = r.tokens[r.idx].length var length = r.tokens[r.idx].length.int
literal = substr(r.buf, r.pos + 0, r.pos + 0 + length - 1) literal = substr(r.buf, r.pos, r.pos + length - 1)
inc(r.pos, length) inc(r.pos, length)
inc(r.idx) inc(r.idx)
else: else:

View file

@ -1302,6 +1302,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkNilLit: of nkNilLit:
result.typ = getSysType(tyNil) result.typ = getSysType(tyNil)
of nkIntLit: of nkIntLit:
# XXX this is stupid:
if result.typ == nil: if result.typ == nil:
let i = result.intVal let i = result.intVal
if i >= low(int32) and i <= high(int32): if i >= low(int32) and i <= high(int32):

View file

@ -191,7 +191,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mCopyStrLast: of mCopyStrLast:
result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)), result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
int(getOrdValue(c))), n) int(getOrdValue(c))), n)
of mFloatToStr: result = newStrNodeT($(getFloat(a)), n) of mFloatToStr: result = newStrNodeT($getFloat(a), n)
of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n) of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n)
of mStrToStr: result = a of mStrToStr: result = a
of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n) of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n)
@ -298,6 +298,7 @@ proc getAppType(n: PNode): PNode =
result = newStrNodeT("console", n) result = newStrNodeT("console", n)
proc foldConv*(n, a: PNode): PNode = proc foldConv*(n, a: PNode): PNode =
# XXX range checks?
case skipTypes(n.typ, abstractRange).kind case skipTypes(n.typ, abstractRange).kind
of tyInt..tyInt64: of tyInt..tyInt64:
case skipTypes(a.typ, abstractRange).kind case skipTypes(a.typ, abstractRange).kind

View file

@ -162,7 +162,7 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
else: else:
var k = skipTypes(a, {tyRange}).kind var k = skipTypes(a, {tyRange}).kind
if k == f.kind: result = isSubtype if k == f.kind: result = isSubtype
elif k == tyInt: elif k == tyInt and f.kind in {tyRange, tyInt8..tyUInt64}:
# and a.n != nil and a.n.intVal >= firstOrd(f) and # and a.n != nil and a.n.intVal >= firstOrd(f) and
# a.n.intVal <= lastOrd(f): # a.n.intVal <= lastOrd(f):
# integer literal in the proper range; we want ``i16 + 4`` to stay an # integer literal in the proper range; we want ``i16 + 4`` to stay an
@ -188,7 +188,9 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
else: else:
var k = skipTypes(a, {tyRange}).kind var k = skipTypes(a, {tyRange}).kind
if k == f.kind: result = isSubtype if k == f.kind: result = isSubtype
elif (k >= tyFloat) and (k <= tyFloat128): result = isConvertible elif k == tyInt and f.kind >= tyFloat and f.kind <= tyFloat128:
result = isIntConv
elif k >= tyFloat and k <= tyFloat128: result = isConvertible
else: result = isNone else: result = isNone
proc isObjectSubtype(a, f: PType): bool = proc isObjectSubtype(a, f: PType): bool =

View file

@ -640,6 +640,14 @@ proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and
cnst.len != 0 cnst.len != 0
proc warnNarrowingConversion(n: PNode) =
if n.kind == nkHiddenStdConv:
var dest = skipTypes(n.typ, abstractVarRange)
var source = skipTypes(n.sons[1].typ, abstractVarRange)
if source.kind == tyInt and
source.size > dest.size and n.sons[1].kind != nkIntLit:
Message(n.info, warnImplicitNarrowing, renderTree(n.sons[1]))
proc transform(c: PTransf, n: PNode): PTransNode = proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind case n.kind
of nkSym: of nkSym:
@ -726,6 +734,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
# we inline constants if they are not complex constants: # we inline constants if they are not complex constants:
if cnst != nil and not dontInlineConstant(n, cnst): if cnst != nil and not dontInlineConstant(n, cnst):
result = PTransNode(cnst) # do not miss an optimization result = PTransNode(cnst) # do not miss an optimization
warnNarrowingConversion(result.pnode)
proc processTransf(context: PPassContext, n: PNode): PNode = proc processTransf(context: PPassContext, n: PNode): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip

145
devel/logging.nim Normal file
View file

@ -0,0 +1,145 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a simple logger. It is based on the following design:
## * Runtime log formating is a bug: Sooner or later every log file is parsed.
## * Keep it simple: If this library does not fullfill your needs, write your
## own. Trying to support every logging feature just leads to bloat.
##
## Format is::
##
## DEBUG|INFO|... (2009-11-02 00:00:00)? (Component: )? Message
##
##
type
TLevel* = enum ## logging level
lvlAll, ## all levels active
lvlDebug, ## debug level (and any above) active
lvlInfo, ## info level (and any above) active
lvlWarn, ## warn level (and any above) active
lvlError, ## error level (and any above) active
lvlFatal ## fatal level (and any above) active
const
LevelNames*: array [TLevel, string] = [
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
]
type
TLogger* = object of TObject ## abstract logger; the base type of all loggers
levelThreshold*: TLevel ## only messages of level >= levelThreshold
## should be processed
TConsoleLogger* = object of TLogger ## logger that writes the messages to the
## console
TFileLogger* = object of TLogger ## logger that writes the messages to a file
f: TFile
TRollingFileLogger* = object of TFileLogger ## logger that writes the
## message to a file
maxlines: int # maximum number of lines
lines: seq[string]
method log*(L: ref TLogger, level: TLevel,
frmt: string, args: openArray[string]) =
## override this method in custom loggers. Default implementation does
## nothing.
nil
method log*(L: ref TConsoleLogger, level: TLevel,
frmt: string, args: openArray[string]) =
Writeln(stdout, LevelNames[level], " ", frmt % args)
method log*(L: ref TFileLogger, level: TLevel,
frmt: string, args: openArray[string]) =
Writeln(L.f, LevelNames[level], " ", frmt % args)
proc defaultFilename*(): string =
## returns the default filename for a logger
var (path, name, ext) = splitFile(getAppFilename())
result = changeFileExt(path / name & "_" & getDateStr(), "log")
proc substituteLog*(frmt: string): string =
## converts $date to the current date
## converts $time to the current time
## converts $app to getAppFilename()
## converts
result = newStringOfCap(frmt.len + 20)
var i = 0
while i < frmt.len:
if frmt[i] != '$':
result.add(frmt[i])
inc(i)
else:
inc(i)
var v = ""
var app = getAppFilename()
while frmt[i] in IdentChars:
v.add(toLower(frmt[i]))
inc(i)
case v
of "date": result.add(getDateStr())
of "time": result.add(getClockStr())
of "app": result.add(app)
of "appdir": result.add(app.splitFile.dir)
of "appname": result.add(app.splitFile.name)
proc newFileLogger*(filename = defaultFilename(),
mode: TFileMode = fmAppend,
levelThreshold = lvlNone): ref TFileLogger =
new(result)
result.levelThreshold = levelThreshold
result.f = open(filename, mode)
proc newRollingFileLogger*(filename = defaultFilename(),
mode: TFileMode = fmAppend,
levelThreshold = lvlNone,
maxLines = 1000): ref TFileLogger =
new(result)
result.levelThreshold = levelThreshold
result.maxLines = maxLines
result.f = open(filename, mode)
var
level* = lvlNone
handlers*: seq[ref TLogger] = @[]
proc logLoop(level: TLevel, msg: string) =
for logger in items(handlers):
if level >= logger.levelThreshold:
log(logger, level, msg)
template log*(level: TLevel, msg: string) =
## logs a message of the given level
bind logLoop
if level >= logging.Level:
logLoop(level, frmt, args)
template debug*(msg: string) =
## logs a debug message
log(lvlDebug, msg)
template info*(msg: string) =
## logs an info message
log(lvlInfo, msg)
template warn*(msg: string) =
## logs a warning message
log(lvlWarn, msg)
template error*(msg: string) =
## logs an error message
log(lvlError, msg)
template fatal*(msg: string) =
## logs a fatal error message
log(lvlFatal, msg)

View file

@ -794,7 +794,7 @@ variadic proc, it is implicitely converted to ``cstring`` too:
printf("This works %s", "as expected") printf("This works %s", "as expected")
Even though the conversion is implict, it is not *safe*: The garbage collector Even though the conversion is implicit, it is not *safe*: The garbage collector
does not consider a ``cstring`` to be a root and may collect the underlying does not consider a ``cstring`` to be a root and may collect the underlying
memory. However in practice this almost never happens as the GC considers memory. However in practice this almost never happens as the GC considers
stack roots conservatively. One can use the builtin procs ``GC_ref`` and stack roots conservatively. One can use the builtin procs ``GC_ref`` and
@ -1461,22 +1461,28 @@ algorithm returns true:
# XXX range types? # XXX range types?
proc isImplicitlyConvertible(a, b: PType): bool = proc isImplicitlyConvertible(a, b: PType): bool =
case a.kind case a.kind
of int8: result = b.kind in {int16, int32, int64, int} of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16,
of int16: result = b.kind in {int32, int64, int} uint32, uint64, float, float32, float64}
of int32: result = b.kind in {int64, int} of int8: result = b in {int16, int32, int64, int}
of float: result = b.kind in {float32, float64} of int16: result = b in {int32, int64, int}
of float32: result = b.kind in {float64, float} of int32: result = b in {int64, int}
of float64: result = b.kind in {float32, float} of uint: result = b in {uint32, uint64}
of uint8: result = b in {uint16, uint32, uint64}
of uint16: result = b in {uint32, uint64}
of uint32: result = b in {uint64}
of float: result = b in {float32, float64}
of float32: result = b in {float64, float}
of float64: result = b in {float32, float}
of seq: of seq:
result = b.kind == openArray and typeEquals(a.baseType, b.baseType) result = b == openArray and typeEquals(a.baseType, b.baseType)
of array: of array:
result = b.kind == openArray and typeEquals(a.baseType, b.baseType) result = b == openArray and typeEquals(a.baseType, b.baseType)
if a.baseType == char and a.indexType.rangeA == 0: if a.baseType == char and a.indexType.rangeA == 0:
result = b.kind = cstring result = b = cstring
of cstring, ptr: of cstring, ptr:
result = b.kind == pointer result = b == pointer
of string: of string:
result = b.kind == cstring result = b == cstring
A type ``a`` is **explicitly** convertible to type ``b`` iff the following A type ``a`` is **explicitly** convertible to type ``b`` iff the following
algorithm returns true: algorithm returns true:

View file

@ -75,7 +75,7 @@ proc addSetElem(result: var string, elem: int, typ: PNimType) =
of tyBool: add result, reprBool(bool(elem)) of tyBool: add result, reprBool(bool(elem))
of tyChar: add result, reprChar(chr(elem)) of tyChar: add result, reprChar(chr(elem))
of tyRange: addSetElem(result, elem, typ.base) of tyRange: addSetElem(result, elem, typ.base)
of tyInt..tyInt64: add result, reprInt(elem) of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem)
else: # data corrupt --> inform the user else: # data corrupt --> inform the user
add result, " (invalid data!)" add result, " (invalid data!)"
@ -221,6 +221,9 @@ when not defined(useNimRtl):
of tyInt16: add result, $int(cast[ptr Int16](p)[]) of tyInt16: add result, $int(cast[ptr Int16](p)[])
of tyInt32: add result, $int(cast[ptr Int32](p)[]) of tyInt32: add result, $int(cast[ptr Int32](p)[])
of tyInt64: add result, $(cast[ptr Int64](p)[]) of tyInt64: add result, $(cast[ptr Int64](p)[])
of tyUInt8: add result, $ze(cast[ptr Int8](p)[])
of tyUInt16: add result, $ze(cast[ptr Int16](p)[])
of tyFloat: add result, $(cast[ptr float](p)[]) of tyFloat: add result, $(cast[ptr float](p)[])
of tyFloat32: add result, $(cast[ptr float32](p)[]) of tyFloat32: add result, $(cast[ptr float32](p)[])
of tyFloat64: add result, $(cast[ptr float64](p)[]) of tyFloat64: add result, $(cast[ptr float64](p)[])

View file

@ -199,7 +199,7 @@ proc Open(f: var TFile, filename: string,
var p: pointer = fopen(filename, FormatOpen[mode]) var p: pointer = fopen(filename, FormatOpen[mode])
result = (p != nil) result = (p != nil)
f = cast[TFile](p) f = cast[TFile](p)
if bufSize > 0 and bufSize <= high(cint): if bufSize > 0 and bufSize <= high(cint).int:
if setvbuf(f, nil, IOFBF, bufSize.cint) != 0'i32: if setvbuf(f, nil, IOFBF, bufSize.cint) != 0'i32:
raise newException(EOutOfMemory, "out of memory") raise newException(EOutOfMemory, "out of memory")
elif bufSize == 0: elif bufSize == 0:

48
tests/gc/weakrefs.nim Normal file
View file

@ -0,0 +1,48 @@
discard """
output: "true"
"""
import intsets
type
TMyObject = object
id: int
StrongObject = ref TMyObject
WeakObject = object
id: int
data: ptr TMyObject
var
gid: int # for id generation
valid = initIntSet()
proc finalizer(x: StrongObject) =
valid.excl(x.id)
proc create: StrongObject =
new(result, finalizer)
result.id = gid
valid.incl(gid)
inc gid
proc register(s: StrongObject): WeakObject =
result.data = cast[ptr TMyObject](s)
result.id = s.id
proc access(w: WeakObject): StrongObject =
## returns nil if the object doesn't exist anymore
if valid.contains(w.id):
result = cast[StrongObject](w.data)
proc main =
var s: seq[WeakObject]
newSeq(s, 10_000)
for i in 0 .. s.high:
s[i] = register(create())
# test that we have at least 80% unreachable weak objects by now:
var unreachable = 0
for i in 0 .. s.high:
if access(s[i]) == nil: inc unreachable
echo unreachable > 8_000
main()

View file

@ -131,7 +131,7 @@ proc runGcTests(r: var TResults, options: string) =
test "gctest" test "gctest"
# disabled for now as it somehow runs very slowly ('delete' bug?) but works: # disabled for now as it somehow runs very slowly ('delete' bug?) but works:
test "gcleak3" test "gcleak3"
test "weakrefs"
# ------------------------- threading tests ----------------------------------- # ------------------------- threading tests -----------------------------------

View file

@ -87,6 +87,7 @@ Changes affecting backwards compatibility
- Deprecated the ``ssl`` module. - Deprecated the ``ssl`` module.
- Deprecated ``nimrod pretty`` as it never worked good enough and has some - Deprecated ``nimrod pretty`` as it never worked good enough and has some
inherent problems. inherent problems.
- The integer promotion rules changed.
Compiler Additions Compiler Additions
@ -120,6 +121,8 @@ Language Additions
- Stand-alone ``finally`` and ``except`` blocks are now supported. - Stand-alone ``finally`` and ``except`` blocks are now supported.
- Macros and templates can now be invoked as pragmas. - Macros and templates can now be invoked as pragmas.
- The apostrophe in type suffixes for numerical literal is now optional. - The apostrophe in type suffixes for numerical literal is now optional.
- Unsigned integer types have been added.
- The integer promotion rules changed.
2012-02-09 Version 0.8.14 released 2012-02-09 Version 0.8.14 released