mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-14 23:33:28 +00:00
added devel/logging; weakrefs test; next steps for proper unsigned support
This commit is contained in:
@@ -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], {})
|
||||
|
||||
@@ -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]",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
145
devel/logging.nim
Normal 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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)[])
|
||||
|
||||
@@ -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
48
tests/gc/weakrefs.nim
Normal 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()
|
||||
@@ -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 -----------------------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user