added devel/logging; weakrefs test; next steps for proper unsigned support
This commit is contained in:
parent
8ef48a34e5
commit
36247e0947
14 changed files with 243 additions and 25 deletions
|
|
@ -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], {})
|
||||||
|
|
|
||||||
|
|
@ -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]",
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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 mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n)
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -725,8 +733,9 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||||
var cnst = getConstExpr(c.module, PNode(result))
|
var cnst = getConstExpr(c.module, PNode(result))
|
||||||
# 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
|
||||||
# this step! We have to rely that the semantic pass transforms too errornous
|
# 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")
|
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:
|
||||||
|
|
|
||||||
|
|
@ -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)[])
|
||||||
|
|
|
||||||
|
|
@ -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
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"
|
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 -----------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue