.cursor implementation (#12637)
* cursors: first implementation * added currently failing test * .cursor works for doubly linked lists * make -d:useMalloc work again * added code to nil out refs in a destructor * it's now called --gc:arc * renderer.nim: render nkBreakState properly * make simple closure iterators work without leaking
This commit is contained in:
parent
7e689873e2
commit
dfb020b174
12 changed files with 106 additions and 36 deletions
|
|
@ -228,7 +228,7 @@ type
|
||||||
TNodeKinds* = set[TNodeKind]
|
TNodeKinds* = set[TNodeKind]
|
||||||
|
|
||||||
type
|
type
|
||||||
TSymFlag* = enum # already 38 flags!
|
TSymFlag* = enum # 39 flags!
|
||||||
sfUsed, # read access of sym (for warnings) or simply used
|
sfUsed, # read access of sym (for warnings) or simply used
|
||||||
sfExported, # symbol is exported from module
|
sfExported, # symbol is exported from module
|
||||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||||
|
|
@ -284,6 +284,7 @@ type
|
||||||
# variable is generated closure environment; requires early
|
# variable is generated closure environment; requires early
|
||||||
# destruction for --newruntime.
|
# destruction for --newruntime.
|
||||||
sfTemplateParam # symbol is a template parameter
|
sfTemplateParam # symbol is a template parameter
|
||||||
|
sfCursor # variable/field is a cursor, see RFC 177 for details
|
||||||
|
|
||||||
|
|
||||||
TSymFlags* = set[TSymFlag]
|
TSymFlags* = set[TSymFlag]
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||||
of "v2": result = false
|
of "v2": result = false
|
||||||
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
||||||
of "generational": result = false
|
of "generational": result = false
|
||||||
of "destructors": result = conf.selectedGC == gcDestructors
|
of "destructors", "arc": result = conf.selectedGC == gcDestructors
|
||||||
of "hooks": result = conf.selectedGC == gcHooks
|
of "hooks": result = conf.selectedGC == gcHooks
|
||||||
of "go": result = conf.selectedGC == gcGo
|
of "go": result = conf.selectedGC == gcGo
|
||||||
of "none": result = conf.selectedGC == gcNone
|
of "none": result = conf.selectedGC == gcNone
|
||||||
|
|
@ -453,7 +453,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "markandsweep":
|
of "markandsweep":
|
||||||
conf.selectedGC = gcMarkAndSweep
|
conf.selectedGC = gcMarkAndSweep
|
||||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||||
of "destructors":
|
of "destructors", "arc":
|
||||||
conf.selectedGC = gcDestructors
|
conf.selectedGC = gcDestructors
|
||||||
defineSymbol(conf.symbols, "gcdestructors")
|
defineSymbol(conf.symbols, "gcdestructors")
|
||||||
incl conf.globalOptions, optSeqDestructors
|
incl conf.globalOptions, optSeqDestructors
|
||||||
|
|
|
||||||
|
|
@ -101,3 +101,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimFixedForwardGeneric")
|
defineSymbol("nimFixedForwardGeneric")
|
||||||
defineSymbol("nimnomagic64")
|
defineSymbol("nimnomagic64")
|
||||||
defineSymbol("nimNewShiftOps")
|
defineSymbol("nimNewShiftOps")
|
||||||
|
defineSymbol("nimHasCursor")
|
||||||
|
|
|
||||||
|
|
@ -418,6 +418,17 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||||
else:
|
else:
|
||||||
result = p(arg, c)
|
result = p(arg, c)
|
||||||
|
|
||||||
|
proc isCursor(n: PNode): bool =
|
||||||
|
case n.kind
|
||||||
|
of nkSym:
|
||||||
|
result = sfCursor in n.sym.flags
|
||||||
|
of nkDotExpr:
|
||||||
|
result = sfCursor in n[1].sym.flags
|
||||||
|
of nkCheckedFieldExpr:
|
||||||
|
result = isCursor(n[0])
|
||||||
|
else:
|
||||||
|
result = false
|
||||||
|
|
||||||
proc p(n: PNode; c: var Con): PNode =
|
proc p(n: PNode; c: var Con): PNode =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
|
|
@ -459,7 +470,7 @@ proc p(n: PNode; c: var Con): PNode =
|
||||||
if it.kind == nkVarTuple and hasDestructor(ri.typ):
|
if it.kind == nkVarTuple and hasDestructor(ri.typ):
|
||||||
let x = lowerTupleUnpacking(c.graph, it, c.owner)
|
let x = lowerTupleUnpacking(c.graph, it, c.owner)
|
||||||
result.add p(x, c)
|
result.add p(x, c)
|
||||||
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ):
|
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0]):
|
||||||
for j in 0..<it.len-2:
|
for j in 0..<it.len-2:
|
||||||
let v = it[j]
|
let v = it[j]
|
||||||
if v.kind == nkSym:
|
if v.kind == nkSym:
|
||||||
|
|
@ -483,7 +494,8 @@ proc p(n: PNode; c: var Con): PNode =
|
||||||
v.add itCopy
|
v.add itCopy
|
||||||
result.add v
|
result.add v
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
|
if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and
|
||||||
|
not isCursor(n[0]):
|
||||||
# rule (self-assignment-removal):
|
# rule (self-assignment-removal):
|
||||||
if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
|
if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
|
||||||
result = newNodeI(nkEmpty, n.info)
|
result = newNodeI(nkEmpty, n.info)
|
||||||
|
|
@ -515,7 +527,7 @@ proc p(n: PNode; c: var Con): PNode =
|
||||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
|
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
|
||||||
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef,
|
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef,
|
||||||
nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt,
|
nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt,
|
||||||
nkPragma, nkCommentStmt, nkBreakStmt:
|
nkPragma, nkCommentStmt, nkBreakStmt, nkBreakState:
|
||||||
result = n
|
result = n
|
||||||
of nkWhileStmt:
|
of nkWhileStmt:
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,23 @@ proc dotField(x: PNode, f: PSym): PNode =
|
||||||
result.sons[1] = newSymNode(f, x.info)
|
result.sons[1] = newSymNode(f, x.info)
|
||||||
result.typ = f.typ
|
result.typ = f.typ
|
||||||
|
|
||||||
|
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||||
|
result = newNodeI(nkAsgn, le.info, 2)
|
||||||
|
result.sons[0] = le
|
||||||
|
result.sons[1] = ri
|
||||||
|
|
||||||
|
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
|
if c.kind != attachedDestructor:
|
||||||
|
body.add newAsgnStmt(x, y)
|
||||||
|
|
||||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
|
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let f = n.sym
|
let f = n.sym
|
||||||
|
if sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
|
||||||
|
c.g.config.selectedGC == gcDestructors:
|
||||||
|
defaultOp(c, f.typ, body, x.dotField(f), y.dotField(f))
|
||||||
|
else:
|
||||||
fillBody(c, f.typ, body, x.dotField(f), y.dotField(f))
|
fillBody(c, f.typ, body, x.dotField(f), y.dotField(f))
|
||||||
of nkNilLit: discard
|
of nkNilLit: discard
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
|
|
@ -113,11 +126,6 @@ proc newAsgnCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode =
|
||||||
result.add genAddr(g, x)
|
result.add genAddr(g, x)
|
||||||
result.add y
|
result.add y
|
||||||
|
|
||||||
proc newAsgnStmt(le, ri: PNode): PNode =
|
|
||||||
result = newNodeI(nkAsgn, le.info, 2)
|
|
||||||
result.sons[0] = le
|
|
||||||
result.sons[1] = ri
|
|
||||||
|
|
||||||
proc newOpCall(op: PSym; x: PNode): PNode =
|
proc newOpCall(op: PSym; x: PNode): PNode =
|
||||||
result = newNodeIT(nkCall, x.info, op.typ.sons[0])
|
result = newNodeIT(nkCall, x.info, op.typ.sons[0])
|
||||||
result.add(newSymNode(op))
|
result.add(newSymNode(op))
|
||||||
|
|
@ -236,10 +244,6 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
body.add newDeepCopyCall(op, x, y)
|
body.add newDeepCopyCall(op, x, y)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
|
||||||
if c.kind != attachedDestructor:
|
|
||||||
body.add newAsgnStmt(x, y)
|
|
||||||
|
|
||||||
proc addVar(father, v, value: PNode) =
|
proc addVar(father, v, value: PNode) =
|
||||||
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||||
vpart.sons[0] = v
|
vpart.sons[0] = v
|
||||||
|
|
@ -393,6 +397,9 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
|
when false:
|
||||||
|
# XXX investigate if this is necessary:
|
||||||
|
actions.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t))
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
|
||||||
|
|
@ -419,6 +426,9 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
body.add newAsgnStmt(x, y)
|
body.add newAsgnStmt(x, y)
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
|
when false:
|
||||||
|
# XXX investigate if this is necessary:
|
||||||
|
actions.add newAsgnStmt(xenv, newNodeIT(nkNilLit, body.info, xenv.typ))
|
||||||
body.add genIf(c, cond, actions)
|
body.add genIf(c, cond, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,11 @@ const
|
||||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||||
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
||||||
fieldPragmas* = declPragmas + {
|
fieldPragmas* = declPragmas + {
|
||||||
wGuard, wBitsize} - {wExportNims, wNodecl} # why exclude these?
|
wGuard, wBitsize, wCursor} - {wExportNims, wNodecl} # why exclude these?
|
||||||
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
||||||
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
||||||
wNoInit, wCompileTime, wGlobal,
|
wNoInit, wCompileTime, wGlobal,
|
||||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto}
|
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor}
|
||||||
constPragmas* = declPragmas + {wHeader, wMagic,
|
constPragmas* = declPragmas + {wHeader, wMagic,
|
||||||
wGensym, wInject,
|
wGensym, wInject,
|
||||||
wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
|
wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
|
||||||
|
|
@ -833,6 +833,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wVolatile:
|
of wVolatile:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
incl(sym.flags, sfVolatile)
|
incl(sym.flags, sfVolatile)
|
||||||
|
of wCursor:
|
||||||
|
noVal(c, it)
|
||||||
|
incl(sym.flags, sfCursor)
|
||||||
of wRegister:
|
of wRegister:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
incl(sym.flags, sfRegister)
|
incl(sym.flags, sfRegister)
|
||||||
|
|
|
||||||
|
|
@ -1526,6 +1526,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
|
|
||||||
of nkBreakState:
|
of nkBreakState:
|
||||||
put(g, tkTuple, "breakstate")
|
put(g, tkTuple, "breakstate")
|
||||||
|
if renderIds in g.flags:
|
||||||
|
gsons(g, n, c, 0)
|
||||||
of nkTypeClassTy:
|
of nkTypeClassTy:
|
||||||
gTypeClassTy(g, n)
|
gTypeClassTy(g, n)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ type
|
||||||
wColon, wColonColon, wEquals, wDot, wDotDot,
|
wColon, wColonColon, wEquals, wDot, wDotDot,
|
||||||
wStar, wMinus,
|
wStar, wMinus,
|
||||||
wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks,
|
wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks,
|
||||||
wIntDefine, wStrDefine, wBoolDefine
|
wIntDefine, wStrDefine, wBoolDefine, wCursor,
|
||||||
|
|
||||||
wImmediate, wConstructor, wDestructor, wDelegator, wOverride,
|
wImmediate, wConstructor, wDestructor, wDelegator, wOverride,
|
||||||
wImportCpp, wImportObjC,
|
wImportCpp, wImportObjC,
|
||||||
|
|
@ -121,7 +121,7 @@ const
|
||||||
":", "::", "=", ".", "..",
|
":", "::", "=", ".", "..",
|
||||||
"*", "-",
|
"*", "-",
|
||||||
"magic", "thread", "final", "profiler", "memtracker", "objchecks",
|
"magic", "thread", "final", "profiler", "memtracker", "objchecks",
|
||||||
"intdefine", "strdefine", "booldefine",
|
"intdefine", "strdefine", "booldefine", "cursor",
|
||||||
|
|
||||||
"immediate", "constructor", "destructor", "delegator", "override",
|
"immediate", "constructor", "destructor", "delegator", "override",
|
||||||
"importcpp", "importobjc",
|
"importcpp", "importobjc",
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ proc getLocalAllocator*(): Allocator =
|
||||||
result = addr allocatorStorage
|
result = addr allocatorStorage
|
||||||
result.alloc = proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall, raises: [].} =
|
result.alloc = proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall, raises: [].} =
|
||||||
when defined(useMalloc) and not defined(nimscript):
|
when defined(useMalloc) and not defined(nimscript):
|
||||||
result = c_malloc(size)
|
result = c_malloc(cuint size)
|
||||||
# XXX do we need this?
|
# XXX do we need this?
|
||||||
nimZeroMem(result, size)
|
nimZeroMem(result, size)
|
||||||
else:
|
else:
|
||||||
|
|
@ -57,7 +57,7 @@ proc getLocalAllocator*(): Allocator =
|
||||||
inc a.deallocCount
|
inc a.deallocCount
|
||||||
result.realloc = proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall, raises: [].} =
|
result.realloc = proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall, raises: [].} =
|
||||||
when defined(useMalloc) and not defined(nimscript):
|
when defined(useMalloc) and not defined(nimscript):
|
||||||
result = c_realloc(p, newSize)
|
result = c_realloc(p, cuint newSize)
|
||||||
else:
|
else:
|
||||||
result = system.realloc(p, newSize)
|
result = system.realloc(p, newSize)
|
||||||
nimZeroMem(result +! oldSize, newSize - oldSize)
|
nimZeroMem(result +! oldSize, newSize - oldSize)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} =
|
||||||
when defined(nimscript):
|
when defined(nimscript):
|
||||||
discard
|
discard
|
||||||
elif defined(useMalloc):
|
elif defined(useMalloc):
|
||||||
var orig = c_malloc(s)
|
var orig = c_malloc(cuint s)
|
||||||
nimZeroMem(orig, s)
|
nimZeroMem(orig, s)
|
||||||
result = orig +! sizeof(RefHeader)
|
result = orig +! sizeof(RefHeader)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,16 @@
|
||||||
when not defined(nimhygiene):
|
when not defined(nimhygiene):
|
||||||
{.pragma: dirty.}
|
{.pragma: dirty.}
|
||||||
|
|
||||||
|
when not defined(nimHasCursor):
|
||||||
|
{.pragma: cursor.}
|
||||||
|
|
||||||
type
|
type
|
||||||
DoublyLinkedNodeObj*[T] = object ## \
|
DoublyLinkedNodeObj*[T] = object ## \
|
||||||
## A node a doubly linked list consists of.
|
## A node a doubly linked list consists of.
|
||||||
##
|
##
|
||||||
## It consists of a `value` field, and pointers to `next` and `prev`.
|
## It consists of a `value` field, and pointers to `next` and `prev`.
|
||||||
next*: <//>(ref DoublyLinkedNodeObj[T])
|
next*: <//>(ref DoublyLinkedNodeObj[T])
|
||||||
prev*: ref DoublyLinkedNodeObj[T]
|
prev* {.cursor.}: ref DoublyLinkedNodeObj[T]
|
||||||
value*: T
|
value*: T
|
||||||
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
||||||
|
|
||||||
|
|
@ -100,7 +103,7 @@ type
|
||||||
## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create
|
## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create
|
||||||
## a new empty list.
|
## a new empty list.
|
||||||
head*: <//>(SinglyLinkedNode[T])
|
head*: <//>(SinglyLinkedNode[T])
|
||||||
tail*: SinglyLinkedNode[T]
|
tail* {.cursor.}: SinglyLinkedNode[T]
|
||||||
|
|
||||||
DoublyLinkedList*[T] = object ## \
|
DoublyLinkedList*[T] = object ## \
|
||||||
## A doubly linked list.
|
## A doubly linked list.
|
||||||
|
|
@ -108,7 +111,7 @@ type
|
||||||
## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create
|
## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create
|
||||||
## a new empty list.
|
## a new empty list.
|
||||||
head*: <//>(DoublyLinkedNode[T])
|
head*: <//>(DoublyLinkedNode[T])
|
||||||
tail*: DoublyLinkedNode[T]
|
tail* {.cursor.}: DoublyLinkedNode[T]
|
||||||
|
|
||||||
SinglyLinkedRing*[T] = object ## \
|
SinglyLinkedRing*[T] = object ## \
|
||||||
## A singly linked ring.
|
## A singly linked ring.
|
||||||
|
|
@ -116,7 +119,7 @@ type
|
||||||
## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create
|
## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create
|
||||||
## a new empty ring.
|
## a new empty ring.
|
||||||
head*: <//>(SinglyLinkedNode[T])
|
head*: <//>(SinglyLinkedNode[T])
|
||||||
tail*: SinglyLinkedNode[T]
|
tail* {.cursor.}: SinglyLinkedNode[T]
|
||||||
|
|
||||||
DoublyLinkedRing*[T] = object ## \
|
DoublyLinkedRing*[T] = object ## \
|
||||||
## A doubly linked ring.
|
## A doubly linked ring.
|
||||||
|
|
|
||||||
38
tests/destructor/tlists.nim
Normal file
38
tests/destructor/tlists.nim
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
discard """
|
||||||
|
output: '''Success
|
||||||
|
@["a", "b", "c"]
|
||||||
|
0'''
|
||||||
|
cmd: '''nim c --gc:destructors $file'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
import lists
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
proc mkleak() =
|
||||||
|
# allocate 1 MB via linked lists
|
||||||
|
let numberOfLists = 100
|
||||||
|
for i in countUp(1, numberOfLists):
|
||||||
|
var leakList = initDoublyLinkedList[string]()
|
||||||
|
let numberOfLeaks = 5000
|
||||||
|
for j in countUp(1, numberOfLeaks):
|
||||||
|
leakList.append(newString(200))
|
||||||
|
|
||||||
|
proc mkManyLeaks() =
|
||||||
|
for i in 0..0:
|
||||||
|
mkleak()
|
||||||
|
echo "Success"
|
||||||
|
|
||||||
|
iterator foobar(c: string): seq[string] {.closure.} =
|
||||||
|
yield @["a", "b", c]
|
||||||
|
|
||||||
|
proc tsimpleClosureIterator =
|
||||||
|
var myc = "c"
|
||||||
|
for it in foobar(myc):
|
||||||
|
echo it
|
||||||
|
|
||||||
|
let startMem = getOccupiedMem()
|
||||||
|
mkManyLeaks()
|
||||||
|
tsimpleClosureIterator()
|
||||||
|
echo getOccupiedMem() - startMem
|
||||||
Loading…
Add table
Add a link
Reference in a new issue