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
parent 8ef48a34e5
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,
# then this function could be removed.
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 =
var code = evalAux(c, n.sons[1], {})

View File

@@ -345,7 +345,7 @@ const
warnAnalysisLoophole: "thread analysis incomplete due to unkown call '$1' [AnalysisLoophole]",
warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
warnImplicitNarrowing: "implicit narrowing conversion: '$1'",
warnImplicitNarrowing: "implicit narrowing conversion: '$1' [ImplicitNarrowing]",
warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]",
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) =
if r.idx < len(r.tokens):
kind = r.tokens[r.idx].kind
var length = r.tokens[r.idx].length
literal = substr(r.buf, r.pos + 0, r.pos + 0 + length - 1)
var length = r.tokens[r.idx].length.int
literal = substr(r.buf, r.pos, r.pos + length - 1)
inc(r.pos, length)
inc(r.idx)
else:

View File

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

View File

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

View File

@@ -162,7 +162,7 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
else:
var k = skipTypes(a, {tyRange}).kind
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
# a.n.intVal <= lastOrd(f):
# integer literal in the proper range; we want ``i16 + 4`` to stay an
@@ -188,7 +188,9 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
else:
var k = skipTypes(a, {tyRange}).kind
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
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
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 =
case n.kind
of nkSym:
@@ -725,8 +733,9 @@ proc transform(c: PTransf, n: PNode): PTransNode =
var cnst = getConstExpr(c.module, PNode(result))
# we inline constants if they are not complex constants:
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 =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous

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")
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
memory. However in practice this almost never happens as the GC considers
stack roots conservatively. One can use the builtin procs ``GC_ref`` and
@@ -1461,22 +1461,28 @@ algorithm returns true:
# XXX range types?
proc isImplicitlyConvertible(a, b: PType): bool =
case a.kind
of int8: result = b.kind in {int16, int32, int64, int}
of int16: result = b.kind in {int32, int64, int}
of int32: result = b.kind in {int64, int}
of float: result = b.kind in {float32, float64}
of float32: result = b.kind in {float64, float}
of float64: result = b.kind in {float32, float}
of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16,
uint32, uint64, float, float32, float64}
of int8: result = b in {int16, int32, int64, int}
of int16: result = b in {int32, int64, int}
of int32: result = b in {int64, int}
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:
result = b.kind == openArray and typeEquals(a.baseType, b.baseType)
result = b == openArray and typeEquals(a.baseType, b.baseType)
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:
result = b.kind = cstring
result = b = cstring
of cstring, ptr:
result = b.kind == pointer
result = b == pointer
of string:
result = b.kind == cstring
result = b == cstring
A type ``a`` is **explicitly** convertible to type ``b`` iff the following
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 tyChar: add result, reprChar(chr(elem))
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
add result, " (invalid data!)"
@@ -221,6 +221,9 @@ when not defined(useNimRtl):
of tyInt16: add result, $int(cast[ptr Int16](p)[])
of tyInt32: add result, $int(cast[ptr Int32](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 tyFloat32: add result, $(cast[ptr float32](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])
result = (p != nil)
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:
raise newException(EOutOfMemory, "out of memory")
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"
# disabled for now as it somehow runs very slowly ('delete' bug?) but works:
test "gcleak3"
test "weakrefs"
# ------------------------- threading tests -----------------------------------

View File

@@ -87,6 +87,7 @@ Changes affecting backwards compatibility
- Deprecated the ``ssl`` module.
- Deprecated ``nimrod pretty`` as it never worked good enough and has some
inherent problems.
- The integer promotion rules changed.
Compiler Additions
@@ -120,6 +121,8 @@ Language Additions
- Stand-alone ``finally`` and ``except`` blocks are now supported.
- Macros and templates can now be invoked as pragmas.
- 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