diff options
-rw-r--r-- | compiler/ccgexprs.nim | 6 | ||||
-rw-r--r-- | compiler/commands.nim | 4 | ||||
-rw-r--r-- | compiler/installer.ini | 1 | ||||
-rw-r--r-- | compiler/options.nim | 1 | ||||
-rw-r--r-- | compiler/renderer.nim | 129 | ||||
-rw-r--r-- | doc/advopt.txt | 1 | ||||
-rw-r--r-- | lib/pure/coro.nim | 2 | ||||
-rw-r--r-- | lib/pure/httpclient.nim | 15 | ||||
-rw-r--r-- | lib/system.nim | 78 | ||||
-rw-r--r-- | lib/system/alloc.nim | 8 | ||||
-rw-r--r-- | lib/system/ansi_c.nim | 18 | ||||
-rw-r--r-- | lib/system/excpt.nim | 2 | ||||
-rw-r--r-- | lib/system/gc2.nim | 190 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 2 | ||||
-rw-r--r-- | lib/system/repr.nim | 1 | ||||
-rw-r--r-- | lib/system/sysio.nim | 110 | ||||
-rw-r--r-- | tools/heapdump2dot.nim | 66 | ||||
-rw-r--r-- | web/news.txt | 7 |
18 files changed, 419 insertions, 222 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index fcc36e4fd..1a5334a98 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2111,8 +2111,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = initLocExpr(p, n.sons[0], a) of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: - if p.module.compileToCpp: genTryCpp(p, n, d) - else: genTry(p, n, d) + if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions: + genTryCpp(p, n, d) + else: + genTry(p, n, d) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection: # we have to emit the type information for object types here to support diff --git a/compiler/commands.nim b/compiler/commands.nim index 8dacebd83..2622d64f4 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -619,6 +619,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = cAssembler = nameToCC(arg) if cAssembler notin cValidAssemblers: localError(info, errGenerated, "'$1' is not a valid assembler." % [arg]) + of "nocppexceptions": + expectNoArg(switch, arg, pass, info) + incl(gGlobalOptions, optNoCppExceptions) + defineSymbol("noCppExceptions") else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: invalidCmdLineOption(pass, switch, info) diff --git a/compiler/installer.ini b/compiler/installer.ini index 95c07e003..12d9baf82 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -104,6 +104,7 @@ Files: "lib/pure/concurrency/*.cfg" Files: "lib/impure/*.nim" Files: "lib/impure/nre/private/*.nim" Files: "lib/wrappers/*.nim" +Files: "lib/arch/*.nim" Files: "lib/wrappers/readline/*.nim" Files: "lib/wrappers/linenoise/*.nim" diff --git a/compiler/options.nim b/compiler/options.nim index 82d18e242..29cdd96fb 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -66,6 +66,7 @@ type # please make sure we have under 32 options # also: generate header file optIdeDebug # idetools: debug mode optIdeTerse # idetools: use terse descriptions + optNoCppExceptions # use C exception handling even with CPP TGlobalOptions* = set[TGlobalOption] TCommands* = enum # Nim's commands # **keep binary compatible** diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 34688c798..d63cba4be 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -803,6 +803,12 @@ proc doParamsAux(g: var TSrcGen, params: PNode) = putWithSpace(g, tkOpr, "->") gsub(g, params.sons[0]) +proc gsub(g: var TSrcGen; n: PNode; i: int) = + if i < n.len: + gsub(g, n[i]) + else: + put(g, tkOpr, "<<" & $i & "th child missing for " & $n.kind & " >>") + proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if isNil(n): return var @@ -837,8 +843,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gcomma(g, n, 1) put(g, tkParRi, ")") of nkCallStrLit: - gsub(g, n.sons[0]) - if n.sons[1].kind == nkRStrLit: + gsub(g, n, 0) + if n.len > 1 and n.sons[1].kind == nkRStrLit: put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"') else: gsub(g, n.sons[1]) @@ -846,10 +852,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkCast: put(g, tkCast, "cast") put(g, tkBracketLe, "[") - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkBracketRi, "]") put(g, tkParLe, "(") - gsub(g, n.sons[1]) + gsub(g, n, 1) put(g, tkParRi, ")") of nkAddr: put(g, tkAddr, "addr") @@ -860,29 +866,29 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkStaticExpr: put(g, tkStatic, "static") put(g, tkSpaces, Space) - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkBracketExpr: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkBracketLe, "[") gcomma(g, n, 1) put(g, tkBracketRi, "]") of nkCurlyExpr: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkCurlyLe, "{") gcomma(g, n, 1) put(g, tkCurlyRi, "}") of nkPragmaExpr: - gsub(g, n.sons[0]) + gsub(g, n, 0) gcomma(g, n, 1) of nkCommand: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkSpaces, Space) gcomma(g, n, 1) of nkExprEqExpr, nkAsgn, nkFastAsgn: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") - gsub(g, n.sons[1]) + gsub(g, n, 1) of nkChckRangeF: put(g, tkSymbol, "chckRangeF") put(g, tkParLe, "(") @@ -936,33 +942,34 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gcomma(g, n, c) put(g, tkBracketRi, "]") of nkDotExpr: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkDot, ".") - gsub(g, n.sons[1]) + gsub(g, n, 1) of nkBind: putWithSpace(g, tkBind, "bind") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref: - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkLambda: putWithSpace(g, tkProc, "proc") - gsub(g, n.sons[paramsPos]) - gsub(g, n.sons[pragmasPos]) + gsub(g, n, paramsPos) + gsub(g, n, pragmasPos) put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") - gsub(g, n.sons[bodyPos]) + gsub(g, n, bodyPos) of nkDo: putWithSpace(g, tkDo, "do") - doParamsAux(g, n.sons[paramsPos]) - gsub(g, n.sons[pragmasPos]) + if paramsPos < n.len: + doParamsAux(g, n.sons[paramsPos]) + gsub(g, n, pragmasPos) put(g, tkColon, ":") - gsub(g, n.sons[bodyPos]) + gsub(g, n, bodyPos) of nkConstDef, nkIdentDefs: gcomma(g, n, 0, -3) var L = sonsLen(n) if L >= 2 and n.sons[L - 2].kind != nkEmpty: putWithSpace(g, tkColon, ":") - gsub(g, n.sons[L - 2]) + gsub(g, n, L - 2) if L >= 1 and n.sons[L - 1].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") @@ -975,20 +982,20 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = putWithSpace(g, tkEquals, "=") gsub(g, lastSon(n), c) of nkExprColonExpr: - gsub(g, n.sons[0]) + gsub(g, n, 0) putWithSpace(g, tkColon, ":") - gsub(g, n.sons[1]) + gsub(g, n, 1) of nkInfix: - gsub(g, n.sons[1]) + gsub(g, n, 1) put(g, tkSpaces, Space) - gsub(g, n.sons[0]) # binary operator + gsub(g, n, 0) # binary operator if not fits(g, lsub(n.sons[2]) + lsub(n.sons[0]) + 1): optNL(g, g.indent + longIndentWid) else: put(g, tkSpaces, Space) - gsub(g, n.sons[2]) + gsub(g, n, 2) of nkPrefix: - gsub(g, n.sons[0]) + gsub(g, n, 0) if n.len > 1: put(g, tkSpaces, Space) if n.sons[1].kind == nkInfix: @@ -998,14 +1005,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = else: gsub(g, n.sons[1]) of nkPostfix: - gsub(g, n.sons[1]) - gsub(g, n.sons[0]) + gsub(g, n, 1) + gsub(g, n, 0) of nkRange: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkDotDot, "..") - gsub(g, n.sons[1]) + gsub(g, n, 1) of nkDerefExpr: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkOpr, "[]") of nkAccQuoted: put(g, tkAccent, "`") @@ -1016,19 +1023,19 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkAccent, "`") of nkIfExpr: putWithSpace(g, tkIf, "if") - gsub(g, n.sons[0].sons[0]) + if n.len > 0: gsub(g, n.sons[0], 0) putWithSpace(g, tkColon, ":") - gsub(g, n.sons[0].sons[1]) + if n.len > 0: gsub(g, n.sons[0], 1) gsons(g, n, emptyContext, 1) of nkElifExpr: putWithSpace(g, tkElif, " elif") - gsub(g, n.sons[0]) + gsub(g, n, 0) putWithSpace(g, tkColon, ":") - gsub(g, n.sons[1]) + gsub(g, n, 1) of nkElseExpr: put(g, tkElse, " else") putWithSpace(g, tkColon, ":") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkTypeOfExpr: put(g, tkType, "type") put(g, tkParLe, "(") @@ -1065,10 +1072,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = else: put(g, tkDistinct, "distinct") of nkTypeDef: - gsub(g, n.sons[0]) - gsub(g, n.sons[1]) + gsub(g, n, 0) + gsub(g, n, 1) put(g, tkSpaces, Space) - if n.sons[2].kind != nkEmpty: + if n.len > 2 and n.sons[2].kind != nkEmpty: putWithSpace(g, tkEquals, "=") gsub(g, n.sons[2]) of nkObjectTy: @@ -1090,19 +1097,19 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = putNL(g) of nkOfInherit: putWithSpace(g, tkOf, "of") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkProcTy: if sonsLen(n) > 0: putWithSpace(g, tkProc, "proc") - gsub(g, n.sons[0]) - gsub(g, n.sons[1]) + gsub(g, n, 0) + gsub(g, n, 1) else: put(g, tkProc, "proc") of nkIteratorTy: if sonsLen(n) > 0: putWithSpace(g, tkIterator, "iterator") - gsub(g, n.sons[0]) - gsub(g, n.sons[1]) + gsub(g, n, 0) + gsub(g, n, 1) else: put(g, tkIterator, "iterator") of nkStaticTy: @@ -1123,10 +1130,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = else: put(g, tkEnum, "enum") of nkEnumFieldDef: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") - gsub(g, n.sons[1]) + gsub(g, n, 1) of nkStmtList, nkStmtListExpr, nkStmtListType: gstmts(g, n, emptyContext) of nkIfStmt: putWithSpace(g, tkIf, "if") @@ -1183,22 +1190,22 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) of nkReturnStmt: putWithSpace(g, tkReturn, "return") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkRaiseStmt: putWithSpace(g, tkRaise, "raise") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkYieldStmt: putWithSpace(g, tkYield, "yield") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkDiscardStmt: putWithSpace(g, tkDiscard, "discard") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkBreakStmt: putWithSpace(g, tkBreak, "break") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkContinueStmt: putWithSpace(g, tkContinue, "continue") - gsub(g, n.sons[0]) + gsub(g, n, 0) of nkPragma: if renderNoPragmas notin g.flags: if g.inPragma <= 0: @@ -1226,7 +1233,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = putWithSpace(g, tkImport, "import") else: putWithSpace(g, tkExport, "export") - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkSpaces, Space) putWithSpace(g, tkExcept, "except") gcommaAux(g, n, g.indent, 1) @@ -1234,7 +1241,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = putNL(g) of nkFromStmt: putWithSpace(g, tkFrom, "from") - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkSpaces, Space) putWithSpace(g, tkImport, "import") gcomma(g, n, emptyContext, 1) @@ -1257,10 +1264,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gcoms(g) gstmts(g, lastSon(n), c) of nkImportAs: - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkSpaces, Space) putWithSpace(g, tkAs, "as") - gsub(g, n.sons[1]) + gsub(g, n, 1) of nkBindStmt: putWithSpace(g, tkBind, "bind") gcomma(g, n, c) @@ -1270,7 +1277,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkElifBranch: optNL(g) putWithSpace(g, tkElif, "elif") - gsub(g, n.sons[0]) + gsub(g, n, 0) putWithSpace(g, tkColon, ":") gcoms(g) gstmts(g, n.sons[1], c) @@ -1304,7 +1311,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") gsemicolon(g, n, 1) put(g, tkParRi, ")") - if n.sons[0].kind != nkEmpty: + if n.len > 0 and n.sons[0].kind != nkEmpty: putWithSpace(g, tkColon, ":") gsub(g, n.sons[0]) of nkTupleTy: @@ -1316,7 +1323,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkTuple, "tuple") of nkMetaNode_Obsolete: put(g, tkParLe, "(META|") - gsub(g, n.sons[0]) + gsub(g, n, 0) put(g, tkParRi, ")") of nkGotoState, nkState: var c: TContext diff --git a/doc/advopt.txt b/doc/advopt.txt index 02849498f..02aada4fb 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -69,6 +69,7 @@ Advanced options: --putenv:key=value set an environment variable --NimblePath:PATH add a path for Nimble support --noNimblePath deactivate the Nimble path + --noCppExceptions use default exception handling with C++ backend --excludePath:PATH exclude a path from the list of search paths --dynlibOverride:SYMBOL marks SYMBOL so that dynlib:SYMBOL has no effect and can be statically linked instead; diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index c5724f26f..2a81b7317 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -when not defined(nimCoroutines): +when not defined(nimCoroutines) and not defined(nimdoc): {.error: "Coroutines require -d:nimCoroutines".} import os, times diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 1b91132db..603763386 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -455,14 +455,15 @@ proc redirection(status: string): bool = if status.startsWith(i): return true -proc getNewLocation(lastUrl: string, headers: StringTableRef): string = +proc getNewLocation(lastURL: string, headers: StringTableRef): string = result = headers.getOrDefault"Location" if result == "": httpError("location header expected") # Relative URLs. (Not part of the spec, but soon will be.) let r = parseUri(result) if r.hostname == "" and r.path != "": - let origParsed = parseUri(lastUrl) - result = origParsed.hostname & "/" & r.path + var parsed = parseUri(lastURL) + parsed.path = r.path + result = $parsed proc get*(url: string, extraHeaders = "", maxRedirects = 5, sslContext: SSLContext = defaultSSLContext, @@ -481,7 +482,7 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5, let redirectTo = getNewLocation(lastURL, result.headers) result = request(redirectTo, httpGET, extraHeaders, "", sslContext, timeout, userAgent, proxy) - lastUrl = redirectTo + lastURL = redirectTo proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, sslContext: SSLContext = defaultSSLContext, @@ -528,14 +529,14 @@ proc post*(url: string, extraHeaders = "", body = "", result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent, proxy) - var lastUrl = "" + var lastURL = url for i in 1..maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) var meth = if result.status != "307": httpGet else: httpPost result = request(redirectTo, meth, xh, xb, sslContext, timeout, userAgent, proxy) - lastUrl = redirectTo + lastURL = redirectTo proc postContent*(url: string, extraHeaders = "", body = "", maxRedirects = 5, @@ -827,7 +828,7 @@ proc get*(client: AsyncHttpClient, url: string): Future[Response] {.async.} = if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) result = await client.request(redirectTo, httpGET) - lastUrl = redirectTo + lastURL = redirectTo proc post*(client: AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[Response] {.async.} = ## Connects to the hostname specified by the URL and performs a POST request. diff --git a/lib/system.nim b/lib/system.nim index 2c049b6b6..c28d0df8b 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2589,6 +2589,30 @@ when not defined(JS): #and not defined(nimscript): var strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic}) + + # ----------------- IO Part ------------------------------------------------ + type + CFile {.importc: "FILE", header: "<stdio.h>", + final, incompletestruct.} = object + File* = ptr CFile ## The type representing a file handle. + + FileMode* = enum ## The file mode when opening a file. + fmRead, ## Open the file for read access only. + fmWrite, ## Open the file for write access only. + fmReadWrite, ## Open the file for read and write access. + ## If the file does not exist, it will be + ## created. + fmReadWriteExisting, ## Open the file for read and write access. + ## If the file does not exist, it will not be + ## created. + fmAppend ## Open the file for writing only; append data + ## at the end. + + FileHandle* = cint ## type that represents an OS file handle; this is + ## useful for low-level file access + + {.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].} + when not defined(nimscript): include "system/ansi_c" @@ -2600,58 +2624,32 @@ when not defined(JS): #and not defined(nimscript): elif x > y: result = 1 else: result = 0 - const pccHack = if defined(pcc): "_" else: "" # Hack for PCC - when not defined(nimscript): + when not defined(nimscript) and hostOS != "standalone": when defined(windows): # work-around C's sucking abstraction: # BUGFIX: stdin and stdout should be binary files! - proc setmode(handle, mode: int) {.importc: pccHack & "setmode", + proc setmode(handle, mode: int) {.importc: "setmode", header: "<io.h>".} - proc fileno(f: C_TextFileStar): int {.importc: pccHack & "fileno", + proc fileno(f: C_TextFileStar): int {.importc: "fileno", header: "<fcntl.h>".} var - O_BINARY {.importc: pccHack & "O_BINARY", nodecl.}: int + O_BINARY {.importc: "O_BINARY", nodecl.}: int - # we use binary mode in Windows: + # we use binary mode on Windows: setmode(fileno(c_stdin), O_BINARY) setmode(fileno(c_stdout), O_BINARY) when defined(endb): proc endbStep() - # ----------------- IO Part ------------------------------------------------ - when hostOS != "standalone": - type - CFile {.importc: "FILE", header: "<stdio.h>", - final, incompletestruct.} = object - File* = ptr CFile ## The type representing a file handle. - - FileMode* = enum ## The file mode when opening a file. - fmRead, ## Open the file for read access only. - fmWrite, ## Open the file for write access only. - fmReadWrite, ## Open the file for read and write access. - ## If the file does not exist, it will be - ## created. - fmReadWriteExisting, ## Open the file for read and write access. - ## If the file does not exist, it will not be - ## created. - fmAppend ## Open the file for writing only; append data - ## at the end. - - FileHandle* = cint ## type that represents an OS file handle; this is - ## useful for low-level file access - - {.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].} - - when not defined(nimscript): - # text file handling: - var - stdin* {.importc: "stdin", header: "<stdio.h>".}: File - ## The standard input stream. - stdout* {.importc: "stdout", header: "<stdio.h>".}: File - ## The standard output stream. - stderr* {.importc: "stderr", header: "<stdio.h>".}: File - ## The standard error stream. + # text file handling: + var + stdin* {.importc: "stdin", header: "<stdio.h>".}: File + ## The standard input stream. + stdout* {.importc: "stdout", header: "<stdio.h>".}: File + ## The standard output stream. + stderr* {.importc: "stderr", header: "<stdio.h>".}: File + ## The standard error stream. when defined(useStdoutAsStdmsg): template stdmsg*: File = stdout @@ -2947,7 +2945,7 @@ when not defined(JS): #and not defined(nimscript): else: include "system/sysio" - when declared(open) and declared(close) and declared(readline): + when not defined(nimscript) and hostOS != "standalone": iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} = ## Iterates over any line in the file named `filename`. ## diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 6de8e19e7..67d380391 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -592,16 +592,16 @@ proc allocInv(a: MemRegion): bool = ## checks some (not all yet) invariants of the allocator's data structures. for s in low(a.freeSmallChunks)..high(a.freeSmallChunks): var c = a.freeSmallChunks[s] - while c != nil: + while not (c == nil): if c.next == c: echo "[SYSASSERT] c.next == c" return false - if c.size != s * MemAlign: + if not (c.size == s * MemAlign): echo "[SYSASSERT] c.size != s * MemAlign" return false var it = c.freeList - while it != nil: - if it.zeroField != 0: + while not (it == nil): + if not (it.zeroField == 0): echo "[SYSASSERT] it.zeroField != 0" c_printf("%ld %p\n", it.zeroField, it) return false diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 702559034..1bbd89fe7 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -23,14 +23,20 @@ proc c_strlen(a: cstring): int {.header: "<string.h>", proc c_memset(p: pointer, value: cint, size: int) {. header: "<string.h>", importc: "memset".} -type - C_TextFile {.importc: "FILE", header: "<stdio.h>", - final, incompleteStruct.} = object - C_BinaryFile {.importc: "FILE", header: "<stdio.h>", +when not declared(File): + type + C_TextFile {.importc: "FILE", header: "<stdio.h>", final, incompleteStruct.} = object - C_TextFileStar = ptr C_TextFile - C_BinaryFileStar = ptr C_BinaryFile + C_BinaryFile {.importc: "FILE", header: "<stdio.h>", + final, incompleteStruct.} = object + C_TextFileStar = ptr C_TextFile + C_BinaryFileStar = ptr C_BinaryFile +else: + type + C_TextFileStar = File + C_BinaryFileStar = File +type C_JmpBuf {.importc: "jmp_buf", header: "<setjmp.h>".} = object when not defined(vm): diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index df28c1493..8d1e04b8d 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -216,7 +216,7 @@ proc raiseExceptionAux(e: ref Exception) = if not localRaiseHook(e): return if globalRaiseHook != nil: if not globalRaiseHook(e): return - when defined(cpp): + when defined(cpp) and not defined(noCppExceptions): if e[] of OutOfMemError: showErrorMessage(e.name) quitOrDebug() diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 6b6b81824..6c44d509e 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -52,7 +52,8 @@ type WalkOp = enum waMarkGlobal, # part of the backup mark&sweep waMarkGrey, - waZctDecRef #, waDebug + waZctDecRef, + waDebug Phase {.pure.} = enum None, Marking, Sweeping @@ -78,7 +79,7 @@ type GcHeap = object # this contains the zero count and # non-zero count table - black: int # either 0 or 1. + black, red: int # either 0 or 1. stack: ptr GcStack stackBottom: pointer phase: Phase @@ -95,6 +96,7 @@ type stat: GcStat additionalRoots: CellSeq # dummy roots for GC_ref/unref spaceIter: ObjectSpaceIter + dumpHeapFile: File # File that is used for GC_dumpHeap var gch {.rtlThreadVar.}: GcHeap @@ -104,6 +106,7 @@ when not defined(useNimRtl): proc initGC() = when not defined(useNimRtl): + gch.red = (1-gch.black) gch.cycleThreshold = InitialCycleThreshold gch.stat.stackScans = 0 gch.stat.cycleCollections = 0 @@ -117,6 +120,14 @@ proc initGC() = init(gch.additionalRoots) init(gch.greyStack) +# Which color to use for new objects is tricky: When we're marking, +# they have to be *white* so that everything is marked that is only +# reachable from them. However, when we are sweeping, they have to +# be black, so that we don't free them prematuredly. In order to save +# a comparison gch.phase == Phase.Marking, we use the pseudo-color +# 'red' for new objects. +template allocColor(): untyped = gch.red + template gcAssert(cond: bool, msg: string) = when defined(useGcAssert): if not cond: @@ -156,15 +167,25 @@ template color(c): expr = c.refCount and colorMask template setColor(c, col) = c.refcount = c.refcount and not colorMask or col -proc writeCell(msg: cstring, c: PCell) = +proc writeCell(file: File; msg: cstring, c: PCell) = var kind = -1 if c.typ != nil: kind = ord(c.typ.kind) + let col = if c.color == rcGrey: 'g' + elif c.color == gch.black: 'b' + else: 'w' + when useCellIds: + let id = c.id + else: + let id = c when leakDetector: - c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n", - msg, c, kind, c.refcount shr rcShift, c.filename, c.line) + c_fprintf(file, "%s %p %d rc=%ld color=%c from %s(%ld)\n", + msg, id, kind, c.refcount shr rcShift, col, c.filename, c.line) else: - c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld; color=%ld\n", - msg, c, kind, c.refcount shr rcShift, c.color) + c_fprintf(file, "%s %p %d rc=%ld color=%c\n", + msg, id, kind, c.refcount shr rcShift, col) + +proc writeCell(msg: cstring, c: PCell) = + c_stdout.writeCell(msg, c) proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.} @@ -236,7 +257,11 @@ proc nimGCunref(p: pointer) {.compilerProc.} = dec(i) template markGrey(x: PCell) = - if x.color == 1-gch.black and gch.phase == Phase.Marking: + if x.color != 1-gch.black and gch.phase == Phase.Marking: + if not isAllocatedPtr(gch.region, x): + c_fprintf(c_stdout, "[GC] markGrey proc: %p\n", x) + #GC_dumpHeap() + sysAssert(false, "wtf") x.setColor(rcGrey) add(gch.greyStack, x) @@ -425,7 +450,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = res.filename = framePtr.prev.filename res.line = framePtr.prev.line # refcount is zero, color is black, but mark it to be in the ZCT - res.refcount = ZctFlag or gch.black + res.refcount = ZctFlag or allocColor() sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3") # its refcount is zero, so add it to the ZCT: addNewObjToZCT(res, gch) @@ -468,11 +493,11 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ - when leakDetector and not hasThreadSupport: + when leakDetector: if framePtr != nil and framePtr.prev != nil: res.filename = framePtr.prev.filename res.line = framePtr.prev.line - res.refcount = rcIncrement or gch.black # refcount is 1 + res.refcount = rcIncrement or allocColor() # refcount is 1 sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3") when logGC: writeCell("new cell", res) gcTrace(res, csAllocated) @@ -536,7 +561,7 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = # A better fix would be to emit the location specific write barrier for # 'growObj', but this is lots of more work and who knows what new problems # this would create. - res.refcount = rcIncrement or gch.black + res.refcount = rcIncrement or allocColor() decRef(ol) else: sysAssert(ol.typ != nil, "growObj: 5") @@ -581,6 +606,49 @@ template checkTime {.dirty.} = if duration >= gch.maxPause - 50_000: return false +# ---------------- dump heap ---------------- + +proc debugGraph(s: PCell) = + c_fprintf(gch.dumpHeapFile, "child %p\n", s) + +proc dumpRoot(gch: var GcHeap; s: PCell) = + if isAllocatedPtr(gch.region, s): + c_fprintf(gch.dumpHeapFile, "global_root %p\n", s) + else: + c_fprintf(gch.dumpHeapFile, "global_root_invalid %p\n", s) + +proc GC_dumpHeap*(file: File) = + ## Dumps the GCed heap's content to a file. Can be useful for + ## debugging. Produces an undocumented text file format that + ## can be translated into "dot" syntax via the "heapdump2dot" tool. + gch.dumpHeapFile = file + var spaceIter: ObjectSpaceIter + var d = gch.decStack.d + for i in 0 .. < gch.decStack.len: + if isAllocatedPtr(gch.region, d[i]): + c_fprintf(file, "onstack %p\n", d[i]) + else: + c_fprintf(file, "onstack_invalid %p\n", d[i]) + for i in 0 .. < globalMarkersLen: globalMarkers[i]() + while true: + let x = allObjectsAsProc(gch.region, addr spaceIter) + if spaceIter.state < 0: break + if isCell(x): + # cast to PCell is correct here: + var c = cast[PCell](x) + writeCell(file, "cell ", c) + forAllChildren(c, waDebug) + c_fprintf(file, "end\n") + gch.dumpHeapFile = nil + +proc GC_dumpHeap() = + var f: File + if open(f, "heap.txt", fmWrite): + GC_dumpHeap(f) + f.close() + else: + c_fprintf(stdout, "cannot write heap.txt") + # ---------------- cycle collector ------------------------------------------- proc freeCyclicCell(gch: var GcHeap, c: PCell) = @@ -588,6 +656,9 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) = var d = gch.decStack.d for i in 0..gch.decStack.len-1: + if d[i] == c: + writeCell("freeing ", c) + GC_dumpHeap() gcAssert d[i] != c, "wtf man, freeing obviously alive stuff?!!" prepareDealloc(c) @@ -603,8 +674,8 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) = proc sweep(gch: var GcHeap): bool = takeStartTime(100) #echo "loop start" - let black = gch.black - cfprintf(cstdout, "black is %d\n", black) + let white = 1-gch.black + #cfprintf(cstdout, "black is %d\n", black) while true: let x = allObjectsAsProc(gch.region, addr gch.spaceIter) if gch.spaceIter.state < 0: break @@ -613,7 +684,7 @@ proc sweep(gch: var GcHeap): bool = # cast to PCell is correct here: var c = cast[PCell](x) gcAssert c.color != rcGrey, "cell is still grey?" - if c.color != black: freeCyclicCell(gch, c) + if c.color == white: freeCyclicCell(gch, c) # Since this is incremental, we MUST not set the object to 'white' here. # We could set all the remaining objects to white after the 'sweep' # completed but instead we flip the meaning of black/white to save one @@ -624,11 +695,19 @@ proc sweep(gch: var GcHeap): bool = gch.spaceIter = ObjectSpaceIter() result = true -proc markRoot(gch: var GcHeap, c: PCell) = - # since we start with 'black' cells, we need to mark them here too: - if c.color != rcGrey: +proc markRoot(gch: var GcHeap, c: PCell) {.inline.} = + if c.color == 1-gch.black: c.setColor(rcGrey) add(gch.greyStack, c) + elif c.color == rcGrey: + var isGrey = false + var d = gch.decStack.d + for i in 0..gch.decStack.len-1: + if d[i] == c: + isGrey = true + break + if not isGrey: + gcAssert false, "markRoot: root is already grey?!" proc markIncremental(gch: var GcHeap): bool = var L = addr(gch.greyStack.len) @@ -637,16 +716,30 @@ proc markIncremental(gch: var GcHeap): bool = var c = gch.greyStack.d[0] if not isAllocatedPtr(gch.region, c): c_fprintf(c_stdout, "[GC] not allocated anymore: %p\n", c) + #GC_dumpHeap() + sysAssert(false, "wtf") - sysAssert(isAllocatedPtr(gch.region, c), "markIncremental: isAllocatedPtr") + #sysAssert(isAllocatedPtr(gch.region, c), "markIncremental: isAllocatedPtr") gch.greyStack.d[0] = gch.greyStack.d[L[] - 1] dec(L[]) takeTime() if c.color == rcGrey: c.setColor(gch.black) forAllChildren(c, waMarkGrey) + elif c.color == (1-gch.black): + gcAssert false, "wtf why are there white object in the greystack?" checkTime() gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty " + + # assert that all local roots are black by now: + var d = gch.decStack.d + var errors = false + for i in 0..gch.decStack.len-1: + gcAssert(isAllocatedPtr(gch.region, d[i]), "markIncremental: isAllocatedPtr 2") + if d[i].color != gch.black: + writeCell("not black ", d[i]) + errors = true + gcAssert(not errors, "wtf something wrong hre") result = true proc markGlobals(gch: var GcHeap) = @@ -658,28 +751,6 @@ proc markLocals(gch: var GcHeap) = sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals" markRoot(gch, d[i]) -when logGC: - var - cycleCheckA: array[100, PCell] - cycleCheckALen = 0 - - proc alreadySeen(c: PCell): bool = - for i in 0 .. <cycleCheckALen: - if cycleCheckA[i] == c: return true - if cycleCheckALen == len(cycleCheckA): - gcAssert(false, "cycle detection overflow") - quit 1 - cycleCheckA[cycleCheckALen] = c - inc cycleCheckALen - - proc debugGraph(s: PCell) = - if alreadySeen(s): - writeCell("child cell (already seen) ", s) - else: - writeCell("cell {", s) - forAllChildren(s, waDebug) - c_fprintf(c_stdout, "}\n") - proc doOperation(p: pointer, op: WalkOp) = if p == nil: return var c: PCell = usrToCell(p) @@ -697,17 +768,31 @@ proc doOperation(p: pointer, op: WalkOp) = decRef(c) #if c.refcount <% rcIncrement: addZCT(gch.zct, c) of waMarkGlobal: + template handleRoot = + if gch.dumpHeapFile.isNil: + markRoot(gch, c) + else: + dumpRoot(gch, c) when hasThreadSupport: # could point to a cell which we don't own and don't want to touch/trace - if isAllocatedPtr(gch.region, c): - markRoot(gch, c) + if isAllocatedPtr(gch.region, c): handleRoot() else: - markRoot(gch, c) + #gcAssert(isAllocatedPtr(gch.region, c), "doOperation: waMarkGlobal") + if not isAllocatedPtr(gch.region, c): + c_fprintf(c_stdout, "[GC] not allocated anymore: MarkGlobal %p\n", c) + #GC_dumpHeap() + sysAssert(false, "wtf") + handleRoot() + discard allocInv(gch.region) of waMarkGrey: + if not isAllocatedPtr(gch.region, c): + c_fprintf(c_stdout, "[GC] not allocated anymore: MarkGrey %p\n", c) + #GC_dumpHeap() + sysAssert(false, "wtf") if c.color == 1-gch.black: c.setColor(rcGrey) add(gch.greyStack, c) - #of waDebug: debugGraph(c) + of waDebug: debugGraph(c) proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, WalkOp(op)) @@ -717,20 +802,29 @@ proc collectZCT(gch: var GcHeap): bool {.benign.} proc collectCycles(gch: var GcHeap): bool = # ensure the ZCT 'color' is not used: while gch.zct.len > 0: discard collectZCT(gch) + case gch.phase - of Phase.None, Phase.Marking: - #if gch.phase == Phase.None: + of Phase.None: gch.phase = Phase.Marking markGlobals(gch) + + cfprintf(stdout, "collectCycles: introduced bug E %ld\n", gch.phase) + discard allocInv(gch.region) + of Phase.Marking: + # since locals do not have a write barrier, we need + # to keep re-scanning them :-( but there is really nothing we can + # do about that. markLocals(gch) if markIncremental(gch): gch.phase = Phase.Sweeping + gch.red = 1 - gch.red of Phase.Sweeping: gcAssert gch.greyStack.len == 0, "greystack not empty" if sweep(gch): gch.phase = Phase.None # flip black/white meanings: gch.black = 1 - gch.black + gcAssert gch.red == 1 - gch.black, "red color is wrong" result = true proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = @@ -770,7 +864,7 @@ proc collectZCT(gch: var GcHeap): bool = gch.zct.d[0] = gch.zct.d[L[] - 1] dec(L[]) takeTime() - if c.refcount <% rcIncrement: + if c.refcount <% rcIncrement and c.color != rcGrey: # It may have a RC > 0, if it is in the hardware stack or # it has not been removed yet from the ZCT. This is because # ``incref`` does not bother to remove the cell from the ZCT diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 2e0fecd49..1e85853d1 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -514,7 +514,7 @@ else: include "system/alloc" include "system/cellsets" - when not leakDetector: + when not leakDetector and not useCellIds: sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell") when compileOption("gc", "v2"): include "system/gc2" diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 986994203..7f18ed31c 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -76,6 +76,7 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = # we read an 'int' but this may have been too large, so mask the other bits: let e = if typ.size == 1: e and 0xff elif typ.size == 2: e and 0xffff + elif typ.size == 4: e and 0xffffffff else: e # XXX we need a proper narrowing based on signedness here #e and ((1 shl (typ.size*8)) - 1) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 3d0b2aa8a..e81219a70 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -44,9 +44,37 @@ proc memchr(s: pointer, c: cint, n: csize): pointer {. importc: "memchr", header: "<string.h>", tags: [].} proc memset(s: pointer, c: cint, n: csize) {. header: "<string.h>", importc: "memset", tags: [].} +proc fwrite(buf: pointer, size, n: int, f: File): int {. + importc: "fwrite", noDecl.} + +proc raiseEIO(msg: string) {.noinline, noreturn.} = + sysFatal(IOError, msg) {.push stackTrace:off, profiler:off.} +proc readBuffer(f: File, buffer: pointer, len: Natural): int = + result = fread(buffer, 1, len, f) + +proc readBytes(f: File, a: var openArray[int8|uint8], start, len: Natural): int = + result = readBuffer(f, addr(a[start]), len) + +proc readChars(f: File, a: var openArray[char], start, len: Natural): int = + result = readBuffer(f, addr(a[start]), len) + proc write(f: File, c: cstring) = fputs(c, f) + +proc writeBuffer(f: File, buffer: pointer, len: Natural): int = + result = fwrite(buffer, 1, len, f) + +proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int = + var x = cast[ptr array[0..1000_000_000, int8]](a) + result = writeBuffer(f, addr(x[start]), len) +proc writeChars(f: File, a: openArray[char], start, len: Natural): int = + var x = cast[ptr array[0..1000_000_000, int8]](a) + result = writeBuffer(f, addr(x[start]), len) + +proc write(f: File, s: string) = + if writeBuffer(f, cstring(s), s.len) != s.len: + raiseEIO("cannot write string to file") {.pop.} when NoFakeVars: @@ -68,9 +96,6 @@ else: const BufSize = 4000 -proc raiseEIO(msg: string) {.noinline, noreturn.} = - sysFatal(IOError, msg) - proc readLine(f: File, line: var TaintedString): bool = var pos = 0 # Use the currently reserved space for a first try @@ -157,6 +182,12 @@ proc rawFileSize(file: File): int = result = ftell(file) discard fseek(file, clong(oldPos), 0) +proc endOfFile(f: File): bool = + # do not blame me; blame the ANSI C standard this is so brain-damaged + var c = fgetc(f) + ungetc(c, f) + return c < 0'i32 + proc readAllFile(file: File, len: int): string = # We acquire the filesize beforehand and hope it doesn't change. # Speeds things up. @@ -188,26 +219,6 @@ proc readAll(file: File): TaintedString = else: result = readAllBuffer(file).TaintedString -proc readFile(filename: string): TaintedString = - var f = open(filename) - try: - result = readAll(f).TaintedString - finally: - close(f) - -proc writeFile(filename, content: string) = - var f = open(filename, fmWrite) - try: - f.write(content) - finally: - close(f) - -proc endOfFile(f: File): bool = - # do not blame me; blame the ANSI C standard this is so brain-damaged - var c = fgetc(f) - ungetc(c, f) - return c < 0'i32 - proc writeLn[Ty](f: File, x: varargs[Ty, `$`]) = for i in items(x): write(f, i) @@ -278,39 +289,12 @@ proc reopen(f: File, filename: string, mode: FileMode = fmRead): bool = result = p != nil proc fdopen(filehandle: FileHandle, mode: cstring): File {. - importc: pccHack & "fdopen", header: "<stdio.h>".} + importc: "fdopen", header: "<stdio.h>".} proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool = f = fdopen(filehandle, FormatOpen[mode]) result = f != nil -proc fwrite(buf: pointer, size, n: int, f: File): int {. - importc: "fwrite", noDecl.} - -proc readBuffer(f: File, buffer: pointer, len: Natural): int = - result = fread(buffer, 1, len, f) - -proc readBytes(f: File, a: var openArray[int8|uint8], start, len: Natural): int = - result = readBuffer(f, addr(a[start]), len) - -proc readChars(f: File, a: var openArray[char], start, len: Natural): int = - result = readBuffer(f, addr(a[start]), len) - -{.push stackTrace:off, profiler:off.} -proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int = - var x = cast[ptr array[0..1000_000_000, int8]](a) - result = writeBuffer(f, addr(x[start]), len) -proc writeChars(f: File, a: openArray[char], start, len: Natural): int = - var x = cast[ptr array[0..1000_000_000, int8]](a) - result = writeBuffer(f, addr(x[start]), len) -proc writeBuffer(f: File, buffer: pointer, len: Natural): int = - result = fwrite(buffer, 1, len, f) - -proc write(f: File, s: string) = - if writeBuffer(f, cstring(s), s.len) != s.len: - raiseEIO("cannot write string to file") -{.pop.} - proc setFilePos(f: File, pos: int64) = if fseek(f, clong(pos), 0) != 0: raiseEIO("cannot set file position") @@ -325,4 +309,28 @@ proc getFileSize(f: File): int64 = result = getFilePos(f) setFilePos(f, oldPos) +when not declared(close): + proc close(f: File) {. + importc: "fclose", header: "<stdio.h>", tags: [].} + +proc readFile(filename: string): TaintedString = + var f: File + if open(f, filename): + try: + result = readAll(f).TaintedString + finally: + close(f) + else: + sysFatal(IOError, "cannot open: ", filename) + +proc writeFile(filename, content: string) = + var f: File + if open(f, filename, fmWrite): + try: + f.write(content) + finally: + close(f) + else: + sysFatal(IOError, "cannot open: ", filename) + {.pop.} diff --git a/tools/heapdump2dot.nim b/tools/heapdump2dot.nim new file mode 100644 index 000000000..4cee6d674 --- /dev/null +++ b/tools/heapdump2dot.nim @@ -0,0 +1,66 @@ + +include prelude + +proc main(input, output: string) = + type NodeKind = enum + local, localInvalid, global, globalInvalid + #c_fprintf(file, "%s %p %d rc=%ld color=%c\n", + # msg, c, kind, c.refcount shr rcShift, col) + # cell 0x10a908190 22 rc=2 color=w + var i, o: File + var roots = initTable[string, NodeKind]() + if open(i, input): + if open(o, output, fmWrite): + o.writeLine("digraph $1 {\n" % extractFilename(input)) + var currNode = "" + for line in lines(i): + let data = line.split() + if data.len == 0: continue + case data[0] + of "end": + currNode = "" + of "cell": + currNode = data[1] + let rc = data[3].substr("rc=".len) + let col = case data[4].substr("color=".len) + of "b": "black" + of "w": "green" + of "g": "grey" + else: "" + o.write("N" & currNode) + if currNode in roots: + let v = roots[currNode] + case v + of local: o.write(" [label=\"local \\N\" fillcolor=$1]" % col) + of localInvalid: o.write(" [label=\"local invalid \\N\" fillcolor=$1]" % col) + of global: o.write(" [label=\"global \\N\" fillcolor=$1]" % col) + of globalInvalid: o.write(" [label=\"global invalid \\N\" fillcolor=$1]" % col) + else: + o.write(" [fillcolor=$1]" % col) + o.writeLine(";") + of "child": + assert currNode.len > 0 + o.writeLine("N$1 -> N$2;" % [currNode, data[1]]) + of "global_root": + roots[data[1]] = global + of "global_root_invalid": + roots[data[1]] = globalInvalid + of "onstack": + roots[data[1]] = local + of "onstack_invalid": + roots[data[1]] = localInvalid + else: discard + close(i) + o.writeLine("\n}") + close(o) + else: + quit "error: cannot open " & output + else: + quit "error: cannot open " & input + +if paramCount() == 1: + main(paramStr(1), changeFileExt(paramStr(1), "dot")) +elif paramCount() == 2: + main(paramStr(1), paramStr(2)) +else: + quit "usage: heapdump2dot inputfile outputfile" diff --git a/web/news.txt b/web/news.txt index bf665c85c..d854347a5 100644 --- a/web/news.txt +++ b/web/news.txt @@ -17,6 +17,13 @@ Library Additions - The rlocks module has been added providing reentrant lock synchronization primitive +Compiler Additions +------------------ + +- Added a new ``--noCppExceptions`` switch that allows to use default exception + handling (no ``throw`` or ``try``/``catch`` generated) when compiling to C++ + code + 2016-01-27 Nim in Action is now available! ========================================== |