Merge branch 'devel' into sighashes

This commit is contained in:
Araq 2016-11-28 20:59:30 +01:00
commit 27723af469
75 changed files with 1258 additions and 361 deletions

2
.gitignore vendored
View file

@ -21,6 +21,7 @@ tags
install.sh install.sh
deinstall.sh deinstall.sh
doc/html/
doc/*.html doc/*.html
doc/*.pdf doc/*.pdf
doc/*.idx doc/*.idx
@ -47,6 +48,7 @@ xcuserdata/
/testresults.json /testresults.json
testament.db testament.db
/csources /csources
dist/
# Private directories and files (IDEs) # Private directories and files (IDEs)
.*/ .*/

View file

@ -22,10 +22,9 @@ script:
- nim c koch - nim c koch
- ./koch boot - ./koch boot
- ./koch boot -d:release - ./koch boot -d:release
- nim e install_nimble.nims - ./koch nimble
- nim e tests/test_nimscript.nims - nim e tests/test_nimscript.nims
- nimble update - nimble install zip -y
- nimble install zip
- nimble install opengl - nimble install opengl
- nimble install sdl1 - nimble install sdl1
- nimble install jester@#head - nimble install jester@#head

View file

@ -354,6 +354,14 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
else: internalError("genAssignment: " & $ty.kind) else: internalError("genAssignment: " & $ty.kind)
if optMemTracker in p.options and dest.s in {OnHeap, OnUnknown}:
#writeStackTrace()
#echo p.currLineInfo, " requesting"
linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
addrLoc(dest), rope getSize(dest.t),
makeCString(p.currLineInfo.toFullPath),
rope p.currLineInfo.safeLineNm)
proc genDeepCopy(p: BProc; dest, src: TLoc) = proc genDeepCopy(p: BProc; dest, src: TLoc) =
var ty = skipTypes(dest.t, abstractVarRange) var ty = skipTypes(dest.t, abstractVarRange)
case ty.kind case ty.kind
@ -1940,6 +1948,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
d.s = OnStatic d.s = OnStatic
proc expr(p: BProc, n: PNode, d: var TLoc) = proc expr(p: BProc, n: PNode, d: var TLoc) =
p.currLineInfo = n.info
case n.kind case n.kind
of nkSym: of nkSym:
var sym = n.sym var sym = n.sym

View file

@ -221,6 +221,10 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
# linear search is not necessary anymore: # linear search is not necessary anymore:
result = tab.getOrDefault(sig) result = tab.getOrDefault(sig)
proc addAbiCheck(m: BModule, t: PType, name: Rope) =
if isDefined("checkabi"):
addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))])
proc getTempName(m: BModule): Rope = proc getTempName(m: BModule): Rope =
result = m.tmpBase & rope(m.labels) result = m.tmpBase & rope(m.labels)
inc m.labels inc m.labels
@ -282,6 +286,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
result = getSimpleTypeDesc(m, lastSon typ) result = getSimpleTypeDesc(m, lastSon typ)
else: result = nil else: result = nil
if result != nil and typ.isImportedType():
if cacheGetType(m.typeCache, typ) == nil:
idTablePut(m.typeCache, typ, result)
addAbiCheck(m, typ, result)
proc pushType(m: BModule, typ: PType) = proc pushType(m: BModule, typ: PType) =
add(m.typeStack, typ) add(m.typeStack, typ)
@ -678,6 +687,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
let foo = getTypeDescAux(m, t.sons[1], check) let foo = getTypeDescAux(m, t.sons[1], check)
addf(m.s[cfsTypes], "typedef $1 $2[$3];$n", addf(m.s[cfsTypes], "typedef $1 $2[$3];$n",
[foo, result, rope(n)]) [foo, result, rope(n)])
else: addAbiCheck(m, t, result)
of tyObject, tyTuple: of tyObject, tyTuple:
if isImportedCppType(t) and origTyp.kind == tyGenericInst: if isImportedCppType(t) and origTyp.kind == tyGenericInst:
# for instantiated templates we do not go through the type cache as the # for instantiated templates we do not go through the type cache as the
@ -739,7 +749,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
m.typeCache[sig] = result # always call for sideeffects: m.typeCache[sig] = result # always call for sideeffects:
let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check) let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check)
else: getTupleDesc(m, t, result, check) else: getTupleDesc(m, t, result, check)
if not isImportedType(t): add(m.s[cfsTypes], recdesc) if not isImportedType(t):
add(m.s[cfsTypes], recdesc)
elif tfIncompleteStruct notin t.flags: addAbiCheck(m, t, result)
of tySet: of tySet:
result = getTypeName(m, t.lastSon, hashType t.lastSon) & "_Set" result = getTypeName(m, t.lastSon, hashType t.lastSon) & "_Set"
m.typeCache[sig] = result m.typeCache[sig] = result
@ -841,6 +853,8 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: Rope) =
#else MessageOut("can contain a cycle: " & typeToString(typ)) #else MessageOut("can contain a cycle: " & typeToString(typ))
if flags != 0: if flags != 0:
addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)]) addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
if isDefined("nimTypeNames"):
addf(m.s[cfsTypeInit3], "$1.name = $2;$n", [name, makeCstring typeToString origType])
discard cgsym(m, "TNimType") discard cgsym(m, "TNimType")
addf(m.s[cfsVars], "TNimType $1; /* $2 */$n", addf(m.s[cfsVars], "TNimType $1; /* $2 */$n",
[name, rope(typeToString(typ))]) [name, rope(typeToString(typ))])

View file

@ -69,6 +69,7 @@ type
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
threadVarAccessed*: bool # true if the proc already accessed some threadvar threadVarAccessed*: bool # true if the proc already accessed some threadvar
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
currLineInfo*: TLineInfo # AST codegen will make this superfluous
nestedTryStmts*: seq[PNode] # in how many nested try statements we are nestedTryStmts*: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then) # (the vars must be volatile then)
inExceptBlock*: int # are we currently inside an except block? inExceptBlock*: int # are we currently inside an except block?

View file

@ -242,6 +242,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "linetrace": result = contains(gOptions, optLineTrace) of "linetrace": result = contains(gOptions, optLineTrace)
of "debugger": result = contains(gOptions, optEndb) of "debugger": result = contains(gOptions, optEndb)
of "profiler": result = contains(gOptions, optProfiler) of "profiler": result = contains(gOptions, optProfiler)
of "memtracker": result = contains(gOptions, optMemTracker)
of "checks", "x": result = gOptions * ChecksOptions == ChecksOptions of "checks", "x": result = gOptions * ChecksOptions == ChecksOptions
of "floatchecks": of "floatchecks":
result = gOptions * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck} result = gOptions * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
@ -264,6 +265,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "implicitstatic": result = contains(gOptions, optImplicitStatic) of "implicitstatic": result = contains(gOptions, optImplicitStatic)
of "patterns": result = contains(gOptions, optPatterns) of "patterns": result = contains(gOptions, optPatterns)
of "experimental": result = gExperimentalMode of "experimental": result = gExperimentalMode
of "excessivestacktrace": result = contains(gGlobalOptions, optExcessiveStackTrace)
else: invalidCmdLineOption(passCmd1, switch, info) else: invalidCmdLineOption(passCmd1, switch, info)
proc processPath(path: string, info: TLineInfo, proc processPath(path: string, info: TLineInfo,
@ -445,6 +447,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
processOnOffSwitch({optProfiler}, arg, pass, info) processOnOffSwitch({optProfiler}, arg, pass, info)
if optProfiler in gOptions: defineSymbol("profiler") if optProfiler in gOptions: defineSymbol("profiler")
else: undefSymbol("profiler") else: undefSymbol("profiler")
of "memtracker":
processOnOffSwitch({optMemTracker}, arg, pass, info)
if optMemTracker in gOptions: defineSymbol("memtracker")
else: undefSymbol("memtracker")
of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info) of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info)
of "floatchecks": of "floatchecks":
processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info) processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)

View file

@ -100,7 +100,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerQuotedIdent(n) let ident = lookups.considerQuotedIdent(n)
let s = strTableGet(fromMod.tab, ident) let s = strTableGet(fromMod.tab, ident)
if s == nil: if s == nil:
localError(n.info, errUndeclaredIdentifier, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)
else: else:
if s.kind == skStub: loadStub(s) if s.kind == skStub: loadStub(s)
if s.kind notin ExportableSymKinds: if s.kind notin ExportableSymKinds:
@ -162,12 +162,26 @@ proc importModuleAs(n: PNode, realModule: PSym): PSym =
proc myImportModule(c: PContext, n: PNode): PSym = proc myImportModule(c: PContext, n: PNode): PSym =
var f = checkModuleName(n) var f = checkModuleName(n)
if f != InvalidFileIDX: if f != InvalidFileIDX:
let L = c.graph.importStack.len
let recursion = c.graph.importStack.find(f)
c.graph.importStack.add f
#echo "adding ", toFullPath(f), " at ", L+1
if recursion >= 0:
var err = ""
for i in countup(recursion, L-1):
if i > recursion: err.add "\n"
err.add toFullPath(c.graph.importStack[i]) & " imports " &
toFullPath(c.graph.importStack[i+1])
c.recursiveDep = err
result = importModuleAs(n, gImportModule(c.graph, c.module, f, c.cache)) result = importModuleAs(n, gImportModule(c.graph, c.module, f, c.cache))
#echo "set back to ", L
c.graph.importStack.setLen(L)
# we cannot perform this check reliably because of # we cannot perform this check reliably because of
# test: modules/import_in_config) # test: modules/import_in_config)
if result.info.fileIndex == c.module.info.fileIndex and when true:
result.info.fileIndex == n.info.fileIndex: if result.info.fileIndex == c.module.info.fileIndex and
localError(n.info, errGenerated, "A module cannot import itself") result.info.fileIndex == n.info.fileIndex:
localError(n.info, errGenerated, "A module cannot import itself")
if sfDeprecated in result.flags: if sfDeprecated in result.flags:
message(n.info, warnDeprecated, result.name.s) message(n.info, warnDeprecated, result.name.s)
#suggestSym(n.info, result, false) #suggestSym(n.info, result, false)

View file

@ -242,6 +242,15 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
inc i inc i
localError(info, errGenerated, err) localError(info, errGenerated, err)
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
var err = "undeclared identifier: '" & name & "'"
if c.recursiveDep.len > 0:
err.add "\nThis might be caused by a recursive module dependency: "
err.add c.recursiveDep
# prevent excessive errors for 'nim check'
c.recursiveDep = nil
localError(info, errGenerated, err)
proc lookUp*(c: PContext, n: PNode): PSym = proc lookUp*(c: PContext, n: PNode): PSym =
# Looks up a symbol. Generates an error in case of nil. # Looks up a symbol. Generates an error in case of nil.
case n.kind case n.kind
@ -249,7 +258,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
result = searchInScopes(c, n.ident).skipAlias(n) result = searchInScopes(c, n.ident).skipAlias(n)
if result == nil: if result == nil:
fixSpelling(n, n.ident, searchInScopes) fixSpelling(n, n.ident, searchInScopes)
localError(n.info, errUndeclaredIdentifier, n.ident.s) errorUndeclaredIdentifier(c, n.info, n.ident.s)
result = errorSym(c, n) result = errorSym(c, n)
of nkSym: of nkSym:
result = n.sym result = n.sym
@ -258,7 +267,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
result = searchInScopes(c, ident).skipAlias(n) result = searchInScopes(c, ident).skipAlias(n)
if result == nil: if result == nil:
fixSpelling(n, ident, searchInScopes) fixSpelling(n, ident, searchInScopes)
localError(n.info, errUndeclaredIdentifier, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)
result = errorSym(c, n) result = errorSym(c, n)
else: else:
internalError(n.info, "lookUp") internalError(n.info, "lookUp")
@ -282,7 +291,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
result = searchInScopes(c, ident, allExceptModule).skipAlias(n) result = searchInScopes(c, ident, allExceptModule).skipAlias(n)
if result == nil and checkUndeclared in flags: if result == nil and checkUndeclared in flags:
fixSpelling(n, ident, searchInScopes) fixSpelling(n, ident, searchInScopes)
localError(n.info, errUndeclaredIdentifier, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)
result = errorSym(c, n) result = errorSym(c, n)
elif checkAmbiguity in flags and result != nil and elif checkAmbiguity in flags and result != nil and
contains(c.ambiguousSymbols, result.id): contains(c.ambiguousSymbols, result.id):
@ -307,7 +316,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
result = strTableGet(m.tab, ident).skipAlias(n) result = strTableGet(m.tab, ident).skipAlias(n)
if result == nil and checkUndeclared in flags: if result == nil and checkUndeclared in flags:
fixSpelling(n.sons[1], ident, searchInScopes) fixSpelling(n.sons[1], ident, searchInScopes)
localError(n.sons[1].info, errUndeclaredIdentifier, ident.s) errorUndeclaredIdentifier(c, n.sons[1].info, ident.s)
result = errorSym(c, n.sons[1]) result = errorSym(c, n.sons[1])
elif n.sons[1].kind == nkSym: elif n.sons[1].kind == nkSym:
result = n.sons[1].sym result = n.sons[1].sym

View file

@ -36,6 +36,8 @@ type
invalidTransitiveClosure: bool invalidTransitiveClosure: bool
inclToMod*: Table[int32, int32] # mapping of include file to the inclToMod*: Table[int32, int32] # mapping of include file to the
# first module that included it # first module that included it
importStack*: seq[int32] # The current import stack. Used for detecting recursive
# module dependencies.
{.this: g.} {.this: g.}
@ -44,12 +46,14 @@ proc newModuleGraph*(): ModuleGraph =
initStrTable(result.packageSyms) initStrTable(result.packageSyms)
result.deps = initIntSet() result.deps = initIntSet()
result.modules = @[] result.modules = @[]
result.importStack = @[]
result.inclToMod = initTable[int32, int32]() result.inclToMod = initTable[int32, int32]()
proc resetAllModules*(g: ModuleGraph) = proc resetAllModules*(g: ModuleGraph) =
initStrTable(packageSyms) initStrTable(packageSyms)
deps = initIntSet() deps = initIntSet()
modules = @[] modules = @[]
importStack = @[]
inclToMod = initTable[int32, int32]() inclToMod = initTable[int32, int32]()
proc getModule*(g: ModuleGraph; fileIdx: int32): PSym = proc getModule*(g: ModuleGraph; fileIdx: int32): PSym =

View file

@ -231,6 +231,7 @@ proc compileProject*(graph: ModuleGraph; cache: IdentCache;
wantMainModule() wantMainModule()
let systemFileIdx = fileInfoIdx(options.libpath / "system.nim") let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
graph.importStack.add projectFile
if projectFile == systemFileIdx: if projectFile == systemFileIdx:
discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule}) discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
else: else:

View file

@ -35,7 +35,7 @@ type
errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
errExprExpected, errUndeclaredIdentifier, errUndeclaredField, errExprExpected, errUndeclaredField,
errUndeclaredRoutine, errUseQualifier, errUndeclaredRoutine, errUseQualifier,
errTypeExpected, errTypeExpected,
errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
@ -197,7 +197,6 @@ const
errInvalidMultipleAsgn: "multiple assignment is not allowed", errInvalidMultipleAsgn: "multiple assignment is not allowed",
errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'", errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'",
errExprExpected: "expression expected, but found \'$1\'", errExprExpected: "expression expected, but found \'$1\'",
errUndeclaredIdentifier: "undeclared identifier: \'$1\'",
errUndeclaredField: "undeclared field: \'$1\'", errUndeclaredField: "undeclared field: \'$1\'",
errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'", errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'",
errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier", errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier",
@ -676,9 +675,8 @@ proc getInfoContext*(index: int): TLineInfo =
if i >=% L: result = unknownLineInfo() if i >=% L: result = unknownLineInfo()
else: result = msgContext[i] else: result = msgContext[i]
proc toFilename*(fileIdx: int32): string = template toFilename*(fileIdx: int32): string =
if fileIdx < 0: result = "???" (if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath)
else: result = fileInfos[fileIdx].projPath
proc toFullPath*(fileIdx: int32): string = proc toFullPath*(fileIdx: int32): string =
if fileIdx < 0: result = "???" if fileIdx < 0: result = "???"

View file

@ -46,7 +46,7 @@ proc handleCmdLine(cache: IdentCache) =
if gProjectName == "-": if gProjectName == "-":
gProjectName = "stdinfile" gProjectName = "stdinfile"
gProjectFull = "stdinfile" gProjectFull = "stdinfile"
gProjectPath = getCurrentDir() gProjectPath = canonicalizePath getCurrentDir()
gProjectIsStdin = true gProjectIsStdin = true
elif gProjectName != "": elif gProjectName != "":
try: try:
@ -54,10 +54,10 @@ proc handleCmdLine(cache: IdentCache) =
except OSError: except OSError:
gProjectFull = gProjectName gProjectFull = gProjectName
let p = splitFile(gProjectFull) let p = splitFile(gProjectFull)
gProjectPath = p.dir gProjectPath = canonicalizePath p.dir
gProjectName = p.name gProjectName = p.name
else: else:
gProjectPath = getCurrentDir() gProjectPath = canonicalizePath getCurrentDir()
loadConfigs(DefaultConfig) # load all config files loadConfigs(DefaultConfig) # load all config files
let scriptFile = gProjectFull.changeFileExt("nims") let scriptFile = gProjectFull.changeFileExt("nims")
if fileExists(scriptFile): if fileExists(scriptFile):

View file

@ -8,10 +8,9 @@
# #
## exposes the Nim VM to clients. ## exposes the Nim VM to clients.
import import
ast, modules, passes, passaux, condsyms, ast, modules, passes, passaux, condsyms,
options, nimconf, lists, sem, semdata, llstream, vm options, nimconf, lists, sem, semdata, llstream, vm, modulegraphs, idents
proc execute*(program: string) = proc execute*(program: string) =
passes.gIncludeFile = includeModule passes.gIncludeFile = includeModule
@ -27,7 +26,9 @@ proc execute*(program: string) =
registerPass(evalPass) registerPass(evalPass)
appendStr(searchPaths, options.libpath) appendStr(searchPaths, options.libpath)
compileSystemModule() var graph = newModuleGraph()
var m = makeStdinModule() var cache = newIdentCache()
var m = makeStdinModule(graph)
incl(m.flags, sfMainModule) incl(m.flags, sfMainModule)
processModule(m, llStreamOpen(program), nil) compileSystemModule(graph,cache)
processModule(graph,m, llStreamOpen(program), nil, cache)

View file

@ -34,7 +34,8 @@ type # please make sure we have under 32 options
optProfiler, # profiler turned on optProfiler, # profiler turned on
optImplicitStatic, # optimization: implicit at compile time optImplicitStatic, # optimization: implicit at compile time
# evaluation # evaluation
optPatterns # en/disable pattern matching optPatterns, # en/disable pattern matching
optMemTracker
TOptions* = set[TOption] TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible** TGlobalOption* = enum # **keep binary compatible**
@ -231,10 +232,10 @@ proc canonicalizePath*(path: string): string =
proc shortenDir*(dir: string): string = proc shortenDir*(dir: string): string =
## returns the interesting part of a dir ## returns the interesting part of a dir
var prefix = getPrefixDir() & DirSep var prefix = gProjectPath & DirSep
if startsWith(dir, prefix): if startsWith(dir, prefix):
return substr(dir, len(prefix)) return substr(dir, len(prefix))
prefix = gProjectPath & DirSep prefix = getPrefixDir() & DirSep
if startsWith(dir, prefix): if startsWith(dir, prefix):
return substr(dir, len(prefix)) return substr(dir, len(prefix))
result = dir result = dir

View file

@ -149,14 +149,25 @@ proc closePassesCached(a: var TPassContextArray) =
m = gPasses[i].close(a[i], m) m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here a[i] = nil # free the memory here
proc resolveMod(module, relativeTo: string): int32 =
let fullPath = findModule(module, relativeTo)
if fullPath.len == 0:
result = InvalidFileIDX
else:
result = fullPath.fileInfoIdx
proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
a: var TPassContextArray) = a: var TPassContextArray; m: PSym) =
# XXX fixme this should actually be relative to the config file!
let relativeTo = m.info.toFullPath
for module in items(implicits): for module in items(implicits):
var importStmt = newNodeI(nodeKind, gCmdLineInfo) # implicit imports should not lead to a module importing itself
var str = newStrNode(nkStrLit, module) if m.position != resolveMod(module, relativeTo):
str.info = gCmdLineInfo var importStmt = newNodeI(nodeKind, gCmdLineInfo)
importStmt.addSon str var str = newStrNode(nkStrLit, module)
if not processTopLevelStmt(importStmt, a): break str.info = gCmdLineInfo
importStmt.addSon str
if not processTopLevelStmt(importStmt, a): break
proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
rd: PRodReader; cache: IdentCache): bool {.discardable.} = rd: PRodReader; cache: IdentCache): bool {.discardable.} =
@ -183,8 +194,8 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
# modules to include between compilation runs? we'd need to track that # modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only # in ROD files. I think we should enable this feature only
# for the interactive mode. # for the interactive mode.
processImplicits implicitImports, nkImportStmt, a processImplicits implicitImports, nkImportStmt, a, module
processImplicits implicitIncludes, nkIncludeStmt, a processImplicits implicitIncludes, nkIncludeStmt, a, module
while true: while true:
var n = parseTopLevelStmt(p) var n = parseTopLevelStmt(p)

View file

@ -323,7 +323,8 @@ proc processOption(c: PContext, n: PNode): bool =
of wStacktrace: onOff(c, n, {optStackTrace}) of wStacktrace: onOff(c, n, {optStackTrace})
of wLinetrace: onOff(c, n, {optLineTrace}) of wLinetrace: onOff(c, n, {optLineTrace})
of wDebugger: onOff(c, n, {optEndb}) of wDebugger: onOff(c, n, {optEndb})
of wProfiler: onOff(c, n, {optProfiler}) of wProfiler: onOff(c, n, {optProfiler, optMemTracker})
of wMemTracker: onOff(c, n, {optMemTracker})
of wByRef: onOff(c, n, {optByRef}) of wByRef: onOff(c, n, {optByRef})
of wDynlib: processDynLib(c, n, nil) of wDynlib: processDynLib(c, n, nil)
of wOptimization: of wOptimization:

View file

@ -110,6 +110,7 @@ type
cache*: IdentCache cache*: IdentCache
graph*: ModuleGraph graph*: ModuleGraph
signatures*: TStrTable signatures*: TStrTable
recursiveDep*: string
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
result.genericSym = s result.genericSym = s

View file

@ -1553,7 +1553,7 @@ proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
if isCallExpr(n): if isCallExpr(n):
var expandedSym = qualifiedLookUp(c, n[0], {checkUndeclared}) var expandedSym = qualifiedLookUp(c, n[0], {checkUndeclared})
if expandedSym == nil: if expandedSym == nil:
localError(n.info, errUndeclaredIdentifier, n[0].renderTree) errorUndeclaredIdentifier(c, n.info, n[0].renderTree)
return errorSym(c, n[0]) return errorSym(c, n[0])
if expandedSym.kind notin {skMacro, skTemplate}: if expandedSym.kind notin {skMacro, skTemplate}:

View file

@ -107,7 +107,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
var s = searchInScopes(c, ident).skipAlias(n) var s = searchInScopes(c, ident).skipAlias(n)
if s == nil: if s == nil:
if ident.id notin ctx.toMixin and withinMixin notin flags: if ident.id notin ctx.toMixin and withinMixin notin flags:
localError(n.info, errUndeclaredIdentifier, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)
else: else:
if withinBind in flags: if withinBind in flags:
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
@ -195,7 +195,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if s == nil and withinMixin notin flags and if s == nil and withinMixin notin flags and
fn.kind in {nkIdent, nkAccQuoted} and fn.kind in {nkIdent, nkAccQuoted} and
considerQuotedIdent(fn).id notin ctx.toMixin: considerQuotedIdent(fn).id notin ctx.toMixin:
localError(n.info, errUndeclaredIdentifier, fn.renderTree) errorUndeclaredIdentifier(c, n.info, fn.renderTree)
var first = 0 var first = 0
var mixinContext = false var mixinContext = false

View file

@ -143,7 +143,7 @@ proc semBindSym(c: PContext, n: PNode): PNode =
var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal)) var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal))
result.add(sc) result.add(sc)
else: else:
localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal) errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode

View file

@ -34,7 +34,7 @@ type
wColon, wColonColon, wEquals, wDot, wDotDot, wColon, wColonColon, wEquals, wDot, wDotDot,
wStar, wMinus, wStar, wMinus,
wMagic, wThread, wFinal, wProfiler, wObjChecks, wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks,
wIntDefine, wStrDefine, wIntDefine, wStrDefine,
wDestroy, wDestroy,
@ -121,7 +121,7 @@ const
":", "::", "=", ".", "..", ":", "::", "=", ".", "..",
"*", "-", "*", "-",
"magic", "thread", "final", "profiler", "objchecks", "intdefine", "strdefine", "magic", "thread", "final", "profiler", "memtracker", "objchecks", "intdefine", "strdefine",
"destroy", "destroy",

View file

@ -61,6 +61,9 @@ Advanced options:
--taintMode:on|off turn taint mode on|off --taintMode:on|off turn taint mode on|off
--implicitStatic:on|off turn implicit compile time evaluation on|off --implicitStatic:on|off turn implicit compile time evaluation on|off
--patterns:on|off turn pattern matching on|off --patterns:on|off turn pattern matching on|off
--memTracker:on|off turn memory tracker on|off
--excessiveStackTrace:on|off
stack traces use full file paths
--skipCfg do not read the general configuration file --skipCfg do not read the general configuration file
--skipUserCfg do not read the user's configuration file --skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files --skipParentCfg do not read the parent dirs' configuration files

View file

@ -77,8 +77,9 @@ Collections and algorithms
* `lists <lists.html>`_ * `lists <lists.html>`_
Nim linked list support. Contains singly and doubly linked lists and Nim linked list support. Contains singly and doubly linked lists and
circular lists ("rings"). circular lists ("rings").
* `queues <queues.html>`_ * `deques <deques.html>`_
Implementation of a queue. The underlying implementation uses a ``seq``. Implementation of a double-ended queue.
The underlying implementation uses a ``seq``.
* `intsets <intsets.html>`_ * `intsets <intsets.html>`_
Efficient implementation of a set of ints as a sparse bit set. Efficient implementation of a set of ints as a sparse bit set.
* `critbits <critbits.html>`_ * `critbits <critbits.html>`_

View file

@ -258,6 +258,10 @@ Define Effect
``ssl`` Enables OpenSSL support for the sockets module. ``ssl`` Enables OpenSSL support for the sockets module.
``memProfiler`` Enables memory profiling for the native GC. ``memProfiler`` Enables memory profiling for the native GC.
``uClibc`` Use uClibc instead of libc. (Relevant for Unix-like OSes) ``uClibc`` Use uClibc instead of libc. (Relevant for Unix-like OSes)
``checkAbi`` When using types from C headers, add checks that compare
what's in the Nim file with what's in the C header
(requires a C compiler with _Static_assert support, like
any C11 compiler)
================== ========================================================= ================== =========================================================

View file

@ -75,22 +75,8 @@ done:
Nimble integration Nimble integration
================== ==================
A ``project.nims`` file can also be used as an alternative to See the `Nimble readme <https://github.com/nim-lang/nimble#readme>`_
a ``project.nimble`` file to specify the meta information (for example, author, for more information.
description) and dependencies of a Nimble package. This means you can easily
have platform specific dependencies:
.. code-block:: nim
version = "1.0"
author = "The green goo."
description = "Lexer generation and regex implementation for Nim."
license = "MIT"
when defined(windows):
requires "oldwinapi >= 1.0"
else:
requires "gtk2 >= 1.0"

View file

@ -459,3 +459,7 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
# include <sys/types.h> # include <sys/types.h>
#endif #endif
/* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */
#define NIM_CHECK_SIZE(typ, sz) \
_Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")

View file

@ -11,7 +11,7 @@ include "system/inclrtl"
import os, oids, tables, strutils, times, heapqueue import os, oids, tables, strutils, times, heapqueue
import nativesockets, net, queues import nativesockets, net, deques
export Port, SocketFlag export Port, SocketFlag
@ -164,7 +164,7 @@ include includes/asyncfutures
type type
PDispatcherBase = ref object of RootRef PDispatcherBase = ref object of RootRef
timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]] timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
callbacks: Queue[proc ()] callbacks: Deque[proc ()]
proc processTimers(p: PDispatcherBase) {.inline.} = proc processTimers(p: PDispatcherBase) {.inline.} =
while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt: while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt:
@ -172,7 +172,7 @@ proc processTimers(p: PDispatcherBase) {.inline.} =
proc processPendingCallbacks(p: PDispatcherBase) = proc processPendingCallbacks(p: PDispatcherBase) =
while p.callbacks.len > 0: while p.callbacks.len > 0:
var cb = p.callbacks.dequeue() var cb = p.callbacks.popFirst()
cb() cb()
proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} = proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
@ -230,7 +230,7 @@ when defined(windows) or defined(nimdoc):
result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
result.handles = initSet[AsyncFD]() result.handles = initSet[AsyncFD]()
result.timers.newHeapQueue() result.timers.newHeapQueue()
result.callbacks = initQueue[proc ()](64) result.callbacks = initDeque[proc ()](64)
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
proc getGlobalDispatcher*(): PDispatcher = proc getGlobalDispatcher*(): PDispatcher =
@ -987,7 +987,7 @@ else:
new result new result
result.selector = newSelector() result.selector = newSelector()
result.timers.newHeapQueue() result.timers.newHeapQueue()
result.callbacks = initQueue[proc ()](64) result.callbacks = initDeque[proc ()](64)
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
proc getGlobalDispatcher*(): PDispatcher = proc getGlobalDispatcher*(): PDispatcher =
@ -1043,6 +1043,26 @@ else:
p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb) p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb)
update(fd, p.selector[fd.SocketHandle].events + {EvWrite}) update(fd, p.selector[fd.SocketHandle].events + {EvWrite})
template processCallbacks(callbacks: expr) =
# Callback may add items to ``callbacks`` which causes issues if
# we are iterating over it at the same time. We therefore
# make a copy to iterate over.
let currentCBs = callbacks
callbacks = @[]
# Using another sequence because callbacks themselves can add
# other callbacks.
var newCBs: seq[Callback] = @[]
for cb in currentCBs:
if newCBs.len > 0:
# A callback has already returned with EAGAIN, don't call any
# others until next `poll`.
newCBs.add(cb)
else:
if not cb(data.fd):
# Callback wants to be called again.
newCBs.add(cb)
callbacks = newCBs & callbacks
proc poll*(timeout = 500) = proc poll*(timeout = 500) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
@ -1056,23 +1076,10 @@ else:
# `recv(...)` routines. # `recv(...)` routines.
if EvRead in info.events or info.events == {EvError}: if EvRead in info.events or info.events == {EvError}:
# Callback may add items to ``data.readCBs`` which causes issues if processCallbacks(data.readCBs)
# we are iterating over ``data.readCBs`` at the same time. We therefore
# make a copy to iterate over.
let currentCBs = data.readCBs
data.readCBs = @[]
for cb in currentCBs:
if not cb(data.fd):
# Callback wants to be called again.
data.readCBs.add(cb)
if EvWrite in info.events or info.events == {EvError}: if EvWrite in info.events or info.events == {EvError}:
let currentCBs = data.writeCBs processCallbacks(data.writeCBs)
data.writeCBs = @[]
for cb in currentCBs:
if not cb(data.fd):
# Callback wants to be called again.
data.writeCBs.add(cb)
if info.key in p.selector: if info.key in p.selector:
var newEvents: set[Event] var newEvents: set[Event]
@ -1410,7 +1417,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
proc callSoon*(cbproc: proc ()) = proc callSoon*(cbproc: proc ()) =
## Schedule `cbproc` to be called as soon as possible. ## Schedule `cbproc` to be called as soon as possible.
## The callback is called when control returns to the event loop. ## The callback is called when control returns to the event loop.
getGlobalDispatcher().callbacks.enqueue(cbproc) getGlobalDispatcher().callbacks.addLast(cbproc)
proc runForever*() = proc runForever*() =
## Begins a never ending global dispatcher poll loop. ## Begins a never ending global dispatcher poll loop.

View file

@ -118,8 +118,8 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
## Read ``size`` bytes from the specified file asynchronously starting at ## Read ``size`` bytes from the specified file asynchronously starting at
## the current position of the file pointer. ## the current position of the file pointer.
## ##
## If the file pointer is past the end of the file then an empty string is ## If the file pointer is past the end of the file then zero is returned
## returned. ## and no bytes are read into ``buf``
var retFuture = newFuture[int]("asyncfile.readBuffer") var retFuture = newFuture[int]("asyncfile.readBuffer")
when defined(windows) or defined(nimdoc): when defined(windows) or defined(nimdoc):
@ -149,7 +149,11 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
let err = osLastError() let err = osLastError()
if err.int32 != ERROR_IO_PENDING: if err.int32 != ERROR_IO_PENDING:
GC_unref(ol) GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(err))) if err.int32 == ERROR_HANDLE_EOF:
# This happens in Windows Server 2003
retFuture.complete(0)
else:
retFuture.fail(newException(OSError, osErrorMsg(err)))
else: else:
# Request completed immediately. # Request completed immediately.
var bytesRead: DWord var bytesRead: DWord
@ -233,7 +237,12 @@ proc read*(f: AsyncFile, size: int): Future[string] =
dealloc buffer dealloc buffer
buffer = nil buffer = nil
GC_unref(ol) GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(err)))
if err.int32 == ERROR_HANDLE_EOF:
# This happens in Windows Server 2003
retFuture.complete("")
else:
retFuture.fail(newException(OSError, osErrorMsg(err)))
else: else:
# Request completed immediately. # Request completed immediately.
var bytesRead: DWord var bytesRead: DWord

View file

@ -0,0 +1,266 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Implementation of a `deque`:idx: (double-ended queue).
## The underlying implementation uses a ``seq``.
##
## None of the procs that get an individual value from the deque can be used
## on an empty deque.
## If compiled with `boundChecks` option, those procs will raise an `IndexError`
## on such access. This should not be relied upon, as `-d:release` will
## disable those checks and may return garbage or crash the program.
##
## As such, a check to see if the deque is empty is needed before any
## access, unless your program logic guarantees it indirectly.
##
## .. code-block:: Nim
## proc foo(a, b: Positive) = # assume random positive values for `a` and `b`
## var deq = initDeque[int]() # initializes the object
## for i in 1 ..< a: deq.addLast i # populates the deque
##
## if b < deq.len: # checking before indexed access
## echo "The element at index position ", b, " is ", deq[b]
##
## # The following two lines don't need any checking on access due to the
## # logic of the program, but that would not be the case if `a` could be 0.
## assert deq.peekFirst == 1
## assert deq.peekLast == a
##
## while deq.len > 0: # checking if the deque is empty
## echo deq.removeLast()
##
## Note: For inter thread communication use
## a `Channel <channels.html>`_ instead.
import math
type
Deque*[T] = object
## A double-ended queue backed with a ringed seq buffer.
data: seq[T]
head, tail, count, mask: int
proc initDeque*[T](initialSize: int = 4): Deque[T] =
## Create a new deque.
## Optionally, the initial capacity can be reserved via `initialSize` as a
## performance optimization. The length of a newly created deque will still
## be 0.
##
## `initialSize` needs to be a power of two. If you need to accept runtime
## values for this you could use the ``nextPowerOfTwo`` proc from the
## `math <math.html>`_ module.
assert isPowerOfTwo(initialSize)
result.mask = initialSize-1
newSeq(result.data, initialSize)
proc len*[T](deq: Deque[T]): int {.inline.} =
## Return the number of elements of `deq`.
result = deq.count
template emptyCheck(deq) =
# Bounds check for the regular deque access.
when compileOption("boundChecks"):
if unlikely(deq.count < 1):
raise newException(IndexError, "Empty deque.")
template xBoundsCheck(deq, i) =
# Bounds check for the array like accesses.
when compileOption("boundChecks"): # d:release should disable this.
if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter
raise newException(IndexError,
"Out of bounds: " & $i & " > " & $(deq.count - 1))
proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
## Access the i-th element of `deq` by order from first to last.
## deq[0] is the first, deq[^1] is the last.
xBoundsCheck(deq, i)
return deq.data[(deq.first + i) and deq.mask]
proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
## Access the i-th element of `deq` and returns a mutable
## reference to it.
xBoundsCheck(deq, i)
return deq.data[(deq.head + i) and deq.mask]
proc `[]=`* [T] (deq: var Deque[T], i: Natural, val : T) {.inline.} =
## Change the i-th element of `deq`.
xBoundsCheck(deq, i)
deq.data[(deq.head + i) and deq.mask] = val
iterator items*[T](deq: Deque[T]): T =
## Yield every element of `deq`.
var i = deq.head
for c in 0 ..< deq.count:
yield deq.data[i]
i = (i + 1) and deq.mask
iterator mitems*[T](deq: var Deque[T]): var T =
## Yield every element of `deq`.
var i = deq.head
for c in 0 ..< deq.count:
yield deq.data[i]
i = (i + 1) and deq.mask
iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
## Yield every (position, value) of `deq`.
var i = deq.head
for c in 0 ..< deq.count:
yield (c, deq.data[i])
i = (i + 1) and deq.mask
proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
## Return true if `item` is in `deq` or false if not found. Usually used
## via the ``in`` operator. It is the equivalent of ``deq.find(item) >= 0``.
##
## .. code-block:: Nim
## if x in q:
## assert q.contains x
for e in deq:
if e == item: return true
return false
proc expandIfNeeded[T](deq: var Deque[T]) =
var cap = deq.mask + 1
if unlikely(deq.count >= cap):
var n = newSeq[T](cap * 2)
for i, x in deq: # don't use copyMem because the GC and because it's slower.
shallowCopy(n[i], x)
shallowCopy(deq.data, n)
deq.mask = cap * 2 - 1
deq.tail = deq.count
deq.head = 0
proc addFirst*[T](deq: var Deque[T], item: T) =
## Add an `item` to the beginning of the `deq`.
expandIfNeeded(deq)
inc deq.count
deq.head = (deq.head - 1) and deq.mask
deq.data[deq.head] = item
proc addLast*[T](deq: var Deque[T], item: T) =
## Add an `item` to the end of the `deq`.
expandIfNeeded(deq)
inc deq.count
deq.data[deq.tail] = item
deq.tail = (deq.tail + 1) and deq.mask
proc peekFirst*[T](deq: Deque[T]): T {.inline.}=
## Returns the first element of `deq`, but does not remove it from the deque.
emptyCheck(deq)
result = deq.data[deq.head]
proc peekLast*[T](deq: Deque[T]): T {.inline.} =
## Returns the last element of `deq`, but does not remove it from the deque.
emptyCheck(deq)
result = deq.data[(deq.tail - 1) and deq.mask]
proc default[T](t: typedesc[T]): T {.inline.} = discard
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
## Remove and returns the first element of the `deq`.
emptyCheck(deq)
dec deq.count
result = deq.data[deq.head]
deq.data[deq.head] = default(type(result))
deq.head = (deq.head + 1) and deq.mask
proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
## Remove and returns the last element of the `deq`.
emptyCheck(deq)
dec deq.count
deq.tail = (deq.tail - 1) and deq.mask
result = deq.data[deq.tail]
deq.data[deq.tail] = default(type(result))
proc `$`*[T](deq: Deque[T]): string =
## Turn a deque into its string representation.
result = "["
for x in deq:
if result.len > 1: result.add(", ")
result.add($x)
result.add("]")
when isMainModule:
var deq = initDeque[int](1)
deq.addLast(4)
deq.addFirst(9)
deq.addFirst(123)
var first = deq.popFirst()
deq.addLast(56)
assert(deq.peekLast() == 56)
deq.addLast(6)
assert(deq.peekLast() == 6)
var second = deq.popFirst()
deq.addLast(789)
assert(deq.peekLast() == 789)
assert first == 123
assert second == 9
assert($deq == "[4, 56, 6, 789]")
assert deq[0] == deq.peekFirst and deq.peekFirst == 4
assert deq[^1] == deq.peekLast and deq.peekLast == 789
deq[0] = 42
deq[^1] = 7
assert 6 in deq and 789 notin deq
assert deq.find(6) >= 0
assert deq.find(789) < 0
for i in -2 .. 10:
if i in deq:
assert deq.contains(i) and deq.find(i) >= 0
else:
assert(not deq.contains(i) and deq.find(i) < 0)
when compileOption("boundChecks"):
try:
echo deq[99]
assert false
except IndexError:
discard
try:
assert deq.len == 4
for i in 0 ..< 5: deq.popFirst()
assert false
except IndexError:
discard
# grabs some types of resize error.
deq = initDeque[int]()
for i in 1 .. 4: deq.addLast i
deq.popFirst()
deq.popLast()
for i in 5 .. 8: deq.addFirst i
assert $deq == "[8, 7, 6, 5, 2, 3]"
# Similar to proc from the documentation example
proc foo(a, b: Positive) = # assume random positive values for `a` and `b`.
var deq = initDeque[int]()
assert deq.len == 0
for i in 1 .. a: deq.addLast i
if b < deq.len: # checking before indexed access.
assert deq[b] == b + 1
# The following two lines don't need any checking on access due to the logic
# of the program, but that would not be the case if `a` could be 0.
assert deq.peekFirst == 1
assert deq.peekLast == a
while deq.len > 0: # checking if the deque is empty
assert deq.popFirst() > 0
#foo(0,0)
foo(8,5)
foo(10,9)
foo(1,1)
foo(2,1)
foo(1,5)
foo(3,2)

View file

@ -39,8 +39,10 @@
import math import math
{.warning: "`queues` module is deprecated - use `deques` instead".}
type type
Queue*[T] = object ## A queue. Queue* {.deprecated.} [T] = object ## A queue.
data: seq[T] data: seq[T]
rd, wr, count, mask: int rd, wr, count, mask: int

View file

@ -39,16 +39,22 @@ template rawGetKnownHCImpl() {.dirty.} =
h = nextTry(h, maxHash(t)) h = nextTry(h, maxHash(t))
result = -1 - h # < 0 => MISSING; insert idx = -1 - result result = -1 - h # < 0 => MISSING; insert idx = -1 - result
template rawGetImpl() {.dirty.} = template genHashImpl(key, hc: typed) =
hc = hash(key) hc = hash(key)
if hc == 0: # This almost never taken branch should be very predictable. if hc == 0: # This almost never taken branch should be very predictable.
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine. hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
template genHash(key: typed): Hash =
var res: Hash
genHashImpl(key, res)
res
template rawGetImpl() {.dirty.} =
genHashImpl(key, hc)
rawGetKnownHCImpl() rawGetKnownHCImpl()
template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add
hc = hash(key) genHashImpl(key, hc)
if hc == 0:
hc = 314159265
var h: Hash = hc and maxHash(t) var h: Hash = hc and maxHash(t)
while isFilled(t.data[h].hcode): while isFilled(t.data[h].hcode):
h = nextTry(h, maxHash(t)) h = nextTry(h, maxHash(t))

View file

@ -224,7 +224,7 @@ template withValue*[A, B](t: var Table[A, B], key: A,
iterator allValues*[A, B](t: Table[A, B]; key: A): B = iterator allValues*[A, B](t: Table[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`. ## iterates over any value in the table `t` that belongs to the given `key`.
var h: Hash = hash(key) and high(t.data) var h: Hash = genHash(key) and high(t.data)
while isFilled(t.data[h].hcode): while isFilled(t.data[h].hcode):
if t.data[h].key == key: if t.data[h].key == key:
yield t.data[h].val yield t.data[h].val
@ -479,7 +479,7 @@ proc clear*[A, B](t: var OrderedTableRef[A, B]) =
## Resets the table so that is is empty. ## Resets the table so that is is empty.
clear(t[]) clear(t[])
template forAllOrderedPairs(yieldStmt: untyped) {.oldimmediate, dirty.} = template forAllOrderedPairs(yieldStmt: untyped): typed {.dirty.} =
var h = t.first var h = t.first
while h >= 0: while h >= 0:
var nxt = t.data[h].next var nxt = t.data[h].next
@ -674,13 +674,6 @@ proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
template forAllOrderedPairs(yieldStmt: untyped) {.oldimmediate, dirty.} =
var h = t.first
while h >= 0:
var nxt = t.data[h].next
if isFilled(t.data[h].hcode): yieldStmt
h = nxt
iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
## iterates over any (key, value) pair in the table `t` in insertion ## iterates over any (key, value) pair in the table `t` in insertion
## order. ## order.
@ -785,20 +778,22 @@ proc sort*[A, B](t: OrderedTableRef[A, B],
proc del*[A, B](t: var OrderedTable[A, B], key: A) = proc del*[A, B](t: var OrderedTable[A, B], key: A) =
## deletes `key` from ordered hash table `t`. O(n) comlexity. ## deletes `key` from ordered hash table `t`. O(n) comlexity.
var prev = -1 var n: OrderedKeyValuePairSeq[A, B]
let hc = hash(key) newSeq(n, len(t.data))
forAllOrderedPairs: var h = t.first
if t.data[h].hcode == hc: t.first = -1
if t.first == h: t.last = -1
t.first = t.data[h].next swap(t.data, n)
let hc = genHash(key)
while h >= 0:
var nxt = n[h].next
if isFilled(n[h].hcode):
if n[h].hcode == hc and n[h].key == key:
dec t.counter
else: else:
t.data[prev].next = t.data[h].next var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode)
var zeroValue : type(t.data[h]) rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
t.data[h] = zeroValue h = nxt
dec t.counter
break
else:
prev = h
proc del*[A, B](t: var OrderedTableRef[A, B], key: A) = proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
## deletes `key` from ordered hash table `t`. O(n) comlexity. ## deletes `key` from ordered hash table `t`. O(n) comlexity.
@ -1164,6 +1159,20 @@ when isMainModule:
doAssert(prev < i) doAssert(prev < i)
prev = i prev = i
block: # Deletion from OrderedTable should account for collision groups. See issue #5057.
# The bug is reproducible only with exact keys
const key1 = "boy_jackpot.inGamma"
const key2 = "boy_jackpot.outBlack"
var t = {
key1: 0,
key2: 0
}.toOrderedTable()
t.del(key1)
assert(t.len == 1)
assert(key2 in t)
var var
t1 = initCountTable[string]() t1 = initCountTable[string]()
t2 = initCountTable[string]() t2 = initCountTable[string]()

View file

@ -263,13 +263,13 @@ proc all*[T](futs: varargs[Future[T]]): auto =
for fut in futs: for fut in futs:
fut.callback = proc(f: Future[T]) = fut.callback = proc(f: Future[T]) =
if f.failed: inc(completedFutures)
retFuture.fail(f.error) if not retFuture.finished:
elif not retFuture.finished: if f.failed:
inc(completedFutures) retFuture.fail(f.error)
else:
if completedFutures == totalFutures: if completedFutures == totalFutures:
retFuture.complete() retFuture.complete()
if totalFutures == 0: if totalFutures == 0:
retFuture.complete() retFuture.complete()
@ -285,14 +285,15 @@ proc all*[T](futs: varargs[Future[T]]): auto =
for i, fut in futs: for i, fut in futs:
proc setCallback(i: int) = proc setCallback(i: int) =
fut.callback = proc(f: Future[T]) = fut.callback = proc(f: Future[T]) =
if f.failed: inc(completedFutures)
retFuture.fail(f.error) if not retFuture.finished:
elif not retFuture.finished: if f.failed:
retValues[i] = f.read() retFuture.fail(f.error)
inc(completedFutures) else:
retValues[i] = f.read()
if completedFutures == len(retValues): if completedFutures == len(retValues):
retFuture.complete(retValues) retFuture.complete(retValues)
setCallback(i) setCallback(i)

View file

@ -954,9 +954,11 @@ proc newIndent(curr, indent: int, ml: bool): int =
proc nl(s: var string, ml: bool) = proc nl(s: var string, ml: bool) =
if ml: s.add("\n") if ml: s.add("\n")
proc escapeJson*(s: string): string = proc escapeJson*(s: string; result: var string) =
## Converts a string `s` to its JSON representation. ## Converts a string `s` to its JSON representation.
result = newStringOfCap(s.len + s.len shr 3) ## Appends to ``result``.
const
HexChars = "0123456789ABCDEF"
result.add("\"") result.add("\"")
for x in runes(s): for x in runes(s):
var r = int(x) var r = int(x)
@ -967,10 +969,19 @@ proc escapeJson*(s: string): string =
of '\\': result.add("\\\\") of '\\': result.add("\\\\")
else: result.add(c) else: result.add(c)
else: else:
result.add("\\u") # toHex inlined for more speed (saves stupid string allocations):
result.add(toHex(r, 4)) result.add("\\u0000")
let start = result.len - 4
for j in countdown(3, 0):
result[j+start] = HexChars[r and 0xF]
r = r shr 4
result.add("\"") result.add("\"")
proc escapeJson*(s: string): string =
## Converts a string `s` to its JSON representation.
result = newStringOfCap(s.len + s.len shr 3)
escapeJson(s, result)
proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true, proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
lstArr = false, currIndent = 0) = lstArr = false, currIndent = 0) =
case node.kind case node.kind
@ -988,7 +999,7 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
inc i inc i
# Need to indent more than { # Need to indent more than {
result.indent(newIndent(currIndent, indent, ml)) result.indent(newIndent(currIndent, indent, ml))
result.add(escapeJson(key)) escapeJson(key, result)
result.add(": ") result.add(": ")
toPretty(result, val, indent, ml, false, toPretty(result, val, indent, ml, false,
newIndent(currIndent, indent, ml)) newIndent(currIndent, indent, ml))
@ -999,16 +1010,19 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
result.add("{}") result.add("{}")
of JString: of JString:
if lstArr: result.indent(currIndent) if lstArr: result.indent(currIndent)
result.add(escapeJson(node.str)) escapeJson(node.str, result)
of JInt: of JInt:
if lstArr: result.indent(currIndent) if lstArr: result.indent(currIndent)
result.add($node.num) when defined(js): result.add($node.num)
else: result.add(node.num)
of JFloat: of JFloat:
if lstArr: result.indent(currIndent) if lstArr: result.indent(currIndent)
result.add($node.fnum) # Fixme: implement new system.add ops for the JS target
when defined(js): result.add($node.fnum)
else: result.add(node.fnum)
of JBool: of JBool:
if lstArr: result.indent(currIndent) if lstArr: result.indent(currIndent)
result.add($node.bval) result.add(if node.bval: "true" else: "false")
of JArray: of JArray:
if lstArr: result.indent(currIndent) if lstArr: result.indent(currIndent)
if len(node.elems) != 0: if len(node.elems) != 0:
@ -1057,16 +1071,18 @@ proc toUgly*(result: var string, node: JsonNode) =
for key, value in pairs(node.fields): for key, value in pairs(node.fields):
if comma: result.add "," if comma: result.add ","
else: comma = true else: comma = true
result.add key.escapeJson() key.escapeJson(result)
result.add ":" result.add ":"
result.toUgly value result.toUgly value
result.add "}" result.add "}"
of JString: of JString:
result.add node.str.escapeJson() node.str.escapeJson(result)
of JInt: of JInt:
result.add($node.num) when defined(js): result.add($node.num)
else: result.add(node.num)
of JFloat: of JFloat:
result.add($node.fnum) when defined(js): result.add($node.fnum)
else: result.add(node.fnum)
of JBool: of JBool:
result.add(if node.bval: "true" else: "false") result.add(if node.bval: "true" else: "false")
of JNull: of JNull:
@ -1394,4 +1410,6 @@ when isMainModule:
var parsed2 = parseFile("tests/testdata/jsontest2.json") var parsed2 = parseFile("tests/testdata/jsontest2.json")
doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
doAssert escapeJson("\10FoobarÄ") == "\"\\u000AFoobar\\u00C4\""
echo("Tests succeeded!") echo("Tests succeeded!")

View file

@ -172,18 +172,26 @@ when not defined(js):
var (path, name, _) = splitFile(getAppFilename()) var (path, name, _) = splitFile(getAppFilename())
result = changeFileExt(path / name, "log") result = changeFileExt(path / name, "log")
proc newFileLogger*(file: File,
levelThreshold = lvlAll,
fmtStr = defaultFmtStr): FileLogger =
## Creates a new file logger. This logger logs to ``file``.
new(result)
result.file = file
result.levelThreshold = levelThreshold
result.fmtStr = fmtStr
proc newFileLogger*(filename = defaultFilename(), proc newFileLogger*(filename = defaultFilename(),
mode: FileMode = fmAppend, mode: FileMode = fmAppend,
levelThreshold = lvlAll, levelThreshold = lvlAll,
fmtStr = defaultFmtStr, fmtStr = defaultFmtStr,
bufSize: int = -1): FileLogger = bufSize: int = -1): FileLogger =
## Creates a new file logger. This logger logs to a file. ## Creates a new file logger. This logger logs to a file, specified
## by ``fileName``.
## Use ``bufSize`` as size of the output buffer when writing the file ## Use ``bufSize`` as size of the output buffer when writing the file
## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size). ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
new(result) let file = open(filename, mode, bufSize = bufSize)
result.levelThreshold = levelThreshold newFileLogger(file, levelThreshold, fmtStr)
result.file = open(filename, mode, bufSize = bufSize)
result.fmtStr = fmtStr
# ------ # ------

View file

@ -9,6 +9,7 @@
## This module contains procs for `serialization`:idx: and `deseralization`:idx: ## This module contains procs for `serialization`:idx: and `deseralization`:idx:
## of arbitrary Nim data structures. The serialization format uses `JSON`:idx:. ## of arbitrary Nim data structures. The serialization format uses `JSON`:idx:.
## Warning: The serialization format could change in future!
## ##
## **Restriction**: For objects their type is **not** serialized. This means ## **Restriction**: For objects their type is **not** serialized. This means
## essentially that it does not work if the object has some other runtime ## essentially that it does not work if the object has some other runtime
@ -29,6 +30,12 @@
## a = b ## a = b
## echo($$a[]) # produces "{}", not "{f: 0}" ## echo($$a[]) # produces "{}", not "{f: 0}"
## ##
## # unmarshal
## let c = to[B]("""{"f": 2}""")
##
## # marshal
## let s = $$c
## **Note**: The ``to`` and ``$$`` operations are available at compile-time! ## **Note**: The ``to`` and ``$$`` operations are available at compile-time!
import streams, typeinfo, json, intsets, tables, unicode import streams, typeinfo, json, intsets, tables, unicode

View file

@ -1250,7 +1250,7 @@ proc IPv6_loopback*(): IpAddress =
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
proc `==`*(lhs, rhs: IpAddress): bool = proc `==`*(lhs, rhs: IpAddress): bool =
## Compares two IpAddresses for Equality. Returns two if the addresses are equal ## Compares two IpAddresses for Equality. Returns true if the addresses are equal
if lhs.family != rhs.family: return false if lhs.family != rhs.family: return false
if lhs.family == IpAddressFamily.IPv4: if lhs.family == IpAddressFamily.IPv4:
for i in low(lhs.address_v4) .. high(lhs.address_v4): for i in low(lhs.address_v4) .. high(lhs.address_v4):

80
lib/pure/nimtracker.nim Normal file
View file

@ -0,0 +1,80 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2016 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Memory tracking support for Nim.
when not defined(memTracker) and not isMainModule:
{.error: "Memory tracking support is turned off!".}
{.push memtracker: off.}
# we import the low level wrapper and are careful not to use Nim's
# memory manager for anything here.
import sqlite3
var
dbHandle: PSqlite3
insertStmt: Pstmt
template sbind(x: int; value) =
when value is cstring:
let ret = insertStmt.bindText(x, value, value.len.int32, SQLITE_TRANSIENT)
if ret != SQLITE_OK:
quit "could not bind value"
else:
let ret = insertStmt.bindInt64(x, value)
if ret != SQLITE_OK:
quit "could not bind value"
when defined(memTracker):
proc logEntries(log: TrackLog) {.nimcall, locks: 0, tags: [].} =
for i in 0..log.count-1:
var success = false
let e = log.data[i]
discard sqlite3.reset(insertStmt)
discard clearBindings(insertStmt)
sbind 1, e.op
sbind(2, cast[int](e.address))
sbind 3, e.size
sbind 4, e.file
sbind 5, e.line
if step(insertStmt) == SQLITE_DONE:
success = true
if not success:
quit "could not write to database!"
proc execQuery(q: string) =
var s: Pstmt
if prepare_v2(dbHandle, q, q.len.int32, s, nil) == SQLITE_OK:
discard step(s)
if finalize(s) != SQLITE_OK:
quit "could not finalize " & $sqlite3.errmsg(dbHandle)
else:
quit "could not prepare statement " & $sqlite3.errmsg(dbHandle)
proc setupDb() =
execQuery """create table if not exists tracking(
id integer primary key,
op varchar not null,
address integer not null,
size integer not null,
file varchar not null,
line integer not null
)"""
execQuery "delete from tracking"
if sqlite3.open("memtrack.db", dbHandle) == SQLITE_OK:
setupDb()
const query = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
if prepare_v2(dbHandle, query,
query.len, insertStmt, nil) == SQLITE_OK:
when defined(memTracker):
setTrackLogger logEntries
else:
quit "could not prepare statement B " & $sqlite3.errmsg(dbHandle)
{.pop.}

View file

@ -1309,10 +1309,12 @@ proc join*[T: not string](a: openArray[T], sep: string = ""): string {.
type type
SkipTable = array[char, int] SkipTable = array[char, int]
{.push profiler: off.}
proc preprocessSub(sub: string, a: var SkipTable) = proc preprocessSub(sub: string, a: var SkipTable) =
var m = len(sub) var m = len(sub)
for i in 0..0xff: a[chr(i)] = m+1 for i in 0..0xff: a[chr(i)] = m+1
for i in 0..m-1: a[sub[i]] = m-i for i in 0..m-1: a[sub[i]] = m-i
{.pop.}
proc findAux(s, sub: string, start: int, a: SkipTable): int = proc findAux(s, sub: string, start: int, a: SkipTable): int =
# Fast "quick search" algorithm: # Fast "quick search" algorithm:

View file

@ -144,8 +144,9 @@ type
yearday*: range[0..365] ## The number of days since January 1, yearday*: range[0..365] ## The number of days since January 1,
## in the range 0 to 365. ## in the range 0 to 365.
## Always 0 if the target is JS. ## Always 0 if the target is JS.
isDST*: bool ## Determines whether DST is in effect. Always isDST*: bool ## Determines whether DST is in effect.
## ``False`` if time is UTC. ## Semantically, this adds another negative hour
## offset to the time in addition to the timezone.
timezone*: int ## The offset of the (non-DST) timezone in seconds timezone*: int ## The offset of the (non-DST) timezone in seconds
## west of UTC. Note that the sign of this number ## west of UTC. Note that the sign of this number
## is the opposite of the one in a formatted ## is the opposite of the one in a formatted
@ -278,16 +279,20 @@ proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
carryO = `div`(ti1.months + ti2.months, 12) carryO = `div`(ti1.months + ti2.months, 12)
result.years = carryO + ti1.years + ti2.years result.years = carryO + ti1.years + ti2.years
proc `-`*(ti: TimeInterval): TimeInterval =
result = TimeInterval(
milliseconds: -ti.milliseconds,
seconds: -ti.seconds,
minutes: -ti.minutes,
hours: -ti.hours,
days: -ti.days,
months: -ti.months,
years: -ti.years
)
proc `-`*(ti1, ti2: TimeInterval): TimeInterval = proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
## Subtracts TimeInterval ``ti1`` from ``ti2``. ## Subtracts TimeInterval ``ti1`` from ``ti2``.
result = ti1 result = ti1 + (-ti2)
result.milliseconds -= ti2.milliseconds
result.seconds -= ti2.seconds
result.minutes -= ti2.minutes
result.hours -= ti2.hours
result.days -= ti2.days
result.months -= ti2.months
result.years -= ti2.years
proc isLeapYear*(year: int): bool = proc isLeapYear*(year: int): bool =
## returns true if ``year`` is a leap year ## returns true if ``year`` is a leap year
@ -363,16 +368,9 @@ proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## ##
## **Note:** This has been only briefly tested, it is inaccurate especially ## **Note:** This has been only briefly tested, it is inaccurate especially
## when you subtract so much that you reach the Julian calendar. ## when you subtract so much that you reach the Julian calendar.
let t = toSeconds(toTime(a)) let
var intval: TimeInterval t = toSeconds(toTime(a))
intval.milliseconds = - interval.milliseconds secs = toSeconds(a, -interval)
intval.seconds = - interval.seconds
intval.minutes = - interval.minutes
intval.hours = - interval.hours
intval.days = - interval.days
intval.months = - interval.months
intval.years = - interval.years
let secs = toSeconds(a, intval)
if a.timezone == 0: if a.timezone == 0:
result = getGMTime(fromSeconds(t + secs)) result = getGMTime(fromSeconds(t + secs))
else: else:
@ -732,6 +730,7 @@ const
secondsInMin = 60 secondsInMin = 60
secondsInHour = 60*60 secondsInHour = 60*60
secondsInDay = 60*60*24 secondsInDay = 60*60*24
minutesInHour = 60
epochStartYear = 1970 epochStartYear = 1970
proc formatToken(info: TimeInfo, token: string, buf: var string) = proc formatToken(info: TimeInfo, token: string, buf: var string) =
@ -823,28 +822,32 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "z": of "z":
let hours = abs(info.timezone) div secondsInHour let
if info.timezone < 0: buf.add('-') nonDstTz = info.timezone - int(info.isDst) * secondsInHour
else: buf.add('+') hours = abs(nonDstTz) div secondsInHour
if nonDstTz <= 0: buf.add('+')
else: buf.add('-')
buf.add($hours) buf.add($hours)
of "zz": of "zz":
let hours = abs(info.timezone) div secondsInHour let
if info.timezone < 0: buf.add('-') nonDstTz = info.timezone - int(info.isDst) * secondsInHour
else: buf.add('+') hours = abs(nonDstTz) div secondsInHour
if nonDstTz <= 0: buf.add('+')
else: buf.add('-')
if hours < 10: buf.add('0') if hours < 10: buf.add('0')
buf.add($hours) buf.add($hours)
of "zzz": of "zzz":
let let
hours = abs(info.timezone) div secondsInHour nonDstTz = info.timezone - int(info.isDst) * secondsInHour
minutes = abs(info.timezone) mod 60 hours = abs(nonDstTz) div secondsInHour
if info.timezone < 0: buf.add('-') minutes = (abs(nonDstTz) div secondsInMin) mod minutesInHour
else: buf.add('+') if nonDstTz <= 0: buf.add('+')
else: buf.add('-')
if hours < 10: buf.add('0') if hours < 10: buf.add('0')
buf.add($hours) buf.add($hours)
buf.add(':') buf.add(':')
if minutes < 10: buf.add('0') if minutes < 10: buf.add('0')
buf.add($minutes) buf.add($minutes)
of "": of "":
discard discard
else: else:
@ -999,7 +1002,6 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
of "M": of "M":
var pd = parseInt(value[j..j+1], sv) var pd = parseInt(value[j..j+1], sv)
info.month = Month(sv-1) info.month = Month(sv-1)
info.monthday = sv
j += pd j += pd
of "MM": of "MM":
var month = value[j..j+1].parseInt() var month = value[j..j+1].parseInt()
@ -1088,27 +1090,42 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
info.year = value[j..j+3].parseInt() info.year = value[j..j+3].parseInt()
j += 4 j += 4
of "z": of "z":
info.isDST = false
if value[j] == '+': if value[j] == '+':
info.timezone = 0 - parseInt($value[j+1]) * secondsInHour info.timezone = 0 - parseInt($value[j+1]) * secondsInHour
elif value[j] == '-': elif value[j] == '-':
info.timezone = parseInt($value[j+1]) * secondsInHour info.timezone = parseInt($value[j+1]) * secondsInHour
elif value[j] == 'Z':
info.timezone = 0
j += 1
return
else: else:
raise newException(ValueError, raise newException(ValueError,
"Couldn't parse timezone offset (z), got: " & value[j]) "Couldn't parse timezone offset (z), got: " & value[j])
j += 2 j += 2
of "zz": of "zz":
info.isDST = false
if value[j] == '+': if value[j] == '+':
info.timezone = 0 - value[j+1..j+2].parseInt() * secondsInHour info.timezone = 0 - value[j+1..j+2].parseInt() * secondsInHour
elif value[j] == '-': elif value[j] == '-':
info.timezone = value[j+1..j+2].parseInt() * secondsInHour info.timezone = value[j+1..j+2].parseInt() * secondsInHour
elif value[j] == 'Z':
info.timezone = 0
j += 1
return
else: else:
raise newException(ValueError, raise newException(ValueError,
"Couldn't parse timezone offset (zz), got: " & value[j]) "Couldn't parse timezone offset (zz), got: " & value[j])
j += 3 j += 3
of "zzz": of "zzz":
info.isDST = false
var factor = 0 var factor = 0
if value[j] == '+': factor = -1 if value[j] == '+': factor = -1
elif value[j] == '-': factor = 1 elif value[j] == '-': factor = 1
elif value[j] == 'Z':
info.timezone = 0
j += 1
return
else: else:
raise newException(ValueError, raise newException(ValueError,
"Couldn't parse timezone offset (zzz), got: " & value[j]) "Couldn't parse timezone offset (zzz), got: " & value[j])
@ -1121,8 +1138,11 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
j += token.len j += token.len
proc parse*(value, layout: string): TimeInfo = proc parse*(value, layout: string): TimeInfo =
## This function parses a date/time string using the standard format identifiers (below) ## This function parses a date/time string using the standard format
## The function defaults information not provided in the format string from the running program (timezone, month, year, etc) ## identifiers as listed below. The function defaults information not provided
## in the format string from the running program (timezone, month, year, etc).
## Daylight saving time is only set if no timezone is given and the given date
## lies within the DST period of the current locale.
## ##
## ========== ================================================================================= ================================================ ## ========== ================================================================================= ================================================
## Specifier Description Example ## Specifier Description Example
@ -1147,7 +1167,7 @@ proc parse*(value, layout: string): TimeInfo =
## tt Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively. ## tt Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively.
## yy Displays the year to two digits. ``2012 -> 12`` ## yy Displays the year to two digits. ``2012 -> 12``
## yyyy Displays the year to four digits. ``2012 -> 2012`` ## yyyy Displays the year to four digits. ``2012 -> 2012``
## z Displays the timezone offset from UTC. ``GMT+7 -> +7``, ``GMT-5 -> -5`` ## z Displays the timezone offset from UTC. ``Z`` is parsed as ``+0`` ``GMT+7 -> +7``, ``GMT-5 -> -5``
## zz Same as above but with leading 0. ``GMT+7 -> +07``, ``GMT-5 -> -05`` ## zz Same as above but with leading 0. ``GMT+7 -> +07``, ``GMT-5 -> -05``
## zzz Same as above but with ``:mm`` where *mm* represents minutes. ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00`` ## zzz Same as above but with ``:mm`` where *mm* represents minutes. ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00``
## ========== ================================================================================= ================================================ ## ========== ================================================================================= ================================================
@ -1165,6 +1185,8 @@ proc parse*(value, layout: string): TimeInfo =
info.hour = 0 info.hour = 0
info.minute = 0 info.minute = 0
info.second = 0 info.second = 0
info.isDST = true # using this is flag for checking whether a timezone has \
# been read (because DST is always false when a tz is parsed)
while true: while true:
case layout[i] case layout[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
@ -1194,16 +1216,17 @@ proc parse*(value, layout: string): TimeInfo =
parseToken(info, token, value, j) parseToken(info, token, value, j)
token = "" token = ""
# We are going to process the date to find out if we are in DST, because the if info.isDST:
# default based on the current time may be wrong. Calling getLocalTime will # means that no timezone has been parsed. In this case, we need to check
# set this correctly, but the actual time may be offset from when we called # whether the date is within DST of the local time.
# toTime with a possibly incorrect DST setting, so we are only going to take let tmp = getLocalTime(toTime(info))
# the isDST from this result. # correctly set isDST so that the following step works on the correct time
let correctDST = getLocalTime(toTime(info)) info.isDST = tmp.isDST
info.isDST = correctDST.isDST
# Now we process it again with the correct isDST to correct things like # Correct weekday and yearday; transform timestamp to local time.
# weekday and yearday. # There currently is no way of returning this with the original (parsed)
# timezone while also setting weekday and yearday (we are depending on stdlib
# to provide this calculation).
return getLocalTime(toTime(info)) return getLocalTime(toTime(info))
# Leap year calculations are adapted from: # Leap year calculations are adapted from:

View file

@ -98,7 +98,7 @@ proc startSuite(name: string) =
template rawPrint() = echo("\n[Suite] ", name) template rawPrint() = echo("\n[Suite] ", name)
when not defined(ECMAScript): when not defined(ECMAScript):
if colorOutput: if colorOutput:
styledEcho styleBright, fgBlue, "\n[Suite] ", fgWhite, name styledEcho styleBright, fgBlue, "\n[Suite] ", resetStyle, name
else: rawPrint() else: rawPrint()
else: rawPrint() else: rawPrint()
@ -159,7 +159,7 @@ proc testDone(name: string, s: TestStatus, indent: bool) =
of FAILED: fgRed of FAILED: fgRed
of SKIPPED: fgYellow of SKIPPED: fgYellow
else: fgWhite else: fgWhite
styledEcho styleBright, color, prefix, "[", $s, "] ", fgWhite, name styledEcho styleBright, color, prefix, "[", $s, "] ", resetStyle, name
else: else:
rawPrint() rawPrint()
else: else:

View file

@ -1272,12 +1272,14 @@ const
seqShallowFlag = low(int) seqShallowFlag = low(int)
{.push profiler: off.}
when defined(nimKnowsNimvm): when defined(nimKnowsNimvm):
let nimvm* {.magic: "Nimvm".}: bool = false let nimvm* {.magic: "Nimvm".}: bool = false
## may be used only in "when" expression. ## may be used only in "when" expression.
## It is true in Nim VM context and false otherwise ## It is true in Nim VM context and false otherwise
else: else:
const nimvm*: bool = false const nimvm*: bool = false
{.pop.}
proc compileOption*(option: string): bool {. proc compileOption*(option: string): bool {.
magic: "CompileOption", noSideEffect.} magic: "CompileOption", noSideEffect.}
@ -2544,6 +2546,7 @@ when hostOS == "standalone":
include "$projectpath/panicoverride" include "$projectpath/panicoverride"
when not declared(sysFatal): when not declared(sysFatal):
{.push profiler: off.}
when hostOS == "standalone": when hostOS == "standalone":
proc sysFatal(exceptn: typedesc, message: string) {.inline.} = proc sysFatal(exceptn: typedesc, message: string) {.inline.} =
panic(message) panic(message)
@ -2563,6 +2566,7 @@ when not declared(sysFatal):
new(e) new(e)
e.msg = message & arg e.msg = message & arg
raise e raise e
{.pop.}
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.} proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.}
## get type information for `x`. Ordinary code should not use this, but ## get type information for `x`. Ordinary code should not use this, but
@ -2616,8 +2620,10 @@ when not defined(JS): #and not defined(nimscript):
when declared(setStackBottom): when declared(setStackBottom):
setStackBottom(locals) setStackBottom(locals)
{.push profiler: off.}
var var
strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic}) strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
{.pop.}
# ----------------- IO Part ------------------------------------------------ # ----------------- IO Part ------------------------------------------------
@ -2950,6 +2956,8 @@ when not defined(JS): #and not defined(nimscript):
## lead to the ``raise`` statement. This only works for debug builds. ## lead to the ``raise`` statement. This only works for debug builds.
{.push stack_trace: off, profiler:off.} {.push stack_trace: off, profiler:off.}
when defined(memtracker):
include "system/memtracker"
when hostOS == "standalone": when hostOS == "standalone":
include "system/embedded" include "system/embedded"
else: else:
@ -2992,7 +3000,9 @@ when not defined(JS): #and not defined(nimscript):
else: else:
result = n.sons[n.len] result = n.sons[n.len]
{.push profiler:off.}
when hasAlloc: include "system/mmdisp" when hasAlloc: include "system/mmdisp"
{.pop.}
{.push stack_trace: off, profiler:off.} {.push stack_trace: off, profiler:off.}
when hasAlloc: include "system/sysstr" when hasAlloc: include "system/sysstr"
{.pop.} {.pop.}

View file

@ -15,6 +15,10 @@
include osalloc include osalloc
template track(op, address, size) =
when defined(memTracker):
memTrackerOp(op, address, size)
# We manage *chunks* of memory. Each chunk is a multiple of the page size. # We manage *chunks* of memory. Each chunk is a multiple of the page size.
# Each chunk starts at an address that is divisible by the page size. Chunks # Each chunk starts at an address that is divisible by the page size. Chunks
# that are bigger than ``ChunkOsReturn`` are returned back to the operating # that are bigger than ``ChunkOsReturn`` are returned back to the operating
@ -645,6 +649,7 @@ proc alloc(allocator: var MemRegion, size: Natural): pointer =
cast[ptr FreeCell](result).zeroField = 1 # mark it as used cast[ptr FreeCell](result).zeroField = 1 # mark it as used
sysAssert(not isAllocatedPtr(allocator, result), "alloc") sysAssert(not isAllocatedPtr(allocator, result), "alloc")
result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell)) result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
track("alloc", result, size)
proc alloc0(allocator: var MemRegion, size: Natural): pointer = proc alloc0(allocator: var MemRegion, size: Natural): pointer =
result = alloc(allocator, size) result = alloc(allocator, size)
@ -658,6 +663,7 @@ proc dealloc(allocator: var MemRegion, p: pointer) =
sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc 2") sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc 2")
rawDealloc(allocator, x) rawDealloc(allocator, x)
sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3") sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3")
track("dealloc", p, 0)
proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer = proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
if newsize > 0: if newsize > 0:

View file

@ -7,18 +7,66 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) {.benign.} type
proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} = PtrTable = ptr object
counter, max: int
data: array[0..0xff_ffff, (pointer, pointer)]
template hashPtr(key: pointer): int = cast[int](key) shr 8
template allocPtrTable: untyped =
cast[PtrTable](alloc0(sizeof(int)*2 + sizeof(pointer)*2*cap))
proc rehash(t: PtrTable): PtrTable =
let cap = (t.max+1) * 2
result = allocPtrTable()
result.counter = t.counter
result.max = cap-1
for i in 0..t.max:
let k = t.data[i][0]
if k != nil:
var h = hashPtr(k)
while result.data[h and result.max][0] != nil: inc h
result.data[h and result.max] = t.data[i]
dealloc t
proc initPtrTable(): PtrTable =
const cap = 32
result = allocPtrTable()
result.counter = 0
result.max = cap-1
template deinit(t: PtrTable) = dealloc(t)
proc get(t: PtrTable; key: pointer): pointer =
var h = hashPtr(key)
while true:
let k = t.data[h and t.max][0]
if k == nil: break
if k == key:
return t.data[h and t.max][1]
inc h
proc put(t: var PtrTable; key, val: pointer) =
if (t.max+1) * 2 < t.counter * 3: t = rehash(t)
var h = hashPtr(key)
while t.data[h and t.max][0] != nil: inc h
t.data[h and t.max] = (key, val)
inc t.counter
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType;
tab: var PtrTable) {.benign.}
proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode;
tab: var PtrTable) {.benign.} =
var var
d = cast[ByteAddress](dest) d = cast[ByteAddress](dest)
s = cast[ByteAddress](src) s = cast[ByteAddress](src)
case n.kind case n.kind
of nkSlot: of nkSlot:
genericDeepCopyAux(cast[pointer](d +% n.offset), genericDeepCopyAux(cast[pointer](d +% n.offset),
cast[pointer](s +% n.offset), n.typ) cast[pointer](s +% n.offset), n.typ, tab)
of nkList: of nkList:
for i in 0..n.len-1: for i in 0..n.len-1:
genericDeepCopyAux(dest, src, n.sons[i]) genericDeepCopyAux(dest, src, n.sons[i], tab)
of nkCase: of nkCase:
var dd = selectBranch(dest, n) var dd = selectBranch(dest, n)
var m = selectBranch(src, n) var m = selectBranch(src, n)
@ -29,10 +77,10 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
n.typ.size) n.typ.size)
if m != nil: if m != nil:
genericDeepCopyAux(dest, src, m) genericDeepCopyAux(dest, src, m, tab)
of nkNone: sysAssert(false, "genericDeepCopyAux") of nkNone: sysAssert(false, "genericDeepCopyAux")
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) = proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
var var
d = cast[ByteAddress](dest) d = cast[ByteAddress](dest)
s = cast[ByteAddress](src) s = cast[ByteAddress](src)
@ -60,22 +108,22 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
GenericSeqSize), GenericSeqSize),
mt.base) mt.base, tab)
of tyObject: of tyObject:
# we need to copy m_type field for tyObject, as it could be empty for # we need to copy m_type field for tyObject, as it could be empty for
# sequence reallocations: # sequence reallocations:
if mt.base != nil: if mt.base != nil:
genericDeepCopyAux(dest, src, mt.base) genericDeepCopyAux(dest, src, mt.base, tab)
else: else:
var pint = cast[ptr PNimType](dest) var pint = cast[ptr PNimType](dest)
pint[] = cast[ptr PNimType](src)[] pint[] = cast[ptr PNimType](src)[]
genericDeepCopyAux(dest, src, mt.node) genericDeepCopyAux(dest, src, mt.node, tab)
of tyTuple: of tyTuple:
genericDeepCopyAux(dest, src, mt.node) genericDeepCopyAux(dest, src, mt.node, tab)
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
for i in 0..(mt.size div mt.base.size)-1: for i in 0..(mt.size div mt.base.size)-1:
genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size), genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base) cast[pointer](s +% i*% mt.base.size), mt.base, tab)
of tyRef: of tyRef:
let s2 = cast[PPointer](src)[] let s2 = cast[PPointer](src)[]
if s2 == nil: if s2 == nil:
@ -84,30 +132,29 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
let z = mt.base.deepcopy(s2) let z = mt.base.deepcopy(s2)
unsureAsgnRef(cast[PPointer](dest), z) unsureAsgnRef(cast[PPointer](dest), z)
else: else:
# we modify the header of the cell temporarily; instead of the type let z = tab.get(s2)
# field we store a forwarding pointer. XXX This is bad when the cloning if z == nil:
# fails due to OOM etc. when declared(usrToCell):
when declared(usrToCell): let x = usrToCell(s2)
# unfortunately we only have cycle detection for our native GCs.
let x = usrToCell(s2)
let forw = cast[int](x.typ)
if (forw and 1) == 1:
# we stored a forwarding pointer, so let's use that:
let z = cast[pointer](forw and not 1)
unsureAsgnRef(cast[PPointer](dest), z)
else:
let realType = x.typ let realType = x.typ
let z = newObj(realType, realType.base.size) let z = newObj(realType, realType.base.size)
unsureAsgnRef(cast[PPointer](dest), z) unsureAsgnRef(cast[PPointer](dest), z)
x.typ = cast[PNimType](cast[int](z) or 1) tab.put(s2, z)
genericDeepCopyAux(z, s2, realType.base) genericDeepCopyAux(z, s2, realType.base, tab)
x.typ = realType else:
when false:
# addition check disabled
let x = usrToCell(s2)
let realType = x.typ
sysAssert realType == mt, " types do differ"
# this version should work for any possible GC:
let size = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[].size else: mt.base.size
let z = newObj(mt, size)
unsureAsgnRef(cast[PPointer](dest), z)
tab.put(s2, z)
genericDeepCopyAux(z, s2, mt.base, tab)
else: else:
let size = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[].size
else: mt.base.size
let z = newObj(mt, size)
unsureAsgnRef(cast[PPointer](dest), z) unsureAsgnRef(cast[PPointer](dest), z)
genericDeepCopyAux(z, s2, mt.base)
of tyPtr: of tyPtr:
# no cycle check here, but also not really required # no cycle check here, but also not really required
let s2 = cast[PPointer](src)[] let s2 = cast[PPointer](src)[]
@ -120,7 +167,9 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} = proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
GC_disable() GC_disable()
genericDeepCopyAux(dest, src, mt) var tab = initPtrTable()
genericDeepCopyAux(dest, src, mt, tab)
deinit tab
GC_enable() GC_enable()
proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} = proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =

View file

@ -339,6 +339,8 @@ when not defined(noSignalHandler):
action("unknown signal\n") action("unknown signal\n")
# print stack trace and quit # print stack trace and quit
when defined(memtracker):
logPendingOps()
when hasSomeStackTrace: when hasSomeStackTrace:
GC_disable() GC_disable()
var buf = newStringOfCap(2000) var buf = newStringOfCap(2000)

View file

@ -445,6 +445,15 @@ proc gcInvariant*() =
markForDebug(gch) markForDebug(gch)
{.pop.} {.pop.}
template setFrameInfo(c: PCell) =
when leakDetector:
if framePtr != nil and framePtr.prev != nil:
c.filename = framePtr.prev.filename
c.line = framePtr.prev.line
else:
c.filename = nil
c.line = 0
proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
sysAssert(allocInv(gch.region), "rawNewObj begin") sysAssert(allocInv(gch.region), "rawNewObj begin")
@ -455,19 +464,14 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
res.typ = typ res.typ = typ
when leakDetector: setFrameInfo(res)
res.filename = nil
res.line = 0
when not hasThreadSupport:
if framePtr != nil and framePtr.prev != nil:
res.filename = framePtr.prev.filename
res.line = framePtr.prev.line
# refcount is zero, color is black, but mark it to be in the ZCT # refcount is zero, color is black, but mark it to be in the ZCT
res.refcount = ZctFlag res.refcount = ZctFlag
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3") sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
# its refcount is zero, so add it to the ZCT: # its refcount is zero, so add it to the ZCT:
addNewObjToZCT(res, gch) addNewObjToZCT(res, gch)
when logGC: writeCell("new cell", res) when logGC: writeCell("new cell", res)
track("rawNewObj", res, size)
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
release(gch) release(gch)
when useCellIds: when useCellIds:
@ -509,16 +513,11 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
res.typ = typ res.typ = typ
when leakDetector: setFrameInfo(res)
res.filename = nil
res.line = 0
when not hasThreadSupport:
if framePtr != nil and framePtr.prev != nil:
res.filename = framePtr.prev.filename
res.line = framePtr.prev.line
res.refcount = rcIncrement # refcount is 1 res.refcount = rcIncrement # refcount is 1
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3") sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
when logGC: writeCell("new cell", res) when logGC: writeCell("new cell", res)
track("newObjRC1", res, size)
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
release(gch) release(gch)
when useCellIds: when useCellIds:
@ -561,6 +560,8 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
writeCell("growObj new cell", res) writeCell("growObj new cell", res)
gcTrace(ol, csZctFreed) gcTrace(ol, csZctFreed)
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
track("growObj old", ol, 0)
track("growObj new", res, newsize)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "growObj before dealloc") sysAssert(allocInv(gch.region), "growObj before dealloc")
if ol.refcount shr rcShift <=% 1: if ol.refcount shr rcShift <=% 1:
@ -604,6 +605,7 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
proc freeCyclicCell(gch: var GcHeap, c: PCell) = proc freeCyclicCell(gch: var GcHeap, c: PCell) =
prepareDealloc(c) prepareDealloc(c)
gcTrace(c, csCycFreed) gcTrace(c, csCycFreed)
track("cycle collector dealloc cell", c, 0)
when logGC: writeCell("cycle collector dealloc cell", c) when logGC: writeCell("cycle collector dealloc cell", c)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "free cyclic cell") sysAssert(allocInv(gch.region), "free cyclic cell")
@ -673,6 +675,7 @@ proc doOperation(p: pointer, op: WalkOp) =
gcAssert(c.refcount >=% rcIncrement, "doOperation 2") gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
#c.refcount = c.refcount -% rcIncrement #c.refcount = c.refcount -% rcIncrement
when logGC: writeCell("decref (from doOperation)", c) when logGC: writeCell("decref (from doOperation)", c)
track("waZctDecref", p, 0)
decRef(c) decRef(c)
#if c.refcount <% rcIncrement: addZCT(gch.zct, c) #if c.refcount <% rcIncrement: addZCT(gch.zct, c)
of waPush: of waPush:
@ -765,6 +768,7 @@ proc collectZCT(gch: var GcHeap): bool =
# In any case, it should be removed from the ZCT. But not # In any case, it should be removed from the ZCT. But not
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!** # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
when logGC: writeCell("zct dealloc cell", c) when logGC: writeCell("zct dealloc cell", c)
track("zct dealloc cell", c, 0)
gcTrace(c, csZctFreed) gcTrace(c, csZctFreed)
# We are about to free the object, call the finalizer BEFORE its # We are about to free the object, call the finalizer BEFORE its
# children are deleted as well, because otherwise the finalizer may # children are deleted as well, because otherwise the finalizer may

View file

@ -86,6 +86,8 @@ type
finalizer: pointer # the finalizer for the type finalizer: pointer # the finalizer for the type
marker: proc (p: pointer, op: int) {.nimcall, benign.} # marker proc for GC marker: proc (p: pointer, op: int) {.nimcall, benign.} # marker proc for GC
deepcopy: proc (p: pointer): pointer {.nimcall, benign.} deepcopy: proc (p: pointer): pointer {.nimcall, benign.}
when defined(nimTypeNames):
name: cstring
PNimType = ptr TNimType PNimType = ptr TNimType
# node.len may be the ``first`` element of a set # node.len may be the ``first`` element of a set

70
lib/system/memtracker.nim Normal file
View file

@ -0,0 +1,70 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2016 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Memory tracking support for Nim.
when not defined(memTracker):
{.error: "Memory tracking support is turned off! Enable memory tracking by passing `--memtracker:on` to the compiler (see the Nim Compiler User Guide for more options).".}
when defined(noSignalHandler):
{.error: "Memory tracking works better with the default signal handler.".}
# We don't want to memtrack the tracking code ...
{.push memtracker: off.}
type
LogEntry* = object
op*: cstring
address*: pointer
size*: int
file*: cstring
line*: int
TrackLog* = object
count*: int
disabled: bool
data*: array[4000, LogEntry]
TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], locks: 0.}
var
gLog*: TrackLog
gLogger*: TrackLogger = proc (log: TrackLog) = discard
proc setTrackLogger*(logger: TrackLogger) =
gLogger = logger
proc addEntry(entry: LogEntry) =
if not gLog.disabled:
if gLog.count > high(gLog.data):
gLogger(gLog)
gLog.count = 0
gLog.data[gLog.count] = entry
inc gLog.count
proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.compilerProc.} =
addEntry LogEntry(op: "write", address: address,
size: size, file: file, line: line)
proc memTrackerOp*(op: cstring; address: pointer; size: int) =
addEntry LogEntry(op: op, address: address, size: size,
file: "", line: 0)
proc memTrackerDisable*() =
gLog.disabled = true
proc memTrackerEnable*() =
gLog.disabled = false
proc logPendingOps() {.noconv.} =
# forward declared and called from Nim's signal handler.
gLogger(gLog)
gLog.count = 0
addQuitProc logPendingOps
{.pop.}

View file

@ -263,27 +263,32 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
result.len = newLen result.len = newLen
# --------------- other string routines ---------------------------------- # --------------- other string routines ----------------------------------
proc nimIntToStr(x: int): string {.compilerRtl.} = proc add*(result: var string; x: int64) =
result = newString(sizeof(x)*4) let base = result.len
setLen(result, base + sizeof(x)*4)
var i = 0 var i = 0
var y = x var y = x
while true: while true:
var d = y div 10 var d = y div 10
result[i] = chr(abs(int(y - d*10)) + ord('0')) result[base+i] = chr(abs(int(y - d*10)) + ord('0'))
inc(i) inc(i)
y = d y = d
if y == 0: break if y == 0: break
if x < 0: if x < 0:
result[i] = '-' result[base+i] = '-'
inc(i) inc(i)
setLen(result, i) setLen(result, base+i)
# mirror the string: # mirror the string:
for j in 0..i div 2 - 1: for j in 0..i div 2 - 1:
swap(result[j], result[i-j-1]) swap(result[base+j], result[base+i-j-1])
proc nimFloatToStr(f: float): string {.compilerproc.} = proc nimIntToStr(x: int): string {.compilerRtl.} =
result = newStringOfCap(sizeof(x)*4)
result.add x
proc add*(result: var string; x: float) =
var buf: array[0..64, char] var buf: array[0..64, char]
var n: int = c_sprintf(buf, "%.16g", f) var n: int = c_sprintf(buf, "%.16g", x)
var hasDot = false var hasDot = false
for i in 0..n-1: for i in 0..n-1:
if buf[i] == ',': if buf[i] == ',':
@ -298,14 +303,18 @@ proc nimFloatToStr(f: float): string {.compilerproc.} =
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced. # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced.
# We want to get rid of these here: # We want to get rid of these here:
if buf[n-1] in {'n', 'N'}: if buf[n-1] in {'n', 'N'}:
result = "nan" result.add "nan"
elif buf[n-1] == 'F': elif buf[n-1] == 'F':
if buf[0] == '-': if buf[0] == '-':
result = "-inf" result.add "-inf"
else: else:
result = "inf" result.add "inf"
else: else:
result = $buf result.add buf
proc nimFloatToStr(f: float): string {.compilerproc.} =
result = newStringOfCap(8)
result.add f
proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
importc: "strtod", header: "<stdlib.h>", noSideEffect.} importc: "strtod", header: "<stdlib.h>", noSideEffect.}
@ -469,22 +478,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
number = c_strtod(t, nil) number = c_strtod(t, nil)
proc nimInt64ToStr(x: int64): string {.compilerRtl.} = proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
result = newString(sizeof(x)*4) result = newStringOfCap(sizeof(x)*4)
var i = 0 result.add x
var y = x
while true:
var d = y div 10
result[i] = chr(abs(int(y - d*10)) + ord('0'))
inc(i)
y = d
if y == 0: break
if x < 0:
result[i] = '-'
inc(i)
setLen(result, i)
# mirror the string:
for j in 0..i div 2 - 1:
swap(result[j], result[i-j-1])
proc nimBoolToStr(x: bool): string {.compilerRtl.} = proc nimBoolToStr(x: bool): string {.compilerRtl.} =
return if x: "true" else: "false" return if x: "true" else: "false"

View file

@ -9,9 +9,9 @@
include "system/inclrtl" include "system/inclrtl"
import os, oids, tables, strutils, times, heapqueue import os, oids, tables, strutils, times, heapqueue, lists
import nativesockets, net, queues import nativesockets, net, deques
export Port, SocketFlag export Port, SocketFlag
@ -135,7 +135,7 @@ include "../includes/asyncfutures"
type type
PDispatcherBase = ref object of RootRef PDispatcherBase = ref object of RootRef
timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]] timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
callbacks: Queue[proc ()] callbacks: Deque[proc ()]
proc processTimers(p: PDispatcherBase) {.inline.} = proc processTimers(p: PDispatcherBase) {.inline.} =
while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt: while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt:
@ -143,7 +143,7 @@ proc processTimers(p: PDispatcherBase) {.inline.} =
proc processPendingCallbacks(p: PDispatcherBase) = proc processPendingCallbacks(p: PDispatcherBase) =
while p.callbacks.len > 0: while p.callbacks.len > 0:
var cb = p.callbacks.dequeue() var cb = p.callbacks.popFirst()
cb() cb()
proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} = proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
@ -729,7 +729,7 @@ when defined(windows) or defined(nimdoc):
var lpOutputBuf = newString(lpOutputLen) var lpOutputBuf = newString(lpOutputLen)
var dwBytesReceived: Dword var dwBytesReceived: Dword
let dwReceiveDataLength = 0.Dword # We don't want any data to be read. let dwReceiveDataLength = 0.Dword # We don't want any data to be read.
let dwLocalAddressLength = Dword(sizeof (Sockaddr_in) + 16) let dwLocalAddressLength = Dword(sizeof(Sockaddr_in) + 16)
let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in) + 16) let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in) + 16)
template completeAccept() {.dirty.} = template completeAccept() {.dirty.} =
@ -1095,9 +1095,11 @@ else:
AsyncFD* = distinct cint AsyncFD* = distinct cint
Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.} Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
DoublyLinkedListRef = ref DoublyLinkedList[Callback]
AsyncData = object AsyncData = object
readCB: Callback readCBs: DoublyLinkedListRef
writeCB: Callback writeCBs: DoublyLinkedListRef
AsyncEvent* = distinct SelectEvent AsyncEvent* = distinct SelectEvent
@ -1112,7 +1114,7 @@ else:
new result new result
result.selector = newSelector[AsyncData]() result.selector = newSelector[AsyncData]()
result.timers.newHeapQueue() result.timers.newHeapQueue()
result.callbacks = initQueue[proc ()](64) result.callbacks = initDeque[proc ()](64)
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
proc getGlobalDispatcher*(): PDispatcher = proc getGlobalDispatcher*(): PDispatcher =
@ -1121,7 +1123,10 @@ else:
proc register*(fd: AsyncFD) = proc register*(fd: AsyncFD) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = AsyncData() var data = AsyncData(
readCBs: DoublyLinkedListRef(),
writeCBs: DoublyLinkedListRef()
)
p.selector.registerHandle(fd.SocketHandle, {}, data) p.selector.registerHandle(fd.SocketHandle, {}, data)
proc newAsyncNativeSocket*(domain: cint, sockType: cint, proc newAsyncNativeSocket*(domain: cint, sockType: cint,
@ -1156,8 +1161,9 @@ else:
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var newEvents = {Event.Read} var newEvents = {Event.Read}
withData(p.selector, fd.SocketHandle, adata) do: withData(p.selector, fd.SocketHandle, adata) do:
adata.readCB = cb adata.readCBs[].append(cb)
if adata.writeCB != nil: newEvents.incl(Event.Read)
if not isNil(adata.writeCBs.head):
newEvents.incl(Event.Write) newEvents.incl(Event.Write)
do: do:
raise newException(ValueError, "File descriptor not registered.") raise newException(ValueError, "File descriptor not registered.")
@ -1167,8 +1173,9 @@ else:
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var newEvents = {Event.Write} var newEvents = {Event.Write}
withData(p.selector, fd.SocketHandle, adata) do: withData(p.selector, fd.SocketHandle, adata) do:
adata.writeCB = cb adata.writeCBs[].append(cb)
if adata.readCB != nil: newEvents.incl(Event.Write)
if not isNil(adata.readCBs.head):
newEvents.incl(Event.Read) newEvents.incl(Event.Read)
do: do:
raise newException(ValueError, "File descriptor not registered.") raise newException(ValueError, "File descriptor not registered.")
@ -1195,31 +1202,32 @@ else:
let events = keys[i].events let events = keys[i].events
if Event.Read in events or events == {Event.Error}: if Event.Read in events or events == {Event.Error}:
let cb = keys[i].data.readCB for node in keys[i].data.readCBs[].nodes():
if cb != nil: let cb = node.value
if cb(fd.AsyncFD): if cb != nil:
p.selector.withData(fd, adata) do: if cb(fd.AsyncFD):
if adata.readCB == cb: keys[i].data.readCBs[].remove(node)
adata.readCB = nil else:
break
if Event.Write in events or events == {Event.Error}: if Event.Write in events or events == {Event.Error}:
let cb = keys[i].data.writeCB for node in keys[i].data.writeCBs[].nodes():
if cb != nil: let cb = node.value
if cb(fd.AsyncFD): if cb != nil:
p.selector.withData(fd, adata) do: if cb(fd.AsyncFD):
if adata.writeCB == cb: keys[i].data.writeCBs[].remove(node)
adata.writeCB = nil else:
break
when supportedPlatform: when supportedPlatform:
if (customSet * events) != {}: if (customSet * events) != {}:
let cb = keys[i].data.readCB for node in keys[i].data.readCBs[].nodes():
doAssert(cb != nil) let cb = node.value
custom = true doAssert(cb != nil)
if cb(fd.AsyncFD): custom = true
p.selector.withData(fd, adata) do: if cb(fd.AsyncFD):
if adata.readCB == cb: keys[i].data.readCBs[].remove(node)
adata.readCB = nil p.selector.unregister(fd)
p.selector.unregister(fd)
# because state `data` can be modified in callback we need to update # because state `data` can be modified in callback we need to update
# descriptor events with currently registered callbacks. # descriptor events with currently registered callbacks.
@ -1227,8 +1235,8 @@ else:
var update = false var update = false
var newEvents: set[Event] = {} var newEvents: set[Event] = {}
p.selector.withData(fd, adata) do: p.selector.withData(fd, adata) do:
if adata.readCB != nil: incl(newEvents, Event.Read) if not isNil(adata.readCBs.head): incl(newEvents, Event.Read)
if adata.writeCB != nil: incl(newEvents, Event.Write) if not isNil(adata.writeCBs.head): incl(newEvents, Event.Write)
update = true update = true
if update: if update:
p.selector.updateHandle(fd, newEvents) p.selector.updateHandle(fd, newEvents)
@ -1491,21 +1499,33 @@ else:
## ``oneshot`` - if ``true`` only one event will be dispatched, ## ``oneshot`` - if ``true`` only one event will be dispatched,
## if ``false`` continuous events every ``timeout`` milliseconds. ## if ``false`` continuous events every ``timeout`` milliseconds.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = AsyncData(readCB: cb) var data = AsyncData(
readCBs: DoublyLinkedListRef(),
writeCBs: DoublyLinkedListRef()
)
data.readCBs[].append(cb)
p.selector.registerTimer(timeout, oneshot, data) p.selector.registerTimer(timeout, oneshot, data)
proc addSignal*(signal: int, cb: Callback) = proc addSignal*(signal: int, cb: Callback) =
## Start watching signal ``signal``, and when signal appears, call the ## Start watching signal ``signal``, and when signal appears, call the
## callback ``cb``. ## callback ``cb``.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = AsyncData(readCB: cb) var data = AsyncData(
readCBs: DoublyLinkedListRef(),
writeCBs: DoublyLinkedListRef()
)
data.readCBs[].append(cb)
p.selector.registerSignal(signal, data) p.selector.registerSignal(signal, data)
proc addProcess*(pid: int, cb: Callback) = proc addProcess*(pid: int, cb: Callback) =
## Start watching for process exit with pid ``pid``, and then call ## Start watching for process exit with pid ``pid``, and then call
## the callback ``cb``. ## the callback ``cb``.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = AsyncData(readCB: cb) var data = AsyncData(
readCBs: DoublyLinkedListRef(),
writeCBs: DoublyLinkedListRef()
)
data.readCBs[].append(cb)
p.selector.registerProcess(pid, data) p.selector.registerProcess(pid, data)
proc newAsyncEvent*(): AsyncEvent = proc newAsyncEvent*(): AsyncEvent =
@ -1524,7 +1544,11 @@ else:
## Start watching for event ``ev``, and call callback ``cb``, when ## Start watching for event ``ev``, and call callback ``cb``, when
## ev will be set to signaled state. ## ev will be set to signaled state.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = AsyncData(readCB: cb) var data = AsyncData(
readCBs: DoublyLinkedListRef(),
writeCBs: DoublyLinkedListRef()
)
data.readCBs[].append(cb)
p.selector.registerEvent(SelectEvent(ev), data) p.selector.registerEvent(SelectEvent(ev), data)
proc sleepAsync*(ms: int): Future[void] = proc sleepAsync*(ms: int): Future[void] =
@ -1591,7 +1615,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
## **Note**: This procedure is mostly used for testing. You likely want to ## **Note**: This procedure is mostly used for testing. You likely want to
## use ``asyncnet.recvLine`` instead. ## use ``asyncnet.recvLine`` instead.
template addNLIfEmpty(): stmt = template addNLIfEmpty(): typed =
if result.len == 0: if result.len == 0:
result.add("\c\L") result.add("\c\L")
@ -1614,7 +1638,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
proc callSoon*(cbproc: proc ()) = proc callSoon*(cbproc: proc ()) =
## Schedule `cbproc` to be called as soon as possible. ## Schedule `cbproc` to be called as soon as possible.
## The callback is called when control returns to the event loop. ## The callback is called when control returns to the event loop.
getGlobalDispatcher().callbacks.enqueue(cbproc) getGlobalDispatcher().callbacks.addLast(cbproc)
proc runForever*() = proc runForever*() =
## Begins a never ending global dispatcher poll loop. ## Begins a never ending global dispatcher poll loop.

View file

@ -261,11 +261,14 @@ proc ERR_error_string*(e: cInt, buf: cstring): cstring{.cdecl,
proc ERR_get_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.} proc ERR_get_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.}
proc ERR_peek_last_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.} proc ERR_peek_last_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.}
proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSSL_add_all_algorithms_conf".} when defined(android):
template OpenSSL_add_all_algorithms*() = discard
else:
proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSSL_add_all_algorithms_conf".}
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.} proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
when not useWinVersion and not defined(macosx): when not useWinVersion and not defined(macosx) and not defined(android):
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
dynlib: DLLUtilName, importc.} dynlib: DLLUtilName, importc.}
@ -279,7 +282,7 @@ when not useWinVersion and not defined(macosx):
if p != nil: dealloc(p) if p != nil: dealloc(p)
proc CRYPTO_malloc_init*() = proc CRYPTO_malloc_init*() =
when not useWinVersion and not defined(macosx): when not useWinVersion and not defined(macosx) and not defined(android):
CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper) CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper)
proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{. proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{.

View file

@ -96,10 +96,6 @@ const
#define SQLITE_TRANSIENT ((void(*)(void *))-1) #define SQLITE_TRANSIENT ((void(*)(void *))-1)
SQLITE_DETERMINISTIC* = 0x800 SQLITE_DETERMINISTIC* = 0x800
const
SQLITE_STATIC* = nil
SQLITE_TRANSIENT* = cast[pointer](- 1)
type type
Sqlite3 {.pure, final.} = object Sqlite3 {.pure, final.} = object
PSqlite3* = ptr Sqlite3 PSqlite3* = ptr Sqlite3
@ -114,7 +110,7 @@ type
Callback* = proc (para1: pointer, para2: int32, para3, Callback* = proc (para1: pointer, para2: int32, para3,
para4: cstringArray): int32{.cdecl.} para4: cstringArray): int32{.cdecl.}
Tbind_destructor_func* = proc (para1: pointer){.cdecl.} Tbind_destructor_func* = proc (para1: pointer){.cdecl, locks: 0, tags: [].}
Create_function_step_func* = proc (para1: Pcontext, para2: int32, Create_function_step_func* = proc (para1: Pcontext, para2: int32,
para3: PValueArg){.cdecl.} para3: PValueArg){.cdecl.}
Create_function_func_func* = proc (para1: Pcontext, para2: int32, Create_function_func_func* = proc (para1: Pcontext, para2: int32,
@ -132,6 +128,10 @@ type
Tresult_func: Result_func, Tcreate_collation_func: Create_collation_func, Tresult_func: Result_func, Tcreate_collation_func: Create_collation_func,
Tcollation_needed_func: Collation_needed_func].} Tcollation_needed_func: Collation_needed_func].}
const
SQLITE_STATIC* = nil
SQLITE_TRANSIENT* = cast[Tbind_destructor_func](-1)
proc close*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_close".} proc close*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_close".}
proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer, proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer,
errmsg: var cstring): int32{.cdecl, dynlib: Lib, errmsg: var cstring): int32{.cdecl, dynlib: Lib,
@ -234,7 +234,8 @@ proc bind_parameter_name*(para1: Pstmt, para2: int32): cstring{.cdecl,
dynlib: Lib, importc: "sqlite3_bind_parameter_name".} dynlib: Lib, importc: "sqlite3_bind_parameter_name".}
proc bind_parameter_index*(para1: Pstmt, zName: cstring): int32{.cdecl, proc bind_parameter_index*(para1: Pstmt, zName: cstring): int32{.cdecl,
dynlib: Lib, importc: "sqlite3_bind_parameter_index".} dynlib: Lib, importc: "sqlite3_bind_parameter_index".}
#function sqlite3_clear_bindings(_para1:Psqlite3_stmt):longint;cdecl; external Sqlite3Lib name 'sqlite3_clear_bindings'; proc clear_bindings*(para1: Pstmt): int32 {.cdecl,
dynlib: Lib, importc: "sqlite3_clear_bindings".}
proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib, proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_column_count".} importc: "sqlite3_column_count".}
proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib, proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,

View file

@ -3,7 +3,7 @@ discard """
WARNING: false first assertion from bar WARNING: false first assertion from bar
ERROR: false second assertion from bar ERROR: false second assertion from bar
-1 -1
tests/assert/tfailedassert.nim:27 false assertion from foo tfailedassert.nim:27 false assertion from foo
''' '''
""" """

View file

@ -112,7 +112,7 @@ block orderedTableTest2:
block countTableTest1: block countTableTest1:
var s = data.toTable var s = data.toTable
var t = initCountTable[string]() var t = initCountTable[string]()
for k in s.keys: t.inc(k) for k in s.keys: t.inc(k)
for k in t.keys: assert t[k] == 1 for k in t.keys: assert t[k] == 1
t.inc("90", 3) t.inc("90", 3)
@ -167,6 +167,29 @@ block mpairsTableTest1:
block SyntaxTest: block SyntaxTest:
var x = toTable[int, string]({:}) var x = toTable[int, string]({:})
block zeroHashKeysTest:
proc doZeroHashValueTest[T, K, V](t: T, nullHashKey: K, value: V) =
let initialLen = t.len
var testTable = t
testTable[nullHashKey] = value
assert testTable[nullHashKey] == value
assert testTable.len == initialLen + 1
testTable.del(nullHashKey)
assert testTable.len == initialLen
# with empty table
doZeroHashValueTest(toTable[int,int]({:}), 0, 42)
doZeroHashValueTest(toTable[string,int]({:}), "", 23)
doZeroHashValueTest(toOrderedTable[int,int]({:}), 0, 42)
doZeroHashValueTest(toOrderedTable[string,int]({:}), "", 23)
# with non-empty table
doZeroHashValueTest(toTable[int,int]({1:2}), 0, 42)
doZeroHashValueTest(toTable[string,string]({"foo": "bar"}), "", "zero")
doZeroHashValueTest(toOrderedTable[int,int]({3:4}), 0, 42)
doZeroHashValueTest(toOrderedTable[string,string]({"egg": "sausage"}),
"", "spam")
# Until #4448 is fixed, these tests will fail # Until #4448 is fixed, these tests will fail
when false: when false:
block clearTableTest: block clearTableTest:

View file

@ -40,7 +40,7 @@ type
trailDelay*: float trailDelay*: float
body: chipmunk.PBody body: chipmunk.PBody
shape: chipmunk.PShape shape: chipmunk.PShape
import vehicles include vehicles
const const
LGrabbable* = (1 shl 0).TLayers LGrabbable* = (1 shl 0).TLayers
LBorders* = (1 shl 1).TLayers LBorders* = (1 shl 1).TLayers

View file

@ -1,6 +1,6 @@
import import
sfml, chipmunk, sfml, chipmunk,
sg_assets, sfml_stuff, "../keineschweine" sg_assets, sfml_stuff#, "../keineschweine"
proc accel*(obj: PVehicle, dt: float) = proc accel*(obj: PVehicle, dt: float) =

View file

@ -1,5 +1,5 @@
discard """ discard """
errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tests/method/tmapper.nim(22,7)" errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tmapper.nim(22,7)"
line: 25 line: 25
""" """

View file

@ -18,15 +18,15 @@ echo v[2]
# bug #569 # bug #569
import queues import deques
type type
TWidget = object TWidget = object
names: Queue[string] names: Deque[string]
var w = TWidget(names: initQueue[string]()) var w = TWidget(names: initDeque[string]())
add(w.names, "Whopie") addLast(w.names, "Whopie")
for n in w.names: echo(n) for n in w.names: echo(n)

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "tests/reject/trecincb.nim" file: "trecincb.nim"
line: 9 line: 9
errormsg: "recursive dependency: 'tests/modules/trecincb.nim'" errormsg: "recursive dependency: 'trecincb.nim'"
""" """
# Test recursive includes # Test recursive includes

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "trecincb.nim" file: "trecincb.nim"
line: 9 line: 9
errormsg: "recursive dependency: 'tests/modules/trecincb.nim'" errormsg: "recursive dependency: 'trecincb.nim'"
""" """
# Test recursive includes # Test recursive includes

View file

@ -1,2 +1,8 @@
discard """
file: "mrecmod.nim"
line: 1
errormsg: "recursive module dependency detected"
disabled: true
"""
# recursive module # recursive module
import mrecmod import mrecmod

View file

@ -1,10 +1,13 @@
discard """
output: "4"
"""
type type
T1* = int # Module A exports the type ``T1`` T1* = int # Module A exports the type ``T1``
import mrecmod2 # the compiler starts parsing B import mrecmod2 # the compiler starts parsing B
# the manual says this should work
proc main() = proc main() =
var i = p(3) # works because B has been parsed completely here echo p(3) # works because B has been parsed completely here
main() main()

View file

@ -98,13 +98,13 @@ block:
x += 10 x += 10
echo sl echo sl
import queues import deques
block: block:
var q = initQueue[int]() var q = initDeque[int]()
q.add(1) q.addLast(1)
q.add(2) q.addLast(2)
q.add(3) q.addLast(3)
for x in q.mitems: for x in q.mitems:
x += 10 x += 10
echo q echo q

View file

@ -96,6 +96,10 @@ parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-dd'T'HH:mm:ss'Z'zzz",
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
parseTest("2006-01-12T15:04:05.999999999Z-07:00", parseTest("2006-01-12T15:04:05.999999999Z-07:00",
"yyyy-MM-ddTHH:mm:ss.999999999Zzzz", "2006-01-12T22:04:05+00:00", 11) "yyyy-MM-ddTHH:mm:ss.999999999Zzzz", "2006-01-12T22:04:05+00:00", 11)
for tzFormat in ["z", "zz", "zzz"]:
# formatting timezone as 'Z' for UTC
parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat,
"2001-01-12T22:04:05+00:00", 11)
# Kitchen = "3:04PM" # Kitchen = "3:04PM"
parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00") parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00")
#when not defined(testing): #when not defined(testing):
@ -190,3 +194,36 @@ doAssert cmpTimeNoSideEffect(0.fromSeconds, 0.fromSeconds)
let seqA: seq[Time] = @[] let seqA: seq[Time] = @[]
let seqB: seq[Time] = @[] let seqB: seq[Time] = @[]
doAssert seqA == seqB doAssert seqA == seqB
for tz in [
(0, "+0", "+00", "+00:00"), # UTC
(-3600, "+1", "+01", "+01:00"), # CET
(-39600, "+11", "+11", "+11:00"), # two digits
(-1800, "+0", "+00", "+00:30"), # half an hour
(7200, "-2", "-02", "-02:00"), # positive
(38700, "-10", "-10", "-10:45")]: # positive with three quaters hour
let ti = TimeInfo(monthday: 1, timezone: tz[0])
doAssert ti.format("z") == tz[1]
doAssert ti.format("zz") == tz[2]
doAssert ti.format("zzz") == tz[3]
block dstTest:
let nonDst = TimeInfo(year: 2015, month: mJan, monthday: 01, yearday: 0,
weekday: dThu, hour: 00, minute: 00, second: 00, isDST: false, timezone: 0)
var dst = nonDst
dst.isDst = true
# note that both isDST == true and isDST == false are valid here because
# DST is in effect on January 1st in some southern parts of Australia.
doAssert nonDst.toTime() - dst.toTime() == 3600
doAssert nonDst.format("z") == "+0"
doAssert dst.format("z") == "+1"
# parsing will set isDST in relation to the local time. We take a date in
# January and one in July to maximize the probability to hit one date with DST
# and one without on the local machine. However, this is not guaranteed.
let
parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
doAssert toTime(parsedJan) == fromSeconds(1451962800)
doAssert toTime(parsedJul) == fromSeconds(1467342000)

View file

@ -0,0 +1,95 @@
discard """
output: "ok"
disabled: "true"
"""
import tables, lists
type
ListTable[K, V] = object
valList: DoublyLinkedList[V]
table: Table[K, DoublyLinkedNode[V]]
ListTableRef*[K, V] = ref ListTable[K, V]
proc initListTable*[K, V](initialSize = 64): ListTable[K, V] =
result.valList = initDoublyLinkedList[V]()
result.table = initTable[K, DoublyLinkedNode[V]]()
proc newListTable*[K, V](initialSize = 64): ListTableRef[K, V] =
new(result)
result[] = initListTable[K, V](initialSize)
proc `[]=`*[K, V](t: var ListTable[K, V], key: K, val: V) =
if key in t.table:
t.table[key].value = val
else:
let node = newDoublyLinkedNode(val)
t.valList.append(node)
t.table[key] = node
proc `[]`*[K, V](t: ListTable[K, V], key: K): var V {.inline.} =
result = t.table[key].value
proc len*[K, V](t: ListTable[K, V]): Natural {.inline.} =
result = t.table.len
iterator values*[K, V](t: ListTable[K, V]): V =
for val in t.valList.items():
yield val
proc `[]=`*[K, V](t: ListTableRef[K, V], key: K, val: V) =
t[][key] = val
proc `[]`*[K, V](t: ListTableRef[K, V], key: K): var V {.inline.} =
t[][key]
proc len*[K, V](t: ListTableRef[K, V]): Natural {.inline.} =
t[].len
iterator values*[K, V](t: ListTableRef[K, V]): V =
for val in t[].values:
yield val
proc main() =
type SomeObj = ref object
for outer in 0..10_000:
let myObj = new(SomeObj)
let table = newListTable[int, SomeObj]()
table[0] = myObj
for i in 1..100:
table[i] = new(SomeObj)
var myObj2: SomeObj
for val in table.values():
if myObj2.isNil:
myObj2 = val
assert(myObj == myObj2) # passes
var tableCopy: ListTableRef[int, SomeObj]
deepCopy(tableCopy, table)
let myObjCopy = tableCopy[0]
var myObjCopy2: SomeObj = nil
for val in tableCopy.values():
if myObjCopy2.isNil:
myObjCopy2 = val
#echo cast[int](myObj)
#echo cast[int](myObjCopy)
#echo cast[int](myObjCopy2)
assert(myObjCopy == myObjCopy2) # fails
type
PtrTable = object
counter, max: int
data: array[0..99, (pointer, pointer)]
assert(sizeof(PtrTable) == 2*sizeof(int)+sizeof(pointer)*2*100)
main()
echo "ok"

View file

@ -14,7 +14,7 @@ import ospaths
# import parseopt # import parseopt
import parseutils import parseutils
# import pegs # import pegs
import queues import deques
import sequtils import sequtils
import strutils import strutils
import subexes import subexes

View file

@ -431,10 +431,10 @@ proc handleCmdLine(cache: IdentCache) =
except OSError: except OSError:
gProjectFull = gProjectName gProjectFull = gProjectName
var p = splitFile(gProjectFull) var p = splitFile(gProjectFull)
gProjectPath = p.dir gProjectPath = canonicalizePath p.dir
gProjectName = p.name gProjectName = p.name
else: else:
gProjectPath = getCurrentDir() gProjectPath = canonicalizePath getCurrentDir()
# Find Nim's prefix dir. # Find Nim's prefix dir.
let binaryPath = findExe("nim") let binaryPath = findExe("nim")

View file

@ -60,17 +60,7 @@
# if currentTab == "index": # if currentTab == "index":
<div id="slideshow"> <div id="slideshow">
<!-- slides --> <!-- slides -->
<div id="slide0" class="active niaslide"> <div id="slide0" class="active codeslide2">
<a href="sponsors.html">
<img src="${rootDir}assets/bountysource/meet_sponsors.png" alt="Meet our BountySource sponsors!"/>
</a>
</div>
<div id="slide1" class="niaslide">
<a href="news/2016_01_27_nim_in_action_is_now_available.html">
<img src="${rootDir}assets/niminaction/banner.jpg" alt="New in Manning Early Access Program: Nim in Action!"/>
</a>
</div>
<div id="slide2" class="codeslide2">
<div> <div>
<h2>Nim is simple..</h2> <h2>Nim is simple..</h2>
<pre> <pre>
@ -104,7 +94,7 @@ p.greet() <span class="cmt"># or greet(p)</span>
</pre> </pre>
</div> </div>
</div> </div>
<div id="slide3" class="codeslide3"> <div id="slide1" class="codeslide3">
<div> <div>
<h2>C FFI is easy in Nim..</h2> <h2>C FFI is easy in Nim..</h2>
<pre> <pre>
@ -136,6 +126,16 @@ runForever()
<p><span class="desc"><b>View in browser at:</b><br>&nbsp;&nbsp;&nbsp;&nbsp;localhost:5000</span></p> <p><span class="desc"><b>View in browser at:</b><br>&nbsp;&nbsp;&nbsp;&nbsp;localhost:5000</span></p>
</div> </div>
</div> </div>
<div id="slide2" class="niaslide">
<a href="news/e030_nim_in_action_in_production.html">
<img src="${rootDir}assets/niminaction/banner2.png" alt="A printed copy of Nim in Action should be available in March 2017!"/>
</a>
</div>
<div id="slide3" class="niaslide">
<a href="sponsors.html">
<img src="${rootDir}assets/bountysource/meet_sponsors.png" alt="Meet our BountySource sponsors!"/>
</a>
</div>
</div> </div>
<div id="slideshow-nav"> <div id="slideshow-nav">
<div id="slideControl0" onclick="slideshow_click(0)" class="active"></div> <div id="slideControl0" onclick="slideshow_click(0)" class="active"></div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -5,7 +5,7 @@ Home
Welcome to Nim Welcome to Nim
-------------- --------------
**Nim** (formerly known as "Nimrod") is a statically typed, imperative **Nim** is a statically typed, imperative
programming language that tries to give the programmer ultimate power without programming language that tries to give the programmer ultimate power without
compromises on runtime efficiency. This means it focuses on compile-time compromises on runtime efficiency. This means it focuses on compile-time
mechanisms in all their various forms. mechanisms in all their various forms.

View file

@ -2,6 +2,9 @@
News News
==== ====
`2016-11-20 Nim in Action is going into production! <news/e030_nim_in_action_in_production.html>`_
===================================
`2016-10-23 Nim Version 0.15.2 released <news/e028_version_0_15_2.html>`_ `2016-10-23 Nim Version 0.15.2 released <news/e028_version_0_15_2.html>`_
=================================== ===================================

View file

@ -35,6 +35,9 @@ Library Additions
- Added new parameter to ``error`` proc of ``macro`` module to provide better - Added new parameter to ``error`` proc of ``macro`` module to provide better
error message error message
- Added new ``deques`` module intended to replace ``queues``.
``deques`` provides a superset of ``queues`` API with clear naming.
``queues`` module is now deprecated and will be removed in the future.
Tool Additions Tool Additions
-------------- --------------

View file

@ -0,0 +1,53 @@
Nim in Action is going into production!
=======================================
.. container:: metadata
Posted by Dominik Picheta on 20/11/2016
.. raw::html
<a href="https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81">
<img src="../assets/niminaction/banner2.png" alt="A printed copy of Nim in Action should be available in March 2017!" width="682"/>
</a>
I am very happy to say that just last week I have put the finishing touches
on Nim in Action. The final manuscript has been submitted to Manning (the book's
publisher), and the printed version is expected to start shipping in March
2017 (give or take 1 month).
The eBook is still available and now contains all of the book's chapters,
including new ones dealing with the foreign function interface and
metaprogramming.
That said, it may still take some time before the eBook is updated with the
latest corrections.
I am incredibly thankful to everyone that purchased the book already. Many of
you have also given me a lot of `brilliant <http://forum.nim-lang.org/t/1978>`_
`feedback <https://forums.manning.com/forums/nim-in-action>`_,
thank you very much for
taking the time to do so. I have done my best to act on this
feedback and I hope you will agree that the book has risen in quality as a
result.
Writing this book has been both exhausting and incredible at the same time.
I look forward
to having a physical copy of it in my hands, and I'm sure many of you do as
well. I can safely say that without your support this book would not have
happened, even if you did not purchase a copy your interest in Nim has made it
possible and I thank you for that.
As always, you can make a purchase on
`Manning's website <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_.
Both eBook's and printed books are available, and purchasing a printed book will
get you an eBook for free.
You can now also pre-order Nim in Action on
`Amazon <https://www.amazon.co.uk/Nim-Action-Dominik-Picheta/dp/1617293431/ref=sr_1_1?ie=UTF8&qid=1479663850&sr=8-1&keywords=nim+in+action>`_!
If you would like updates about the book then please feel free to
follow either `myself <https://twitter.com/d0m96>`_ or
`@nim_lang <https://twitter.com/nim_lang>`_ on Twitter. Finally, if you have any
questions, do get in touch via `Twitter, NimForum,
IRC or Gitter <http://nim-lang.org/community.html>`_.
Thanks for reading!

View file

@ -1,8 +1,18 @@
<a class="news" href="$1news/e030_nim_in_action_in_production.html">
<h4>November 20, 2016</h4>
<p>Nim in Action is going into production!</p>
</a>
<a class="news" href="$1news/e028_version_0_15_2.html"> <a class="news" href="$1news/e028_version_0_15_2.html">
<h4>October 23, 2016</h4> <h4>October 23, 2016</h4>
<p>Nim version 0.15.2 has been released!</p> <p>Nim version 0.15.2 has been released!</p>
</a> </a>
<a class="news" href="$1news/e027_version_0_15_0.html">
<h4>September 30, 2016</h4>
<p>Nim version 0.15.0 has been released!</p>
</a>
<a class="news" href="$1news/e026_survey_results.html"> <a class="news" href="$1news/e026_survey_results.html">
<h4>September 3, 2016</h4> <h4>September 3, 2016</h4>
<p>Nim Community Survey results</p> <p>Nim Community Survey results</p>
@ -13,14 +23,4 @@
<p>BountySource Update: The Road to v1.0</p> <p>BountySource Update: The Road to v1.0</p>
</a> </a>
<a class="news" href="$1news/e024_survey.html">
<h4>June 23, 2016</h4>
<p>Launching the 2016 Nim community survey!</p>
</a>
<a class="news" href="$1news/e023_version_0_14_2.html">
<h4>June 11, 2016</h4>
<p>Nim version 0.14.2 has been released!</p>
</a>
<a href="$1news.html" class="blue">See All News...</a> <a href="$1news.html" class="blue">See All News...</a>

View file

@ -51,7 +51,7 @@ srcdoc2: "pure/ropes;pure/unidecode/unidecode;pure/xmldom;pure/xmldomparser"
srcdoc2: "pure/xmlparser;pure/htmlparser;pure/xmltree;pure/colors;pure/mimetypes" srcdoc2: "pure/xmlparser;pure/htmlparser;pure/xmltree;pure/colors;pure/mimetypes"
srcdoc2: "pure/json;pure/base64;pure/scgi" srcdoc2: "pure/json;pure/base64;pure/scgi"
srcdoc2: "pure/collections/tables;pure/collections/sets;pure/collections/lists" srcdoc2: "pure/collections/tables;pure/collections/sets;pure/collections/lists"
srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/encodings" srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/collections/deques;pure/encodings"
srcdoc2: "pure/events;pure/collections/sequtils;pure/cookies" srcdoc2: "pure/events;pure/collections/sequtils;pure/cookies"
srcdoc2: "pure/memfiles;pure/subexes;pure/collections/critbits" srcdoc2: "pure/memfiles;pure/subexes;pure/collections/critbits"
srcdoc2: "deprecated/pure/asyncio;deprecated/pure/actors;core/locks;core/rlocks;pure/oids;pure/endians;pure/uri" srcdoc2: "deprecated/pure/asyncio;deprecated/pure/actors;core/locks;core/rlocks;pure/oids;pure/endians;pure/uri"