Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf 2017-07-13 04:48:22 +02:00
commit 2b862b74e0
165 changed files with 4572 additions and 920 deletions

View file

@ -225,7 +225,13 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
proc newNimNode*(kind: NimNodeKind,
n: NimNode=nil): NimNode {.magic: "NNewNimNode", noSideEffect.}
lineInfoFrom: NimNode=nil): NimNode
{.magic: "NNewNimNode", noSideEffect.}
## Creates a new AST node of the specified kind.
##
## The ``lineInfoFrom`` parameter is used for line information when the
## produced code crashes. You should ensure that it is set to a node that
## you are transforming.
proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
@ -249,6 +255,11 @@ proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} =
result = newNimNode(nnkStrLit)
result.strVal = s
proc newCommentStmtNode*(s: string): NimNode {.compileTime, noSideEffect.} =
## creates a comment statement node
result = newNimNode(nnkCommentStmt)
result.strVal = s
proc newIntLitNode*(i: BiggestInt): NimNode {.compileTime.} =
## creates a int literal node from `i`
result = newNimNode(nnkIntLit)
@ -269,6 +280,7 @@ proc newIdentNode*(i: string): NimNode {.compileTime.} =
result = newNimNode(nnkIdent)
result.ident = !i
type
BindSymRule* = enum ## specifies how ``bindSym`` behaves
brClosed, ## only the symbols in current scope are bound
@ -426,20 +438,105 @@ proc newLit*(c: char): NimNode {.compileTime.} =
result = newNimNode(nnkCharLit)
result.intVal = ord(c)
proc newLit*(i: BiggestInt): NimNode {.compileTime.} =
proc newLit*(i: int): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkIntLit)
result.intVal = i
proc newLit*(i: int8): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt8Lit)
result.intVal = i
proc newLit*(i: int16): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt16Lit)
result.intVal = i
proc newLit*(i: int32): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt32Lit)
result.intVal = i
proc newLit*(i: int64): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt64Lit)
result.intVal = i
proc newLit*(i: uint): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUIntLit)
result.intVal = BiggestInt(i)
proc newLit*(i: uint8): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt8Lit)
result.intVal = BiggestInt(i)
proc newLit*(i: uint16): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt16Lit)
result.intVal = BiggestInt(i)
proc newLit*(i: uint32): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt32Lit)
result.intVal = BiggestInt(i)
proc newLit*(i: uint64): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt64Lit)
result.intVal = BiggestInt(i)
proc newLit*(b: bool): NimNode {.compileTime.} =
## produces a new boolean literal node.
result = if b: bindSym"true" else: bindSym"false"
proc newLit*(f: BiggestFloat): NimNode {.compileTime.} =
when false:
# the float type is not really a distinct type as described in https://github.com/nim-lang/Nim/issues/5875
proc newLit*(f: float): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloatLit)
result.floatVal = f
proc newLit*(f: float32): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloatLit)
result = newNimNode(nnkFloat32Lit)
result.floatVal = f
proc newLit*(f: float64): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloat64Lit)
result.floatVal = f
when compiles(float128):
proc newLit*(f: float128): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloat128Lit)
result.floatVal = f
proc newLit*(arg: object): NimNode {.compileTime.} =
result = nnkObjConstr.newTree(arg.type.getTypeInst[1])
for a, b in arg.fieldPairs:
result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) )
proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} =
result = nnkBracket.newTree
for x in arg:
result.add newLit(x)
proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
result = nnkBracket.newTree
for x in arg:
result.add newLit(x)
result = nnkPrefix.newTree(bindSym"@", result)
proc newLit*(arg: tuple): NimNode {.compileTime.} =
result = nnkPar.newTree
for a,b in arg.fieldPairs:
result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) )
proc newLit*(s: string): NimNode {.compileTime.} =
## produces a new string literal node.
result = newNimNode(nnkStrLit)
@ -744,6 +841,8 @@ proc `$`*(node: NimNode): string {.compileTime.} =
result = $node[0]
of nnkAccQuoted:
result = $node[0]
of nnkCommentStmt:
result = node.strVal
else:
badNodeKind node.kind, "$"

View file

@ -79,6 +79,9 @@ const
DT_SOCK* = 12 ## UNIX domain socket.
DT_WHT* = 14
# Special types
type Sighandler = proc (a: cint) {.noconv.}
# Platform specific stuff
when defined(linux) and defined(amd64):
@ -86,6 +89,9 @@ when defined(linux) and defined(amd64):
else:
include posix_other
# There used to be this name in posix.nim a long time ago, not sure why!
{.deprecated: [cSIG_HOLD: SIG_HOLD].}
when not defined(macosx):
proc st_atime*(s: Stat): Time {.inline.} =
## Second-granularity time of last access
@ -659,7 +665,7 @@ proc sighold*(a1: cint): cint {.importc, header: "<signal.h>".}
proc sigignore*(a1: cint): cint {.importc, header: "<signal.h>".}
proc siginterrupt*(a1, a2: cint): cint {.importc, header: "<signal.h>".}
proc sigismember*(a1: var Sigset, a2: cint): cint {.importc, header: "<signal.h>".}
proc signal*(a1: cint, a2: proc (x: cint) {.noconv.}) {.
proc signal*(a1: cint, a2: Sighandler) {.
importc, header: "<signal.h>".}
proc sigpause*(a1: cint): cint {.importc, header: "<signal.h>".}
proc sigpending*(a1: var Sigset): cint {.importc, header: "<signal.h>".}

View file

@ -36,6 +36,9 @@ type
{.deprecated: [TSocketHandle: SocketHandle].}
# not detected by detect.nim, guarded by #ifdef __USE_UNIX98 in glibc
const SIG_HOLD* = cast[SigHandler](2)
type
Timespec* {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object ## struct timespec

View file

@ -399,6 +399,9 @@ const SS_ONSTACK* = cint(1)
const SS_DISABLE* = cint(2)
const MINSIGSTKSZ* = cint(2048)
const SIGSTKSZ* = cint(8192)
const SIG_DFL* = cast[Sighandler](0)
const SIG_ERR* = cast[Sighandler](-1)
const SIG_IGN* = cast[Sighandler](1)
# <sys/ipc.h>
const IPC_CREAT* = cint(512)

View file

@ -414,6 +414,10 @@ var SS_ONSTACK* {.importc: "SS_ONSTACK", header: "<signal.h>".}: cint
var SS_DISABLE* {.importc: "SS_DISABLE", header: "<signal.h>".}: cint
var MINSIGSTKSZ* {.importc: "MINSIGSTKSZ", header: "<signal.h>".}: cint
var SIGSTKSZ* {.importc: "SIGSTKSZ", header: "<signal.h>".}: cint
var SIG_HOLD* {.importc: "SIG_HOLD", header: "<signal.h>".}: Sighandler
var SIG_DFL* {.importc: "SIG_DFL", header: "<signal.h>".}: Sighandler
var SIG_ERR* {.importc: "SIG_ERR", header: "<signal.h>".}: Sighandler
var SIG_IGN* {.importc: "SIG_IGN", header: "<signal.h>".}: Sighandler
# <sys/ipc.h>
var IPC_CREAT* {.importc: "IPC_CREAT", header: "<sys/ipc.h>".}: cint

View file

@ -163,8 +163,8 @@ include includes/asyncfutures
type
PDispatcherBase = ref object of RootRef
timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
callbacks: Deque[proc ()]
timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
callbacks*: Deque[proc ()]
proc processTimers(p: PDispatcherBase) {.inline.} =
#Process just part if timers at a step

View file

@ -127,7 +127,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
i.inc protocol.parseInt(result.minor, i)
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
client.send("HTTP/1.1 " & status & "\c\L")
client.send("HTTP/1.1 " & status & "\c\L\c\L")
proc processClient(client: AsyncSocket, address: string,
callback: proc (request: Request):

View file

@ -301,13 +301,13 @@ proc verifyReturnType(typeName: string) {.compileTime.} =
proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
## This macro transforms a single procedure into a closure iterator.
## The ``async`` macro supports a stmtList holding multiple async procedures.
if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef}:
if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
error("Cannot transform this node kind into an async proc." &
" proc/method definition or lambda node expected.")
hint("Processing " & prc[0].getName & " as an async proc.")
let prcName = prc.name.getName
let returnType = prc[3][0]
let returnType = prc.params[0]
var baseType: NimNode
# Verify that the return type is a Future[T]
if returnType.kind == nnkBracketExpr:
@ -326,9 +326,9 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
let subtypeIsVoid = returnType.kind == nnkEmpty or
(baseType.kind == nnkIdent and returnType[1].ident == !"void")
let futureVarIdents = getFutureVarIdents(prc[3])
let futureVarIdents = getFutureVarIdents(prc.params)
var outerProcBody = newNimNode(nnkStmtList, prc[6])
var outerProcBody = newNimNode(nnkStmtList, prc.body)
# -> var retFuture = newFuture[T]()
var retFutureSym = genSym(nskVar, "retFuture")
@ -338,10 +338,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
outerProcBody.add(
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr, prc[6]).add(
newNimNode(nnkBracketExpr, prc.body).add(
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
subRetType),
newLit(prc[0].getName)))) # Get type from return type of this proc
newLit(prcName)))) # Get type from return type of this proc
# -> iterator nameIter(): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.}
@ -349,8 +349,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
# -> {.pop.}
# -> <proc_body>
# -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid,
var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
futureVarIdents, nil)
# don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty:
@ -362,7 +362,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
newIdentNode("warning"), newIdentNode("resultshadowed")),
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add(
procBody.insert(1, newNimNode(nnkVarSection, prc.body).add(
newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
procBody.insert(2, newNimNode(nnkPragma).add(
@ -377,35 +377,32 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
closureIterator.addPragma(newIdentNode("closure"))
closureIterator.addPragma(newIdentNode("gcsafe"))
outerProcBody.add(closureIterator)
# -> createCb(retFuture)
#var cbName = newIdentNode("cb")
var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prc[0].getName),
newStrLitNode(prcName),
createFutureVarCompletions(futureVarIdents, nil))
outerProcBody.add procCb
# -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym)
outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)
result = prc
# Remove the 'async' pragma.
for i in 0 .. <result[4].len:
if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
result[4].del(i)
result[4] = newEmptyNode()
if subtypeIsVoid:
# Add discardable pragma.
if returnType.kind == nnkEmpty:
# Add Future[void]
result[3][0] = parseExpr("Future[void]")
result.params[0] = parseExpr("Future[void]")
if procBody.kind != nnkEmpty:
result[6] = outerProcBody
result.body = outerProcBody
#echo(treeRepr(result))
#if prc[0].getName == "recvLineInto":
#if prcName == "recvLineInto":
# echo(toStrLit(result))
macro async*(prc: untyped): untyped =
@ -529,8 +526,6 @@ macro multisync*(prc: untyped): untyped =
##
## The generated async procedures use the ``async`` macro, whereas the
## generated synchronous procedures simply strip off the ``await`` calls.
hint("Processing " & prc[0].getName & " as a multisync proc.")
let (sync, asyncPrc) = splitProc(prc)
result = newStmtList()
result.add(asyncSingleProc(asyncPrc))

View file

@ -533,15 +533,13 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
else:
var c = ""
while true:
let recvFut = recv(socket, 1, flags)
c = recvFut.read()
c = await recv(socket, 1, flags)
if c.len == 0:
resString.mget.setLen(0)
resString.complete()
return
if c == "\r":
let recvFut = recv(socket, 1, flags) # Skip \L
c = recvFut.read()
c = await recv(socket, 1, flags) # Skip \L
assert c == "\L"
addNLIfEmpty()
resString.complete()

View file

@ -590,8 +590,11 @@ proc enlarge[A, B](t: var OrderedTable[A, B]) =
swap(t.data, n)
while h >= 0:
var nxt = n[h].next
if isFilled(n[h].hcode):
var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode)
let eh = n[h].hcode
if isFilled(eh):
var j: Hash = eh and maxHash(t)
while isFilled(t.data[j].hcode):
j = nextTry(j, maxHash(t))
rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
h = nxt

View file

@ -69,4 +69,4 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
result = affinitySpaceTotal().int
else:
result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 1
if result <= 0: result = 0

View file

@ -158,7 +158,7 @@ proc detectOsImpl(d: Distribution): bool =
of Distribution.ArchLinux:
result = "arch" in toLowerAscii(uname())
of Distribution.OpenSUSE:
result = "suse" in toLowerAscii(uname())
result = "suse" in toLowerAscii(uname()) or "suse" in toLowerAscii(release())
of Distribution.GoboLinux:
result = "-Gobo " in uname()
of Distribution.OpenMandriva:

View file

@ -139,9 +139,14 @@ proc hash*(x: cstring): Hash =
## efficient hashing of null-terminated strings
var h: Hash = 0
var i = 0
while x[i] != 0.char:
h = h !& ord(x[i])
inc i
when defined(js):
while i < x.len:
h = h !& ord(x[i])
inc i
else:
while x[i] != 0.char:
h = h !& ord(x[i])
inc i
result = !$h
proc hash*(sBuf: string, sPos, ePos: int): Hash =

View file

@ -62,7 +62,8 @@
## let body = %*{
## "data": "some text"
## }
## echo client.request("http://some.api", httpMethod = HttpPost, body = $body)
## let response = client.request("http://some.api", httpMethod = HttpPost, body = $body)
## echo response.status
##
## Progress reporting
## ==================

View file

@ -3,7 +3,7 @@ template newAsyncNativeSocketImpl(domain, sockType, protocol) =
if handle == osInvalidSocket:
raiseOSError(osLastError())
handle.setBlocking(false)
when defined(macosx):
when defined(macosx) and not defined(nimdoc):
handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
result = handle.AsyncFD
register(result)

View file

@ -1001,7 +1001,7 @@ proc escapeJson*(s: string; result: var string) =
result.add("\"")
for x in runes(s):
var r = int(x)
if r >= 32 and r <= 127:
if r >= 32 and r <= 126:
var c = chr(r)
case c
of '"': result.add("\\\"")
@ -1608,7 +1608,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
result = processType(newIdentNode(typeName), obj, jsonNode, true)
of "seq":
let seqT = typeSym[1]
let forLoopI = newIdentNode("i")
let forLoopI = genSym(nskForVar, "i")
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
let constructorNode = createConstructor(seqT, indexerNode)
@ -1616,12 +1616,25 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
result = quote do:
(
var list: `typeSym` = @[];
# if `jsonNode`.kind != JArray:
# # TODO: Improve error message.
# raise newException(ValueError, "Expected a list")
verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
for `forLoopI` in 0 .. <`jsonNode`.len: list.add(`constructorNode`);
list
)
of "array":
let arrayT = typeSym[2]
let forLoopI = genSym(nskForVar, "i")
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
let constructorNode = createConstructor(arrayT, indexerNode)
# Create a statement expression containing a for loop.
result = quote do:
(
var list: `typeSym`;
verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
for `forLoopI` in 0 .. <`jsonNode`.len: list[`forLoopI`] =`constructorNode`;
list
)
else:
# Generic type.
let obj = getType(typeSym)

View file

@ -235,7 +235,7 @@ when not defined(JS):
x = x and (not (1'u64 shl (64'u64-12'u64-e) - 1'u64))
result = cast[float64](x)
proc truncImpl(f: float32): float32 =
const
mask : uint32 = 0xFF
@ -255,7 +255,7 @@ when not defined(JS):
x = x and (not (1'u32 shl (32'u32-9'u32-e) - 1'u32))
result = cast[float32](x)
proc trunc*(x: float64): float64 =
if classify(x) in {fcZero, fcNegZero, fcNan, fcInf, fcNegInf}: return x
result = truncImpl(x)
@ -395,6 +395,12 @@ proc radToDeg*[T: float32|float64](d: T): T {.inline.} =
## Convert from radians to degrees
result = T(d) / RadPerDeg
proc sgn*[T: SomeNumber](x: T): int {.inline.} =
## Sign function. Returns -1 for negative numbers and `NegInf`, 1 for
## positive numbers and `Inf`, and 0 for positive zero, negative zero and
## `NaN`.
ord(T(0) < x) - ord(x < T(0))
proc `mod`*[T: float32|float64](x, y: T): T =
## Computes the modulo operation for float operators. Equivalent
## to ``x - y * floor(x/y)``. Note that the remainder will always
@ -407,10 +413,13 @@ proc `mod`*[T: float32|float64](x, y: T): T =
{.pop.}
{.pop.}
proc `^`*[T](x, y: T): T =
proc `^`*[T](x: T, y: Natural): T =
## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use
## `pow <#pow,float,float>` for negative exponents.
assert y >= T(0)
when compiles(y >= T(0)):
assert y >= T(0)
else:
assert T(y) >= T(0)
var (x, y) = (x, y)
result = 1
@ -447,6 +456,7 @@ when isMainModule and not defined(JS):
assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0
assert(erf(6.0) > erf(5.0))
assert(erfc(6.0) < erfc(5.0))
when isMainModule:
# Function for approximate comparison of floats
proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9)
@ -509,3 +519,21 @@ when isMainModule:
doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf)
doAssert(classify(trunc(f_nan.float32)) == fcNan)
doAssert(classify(trunc(0.0'f32)) == fcZero)
block: # sgn() tests
assert sgn(1'i8) == 1
assert sgn(1'i16) == 1
assert sgn(1'i32) == 1
assert sgn(1'i64) == 1
assert sgn(1'u8) == 1
assert sgn(1'u16) == 1
assert sgn(1'u32) == 1
assert sgn(1'u64) == 1
assert sgn(-12342.8844'f32) == -1
assert sgn(123.9834'f64) == 1
assert sgn(0'i32) == 0
assert sgn(0'f32) == 0
assert sgn(NegInf) == -1
assert sgn(Inf) == 1
assert sgn(NaN) == 0

View file

@ -666,29 +666,38 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].}
else:
raiseOSError(osLastError(), $strerror(errno))
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
proc tryMoveFSObject(source, dest: string): bool =
## Moves a file or directory from `source` to `dest`. Returns false in case
## of `EXDEV` error. In case of other errors `OSError` is raised. Returns
## true in case of success.
when defined(Windows):
when useWinUnicode:
let s = newWideCString(source)
let d = newWideCString(dest)
if moveFileW(s, d) == 0'i32: raiseOSError(osLastError())
if moveFileExW(s, d, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError())
else:
if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError())
if moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError())
else:
if c_rename(source, dest) != 0'i32:
let err = osLastError()
if err == EXDEV.OSErrorCode:
# Fallback to copy & del
copyFile(source, dest)
try:
removeFile(source)
except:
discard tryRemoveFile(dest)
raise
return false
else:
raiseOSError(err, $strerror(errno))
return true
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
if not tryMoveFSObject(source, dest):
when not defined(windows):
# Fallback to copy & del
copyFile(source, dest)
try:
removeFile(source)
except:
discard tryRemoveFile(dest)
raise
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [ExecIOEffect].} =
@ -1369,6 +1378,14 @@ proc exclFilePermissions*(filename: string,
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
setFilePermissions(filename, getFilePermissions(filename)-permissions)
proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a directory from `source` to `dest`. If this fails, `OSError` is raised.
if not tryMoveFSObject(source, dest):
when not defined(windows):
# Fallback to copy & del
copyDir(source, dest)
removeDir(source)
include ospaths
proc expandSymlink*(symlinkPath: string): string =
@ -1412,7 +1429,7 @@ when defined(nimdoc):
proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
## Returns the `i`-th `command line argument`:idx: given to the application.
##
## `i` should be in the range `1..paramCount()`, the `EInvalidIndex`
## `i` should be in the range `1..paramCount()`, the `IndexError`
## exception will be raised for invalid values. Instead of iterating over
## `paramCount() <#paramCount>`_ with this proc you can call the
## convenience `commandLineParams() <#commandLineParams>`_.
@ -1450,7 +1467,8 @@ elif defined(windows):
tags: [ReadIOEffect].} =
# Docstring in nimdoc block.
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
return TaintedString(ownArgv[i])
if i < ownArgv.len and i >= 0: return TaintedString(ownArgv[i])
raise newException(IndexError, "invalid index")
elif not defined(createNimRtl) and
not(defined(posix) and appType == "lib") and

View file

@ -516,7 +516,16 @@ when declared(getEnv) or defined(nimscript):
proc getConfigDir*(): string {.rtl, extern: "nos$1",
tags: [ReadEnvEffect, ReadIOEffect].} =
## Returns the config directory of the current user for applications.
##
## On non-Windows OSs, this proc conforms to the XDG Base Directory
## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment
## variable if it is set, and returns the default configuration directory,
## "~/.config/", otherwise.
##
## An OS-dependent trailing slash is always present at the end of the
## returned string; `\\` on Windows and `/` on all other OSs.
when defined(windows): return string(getEnv("APPDATA")) & "\\"
elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/"
else: return string(getEnv("HOME")) & "/.config/"
proc getTempDir*(): string {.rtl, extern: "nos$1",

View file

@ -863,18 +863,15 @@ elif not defined(useNimRtl):
if data.workingDir.len > 0:
setCurrentDir($data.workingDir)
var pid: Pid
var err: OSErrorCode
if data.optionPoUsePath:
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
if res != 0'i32: err = osLastError()
else:
res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
if res != 0'i32: err = osLastError()
discard posix_spawn_file_actions_destroy(fops)
discard posix_spawnattr_destroy(attr)
if res != 0'i32: raiseOSError(err)
if res != 0'i32: raiseOSError(OSErrorCode(res))
return pid
else:

View file

@ -540,8 +540,8 @@ proc writeConfig*(dict: Config, filename: string) =
## Writes the contents of the table to the specified configuration file.
## Note: Comment statement will be ignored.
let file = open(filename, fmWrite)
let fileStream = newFileStream(filename)
defer: fileStream.close()
defer: file.close()
let fileStream = newFileStream(file)
dict.writeConfig(fileStream)
proc getSectionValue*(dict: Config, section, key: string): string =

View file

@ -149,26 +149,41 @@ proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
## retrieves the rest of the command line that has not been parsed yet.
result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedString] =
## This is an convenience iterator for iterating over the given OptParser object.
## Example:
##
## .. code-block:: nim
## var p = initOptParser("--left --debug:3 -l=4 -r:2")
## for kind, key, val in p.getopt():
## case kind
## of cmdArgument:
## filename = key
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## # no filename has been given, so we show the help:
## writeHelp()
p.pos = 0
while true:
next(p)
if p.kind == cmdEnd: break
yield (p.kind, p.key, p.val)
when declared(initOptParser):
iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
## This is an convenience iterator for iterating over the command line.
## This uses the OptParser object. Example:
## This is an convenience iterator for iterating over the command line arguments.
## This create a new OptParser object.
## See above for a more detailed example
##
## .. code-block:: nim
## var
## filename = ""
## for kind, key, val in getopt():
## case kind
## of cmdArgument:
## filename = key
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## # no filename has been given, so we show the help:
## writeHelp()
## # this will iterate over all arguments passed to the cmdline.
## continue
##
var p = initOptParser()
while true:
next(p)

View file

@ -123,26 +123,41 @@ type
{.deprecated: [TGetoptResult: GetoptResult].}
iterator getopt*(p: var OptParser): GetoptResult =
## This is an convenience iterator for iterating over the given OptParser object.
## Example:
##
## .. code-block:: nim
## var p = initOptParser("--left --debug:3 -l=4 -r:2")
## for kind, key, val in p.getopt():
## case kind
## of cmdArgument:
## filename = key
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## # no filename has been given, so we show the help:
## writeHelp()
p.pos = 0
while true:
next(p)
if p.kind == cmdEnd: break
yield (p.kind, p.key, p.val)
when declared(paramCount):
iterator getopt*(): GetoptResult =
## This is an convenience iterator for iterating over the command line.
## This uses the OptParser object. Example:
## This is an convenience iterator for iterating over the command line arguments.
## This create a new OptParser object.
## See above for a more detailed example
##
## .. code-block:: nim
## var
## filename = ""
## for kind, key, val in getopt():
## case kind
## of cmdArgument:
## filename = key
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## # no filename has been given, so we show the help:
## writeHelp()
## # this will iterate over all arguments passed to the cmdline.
## continue
##
var p = initOptParser()
while true:
next(p)

View file

@ -371,7 +371,7 @@ proc esc(c: char, reserved = {'\0'..'\255'}): string =
of '\a': result = "\\a"
of '\\': result = "\\\\"
of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c
elif c < ' ' or c >= '\128': result = '\\' & $ord(c)
elif c < ' ' or c >= '\127': result = '\\' & $ord(c)
elif c in reserved: result = '\\' & c
else: result = $c

View file

@ -123,7 +123,8 @@ when not defined(nimscript):
proc getMil(t: Time): int {.importcpp: "getTime", nodecl.}
randomize(getMil times.getTime())
else:
randomize(int times.getTime())
let time = int(times.epochTime() * 1_000_000_000)
randomize(time)
{.pop.}

View file

@ -334,12 +334,16 @@ when not defined(js):
if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result)
inc(s.pos, result)
else:
result = 0
proc ssPeekData(s: Stream, buffer: pointer, bufLen: int): int =
var s = StringStream(s)
result = min(bufLen, s.data.len - s.pos)
if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result)
else:
result = 0
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
var s = StringStream(s)

View file

@ -1643,7 +1643,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
## * replaces any ``\`` by ``\\``
## * replaces any ``'`` by ``\'``
## * replaces any ``"`` by ``\"``
## * replaces any other character in the set ``{'\0'..'\31', '\128'..'\255'}``
## * replaces any other character in the set ``{'\0'..'\31', '\127'..'\255'}``
## by ``\xHH`` where ``HH`` is its hexadecimal value.
## The procedure has been designed so that its output is usable for many
## different common syntaxes. The resulting string is prefixed with
@ -1653,7 +1653,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
result.add(prefix)
for c in items(s):
case c
of '\0'..'\31', '\128'..'\255':
of '\0'..'\31', '\127'..'\255':
add(result, "\\x")
add(result, toHex(ord(c), 2))
of '\\': add(result, "\\\\")

View file

@ -81,7 +81,7 @@ when defined(posix) and not defined(JS):
elif defined(windows):
import winlean
when defined(vcc) or defined(bcc):
when defined(vcc) or defined(bcc) or defined(icl):
# newest version of Visual C++ defines time_t to be of 64 bits
type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
# visual c's c runtime exposes these under a different name
@ -98,46 +98,47 @@ elif defined(windows):
elif defined(JS):
type
Time* = ref TimeObj
TimeObj {.importc.} = object
getDay: proc (): int {.tags: [], raises: [], benign.}
getFullYear: proc (): int {.tags: [], raises: [], benign.}
getHours: proc (): int {.tags: [], raises: [], benign.}
getMilliseconds: proc (): int {.tags: [], raises: [], benign.}
getMinutes: proc (): int {.tags: [], raises: [], benign.}
getMonth: proc (): int {.tags: [], raises: [], benign.}
getSeconds: proc (): int {.tags: [], raises: [], benign.}
getTime: proc (): int {.tags: [], raises: [], noSideEffect, benign.}
getTimezoneOffset: proc (): int {.tags: [], raises: [], benign.}
getDate: proc (): int {.tags: [], raises: [], benign.}
getUTCDate: proc (): int {.tags: [], raises: [], benign.}
getUTCFullYear: proc (): int {.tags: [], raises: [], benign.}
getUTCHours: proc (): int {.tags: [], raises: [], benign.}
getUTCMilliseconds: proc (): int {.tags: [], raises: [], benign.}
getUTCMinutes: proc (): int {.tags: [], raises: [], benign.}
getUTCMonth: proc (): int {.tags: [], raises: [], benign.}
getUTCSeconds: proc (): int {.tags: [], raises: [], benign.}
getUTCDay: proc (): int {.tags: [], raises: [], benign.}
getYear: proc (): int {.tags: [], raises: [], benign.}
parse: proc (s: cstring): Time {.tags: [], raises: [], benign.}
setDate: proc (x: int) {.tags: [], raises: [], benign.}
setFullYear: proc (x: int) {.tags: [], raises: [], benign.}
setHours: proc (x: int) {.tags: [], raises: [], benign.}
setMilliseconds: proc (x: int) {.tags: [], raises: [], benign.}
setMinutes: proc (x: int) {.tags: [], raises: [], benign.}
setMonth: proc (x: int) {.tags: [], raises: [], benign.}
setSeconds: proc (x: int) {.tags: [], raises: [], benign.}
setTime: proc (x: int) {.tags: [], raises: [], benign.}
setUTCDate: proc (x: int) {.tags: [], raises: [], benign.}
setUTCFullYear: proc (x: int) {.tags: [], raises: [], benign.}
setUTCHours: proc (x: int) {.tags: [], raises: [], benign.}
setUTCMilliseconds: proc (x: int) {.tags: [], raises: [], benign.}
setUTCMinutes: proc (x: int) {.tags: [], raises: [], benign.}
setUTCMonth: proc (x: int) {.tags: [], raises: [], benign.}
setUTCSeconds: proc (x: int) {.tags: [], raises: [], benign.}
setYear: proc (x: int) {.tags: [], raises: [], benign.}
toGMTString: proc (): cstring {.tags: [], raises: [], benign.}
toLocaleString: proc (): cstring {.tags: [], raises: [], benign.}
TimeBase = float
Time* = distinct TimeBase
proc getDay(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getHours(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getTime(t: Time): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
proc getTimezoneOffset(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getDate(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getUTCDate(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getUTCFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getUTCHours(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getUTCMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getUTCMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getUTCMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getUTCSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getUTCDay(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc getYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
proc parse(t: Time; s: cstring): Time {.tags: [], raises: [], benign, importcpp.}
proc setDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setTime(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setUTCDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setUTCFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setUTCHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setUTCMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setUTCMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setUTCMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setUTCSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc setYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
proc toGMTString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.}
proc toLocaleString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.}
type
TimeInfo* = object of RootObj ## represents a time in different parts
@ -225,17 +226,26 @@ proc `-`*(a, b: Time): int64 {.
proc `<`*(a, b: Time): bool {.
rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect.} =
## returns true iff ``a < b``, that is iff a happened before b.
result = a - b < 0
when defined(js):
result = TimeBase(a) < TimeBase(b)
else:
result = a - b < 0
proc `<=` * (a, b: Time): bool {.
rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect.}=
## returns true iff ``a <= b``.
result = a - b <= 0
when defined(js):
result = TimeBase(a) <= TimeBase(b)
else:
result = a - b <= 0
proc `==`*(a, b: Time): bool {.
rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect.} =
## returns true if ``a == b``, that is if both times represent the same value
result = a - b == 0
when defined(js):
result = TimeBase(a) == TimeBase(b)
else:
result = a - b == 0
proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.}
## returns the offset of the local (non-DST) timezone in seconds west of UTC.
@ -479,7 +489,7 @@ proc `-=`*(t: var Time, ti: TimeInterval) =
t = toTime(getLocalTime(t) - ti)
proc `-`*(t: Time, ti: TimeInterval): Time =
## adds the interval `ti` to Time `t`
## subtracts the interval `ti` from Time `t`
##
## ``echo getTime() - 1.day``
result = toTime(getLocalTime(t) - ti)

View file

@ -49,6 +49,9 @@ type
cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr``
## operator to get a pointer to a variable
typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
const
on* = true ## alias for ``true``
off* = false ## alias for ``false``
@ -56,6 +59,15 @@ const
{.push warning[GcMem]: off, warning[Uninit]: off.}
{.push hints: off.}
proc `or` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `or` meta class
proc `and` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `and` meta class
proc `not` *(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `not` meta class
type
Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer,
## bool, character, and enumeration types
@ -66,11 +78,11 @@ type
`ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type
`nil` {.magic: "Nil".}
expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates)
## **Deprecated** since version 0.15. Use ``untyped`` instead.
## **Deprecated** since version 0.15. Use ``untyped`` instead.
stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates)
## **Deprecated** since version 0.15. Use ``typed`` instead.
typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
void* {.magic: "VoidType".} ## meta type to denote the absence of any type
auto* {.magic: Expr.} ## meta type for automatic type determination
any* = distinct auto ## meta type for any supported type
@ -430,6 +442,7 @@ type
## providing an exception message
## is bad style.
trace: string
up: ref Exception # used for stacking exceptions. Not exported!
SystemError* = object of Exception ## \
## Abstract class for exceptions that the runtime system raises.
@ -1056,7 +1069,7 @@ proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
## ``floor(x/y)``.
##
## .. code-block:: Nim
## (7 div 5) == 2
## (7 div 5) == 1
proc `mod`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ModU", noSideEffect.}
## computes the integer modulo operation (remainder).
@ -1332,6 +1345,7 @@ const
hasThreadSupport = compileOption("threads") and not defined(nimscript)
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
taintMode = compileOption("taintmode")
nimEnableCovariance* = defined(nimEnableCovariance) # or true
when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
# tcc doesn't support TLS
@ -1879,7 +1893,7 @@ const
NimMajor*: int = 0
## is the major number of Nim's version.
NimMinor*: int = 16
NimMinor*: int = 17
## is the minor number of Nim's version.
NimPatch*: int = 1
@ -2034,6 +2048,14 @@ proc clamp*[T](x, a, b: T): T =
if x > b: return b
return x
proc len*[T: Ordinal](x: Slice[T]): int {.noSideEffect, inline.} =
## length of ordinal slice, when x.b < x.a returns zero length
##
## .. code-block:: Nim
## assert((0..5).len == 6)
## assert((5..2).len == 0)
result = max(0, ord(x.b) - ord(x.a) + 1)
iterator items*[T](a: openArray[T]): T {.inline.} =
## iterates over each item of `a`.
var i = 0
@ -2428,18 +2450,6 @@ when false:
# ----------------- GC interface ---------------------------------------------
when not defined(nimscript) and hasAlloc:
proc GC_disable*() {.rtl, inl, benign.}
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
## reactivate the GC. Note that in most circumstances one should only disable
## the mark and sweep phase with `GC_disableMarkAndSweep`.
proc GC_enable*() {.rtl, inl, benign.}
## enables the GC again.
proc GC_fullCollect*() {.rtl, benign.}
## forces a full garbage collection pass.
## Ordinary code does not need to call this (and should not).
type
GC_Strategy* = enum ## the strategy the GC should use for the application
gcThroughput, ## optimize for throughput
@ -2449,33 +2459,87 @@ when not defined(nimscript) and hasAlloc:
{.deprecated: [TGC_Strategy: GC_Strategy].}
proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.}
## tells the GC the desired strategy for the application.
## **Deprecated** since version 0.8.14. This has always been a nop.
when not defined(JS):
proc GC_disable*() {.rtl, inl, benign.}
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
## reactivate the GC. Note that in most circumstances one should only disable
## the mark and sweep phase with `GC_disableMarkAndSweep`.
proc GC_enableMarkAndSweep*() {.rtl, benign.}
proc GC_disableMarkAndSweep*() {.rtl, benign.}
## the current implementation uses a reference counting garbage collector
## with a seldomly run mark and sweep phase to free cycles. The mark and
## sweep phase may take a long time and is not needed if the application
## does not create cycles. Thus the mark and sweep phase can be deactivated
## and activated separately from the rest of the GC.
proc GC_enable*() {.rtl, inl, benign.}
## enables the GC again.
proc GC_getStatistics*(): string {.rtl, benign.}
## returns an informative string about the GC's activity. This may be useful
## for tweaking.
proc GC_fullCollect*() {.rtl, benign.}
## forces a full garbage collection pass.
## Ordinary code does not need to call this (and should not).
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
proc GC_ref*(x: string) {.magic: "GCref", benign.}
## marks the object `x` as referenced, so that it will not be freed until
## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
## n calls to `GC_unref` are needed to unmark `x`.
proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.}
## tells the GC the desired strategy for the application.
## **Deprecated** since version 0.8.14. This has always been a nop.
proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
proc GC_unref*(x: string) {.magic: "GCunref", benign.}
## see the documentation of `GC_ref`.
proc GC_enableMarkAndSweep*() {.rtl, benign.}
proc GC_disableMarkAndSweep*() {.rtl, benign.}
## the current implementation uses a reference counting garbage collector
## with a seldomly run mark and sweep phase to free cycles. The mark and
## sweep phase may take a long time and is not needed if the application
## does not create cycles. Thus the mark and sweep phase can be deactivated
## and activated separately from the rest of the GC.
proc GC_getStatistics*(): string {.rtl, benign.}
## returns an informative string about the GC's activity. This may be useful
## for tweaking.
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
proc GC_ref*(x: string) {.magic: "GCref", benign.}
## marks the object `x` as referenced, so that it will not be freed until
## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
## n calls to `GC_unref` are needed to unmark `x`.
proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
proc GC_unref*(x: string) {.magic: "GCunref", benign.}
## see the documentation of `GC_ref`.
else:
template GC_disable* =
{.warning: "GC_disable is a no-op in JavaScript".}
template GC_enable* =
{.warning: "GC_enable is a no-op in JavaScript".}
template GC_fullCollect* =
{.warning: "GC_fullCollect is a no-op in JavaScript".}
template GC_setStrategy* =
{.warning: "GC_setStrategy is a no-op in JavaScript".}
template GC_enableMarkAndSweep* =
{.warning: "GC_enableMarkAndSweep is a no-op in JavaScript".}
template GC_disableMarkAndSweep* =
{.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".}
template GC_ref*[T](x: ref T) =
{.warning: "GC_ref is a no-op in JavaScript".}
template GC_ref*[T](x: seq[T]) =
{.warning: "GC_ref is a no-op in JavaScript".}
template GC_ref*(x: string) =
{.warning: "GC_ref is a no-op in JavaScript".}
template GC_unref*[T](x: ref T) =
{.warning: "GC_unref is a no-op in JavaScript".}
template GC_unref*[T](x: seq[T]) =
{.warning: "GC_unref is a no-op in JavaScript".}
template GC_unref*(x: string) =
{.warning: "GC_unref is a no-op in JavaScript".}
template GC_getStatistics*(): string =
{.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".}
""
template accumulateResult*(iter: untyped) =
## helps to convert an iterator to a proc.
@ -3210,16 +3274,6 @@ when not defined(JS): #and not defined(nimscript):
elif defined(JS):
# Stubs:
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard
proc GC_disable() = discard
proc GC_enable() = discard
proc GC_fullCollect() = discard
proc GC_setStrategy(strategy: GC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1
@ -3458,7 +3512,7 @@ proc `*=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
proc `+=`*[T: float|float32|float64] (x: var T, y: T) {.
inline, noSideEffect.} =
## Increments in placee a floating point number
## Increments in place a floating point number
x = x + y
proc `-=`*[T: float|float32|float64] (x: var T, y: T) {.
@ -3721,7 +3775,8 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
when hasAlloc and not defined(nimscript) and not defined(JS):
proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} =
## performs a deep copy of `x`. This is also used by the code generator
## performs a deep copy of `y` and copies it into `x`.
## This is also used by the code generator
## for the implementation of ``spawn``.
discard

View file

@ -9,7 +9,6 @@
# Low level allocator for Nim. Has been designed to support the GC.
# TODO:
# - eliminate "used" field
# - make searching for block O(1)
{.push profiler:off.}

View file

@ -90,12 +90,11 @@ proc popSafePoint {.compilerRtl, inl.} =
excHandler = excHandler.prev
proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} =
#if e.parent.isNil:
# e.parent = currException
e.up = currException
currException = e
proc popCurrentException {.compilerRtl, inl.} =
currException = nil # currException.parent
currException = currException.up
# some platforms have native support for stack traces:
const
@ -247,6 +246,18 @@ when false:
pushCurrentException(e)
c_longjmp(excHandler.context, 1)
var onUnhandledException*: (proc (errorMsg: string) {.
nimcall.}) ## set this error \
## handler to override the existing behaviour on an unhandled exception.
## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``.
## Unstable API.
template unhandled(buf, body) =
if onUnhandledException != nil:
onUnhandledException($buf)
else:
body
proc raiseExceptionAux(e: ref Exception) =
if localRaiseHook != nil:
if not localRaiseHook(e): return
@ -277,7 +288,9 @@ proc raiseExceptionAux(e: ref Exception) =
add(buf, " [")
add(buf, $e.name)
add(buf, "]\n")
showErrorMessage(buf)
unhandled(buf):
showErrorMessage(buf)
quitOrDebug()
else:
# ugly, but avoids heap allocations :-)
template xadd(buf, s, slen: expr) =
@ -293,8 +306,9 @@ proc raiseExceptionAux(e: ref Exception) =
add(buf, " [")
xadd(buf, e.name, e.name.len)
add(buf, "]\n")
showErrorMessage(buf)
quitOrDebug()
unhandled(buf):
showErrorMessage(buf)
quitOrDebug()
proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
if e.name.isNil: e.name = ename

View file

@ -25,6 +25,13 @@ when defined(nimTypeNames):
c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", it.name, it.instances, it.sizes)
it = it.nextType
when defined(nimGcRefLeak):
proc oomhandler() =
c_fprintf(stdout, "[Heap] ROOTS: #%ld\n", gch.additionalRoots.len)
writeLeaks()
outOfMemHook = oomhandler
template decTypeSize(cell, t) =
# XXX this needs to use atomics for multithreaded apps!
when defined(nimTypeNames):

View file

@ -142,11 +142,54 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
# we need the prototype here for debugging purposes
when defined(nimGcRefLeak):
const
MaxTraceLen = 20 # tracking the last 20 calls is enough
type
GcStackTrace = object
lines: array[0..MaxTraceLen-1, cstring]
files: array[0..MaxTraceLen-1, cstring]
proc captureStackTrace(f: PFrame, st: var GcStackTrace) =
const
firstCalls = 5
var
it = f
i = 0
total = 0
while it != nil and i <= high(st.lines)-(firstCalls-1):
# the (-1) is for the "..." entry
st.lines[i] = it.procname
st.files[i] = it.filename
inc(i)
inc(total)
it = it.prev
var b = it
while it != nil:
inc(total)
it = it.prev
for j in 1..total-i-(firstCalls-1):
if b != nil: b = b.prev
if total != i:
st.lines[i] = "..."
st.files[i] = "..."
inc(i)
while b != nil and i <= high(st.lines):
st.lines[i] = b.procname
st.files[i] = b.filename
inc(i)
b = b.prev
var ax: array[10_000, GcStackTrace]
proc nimGCref(p: pointer) {.compilerProc.} =
# we keep it from being collected by pretending it's not even allocated:
when false:
when withBitvectors: excl(gch.allocated, usrToCell(p))
else: usrToCell(p).refcount = rcBlack
when defined(nimGcRefLeak):
captureStackTrace(framePtr, ax[gch.additionalRoots.len])
add(gch.additionalRoots, usrToCell(p))
proc nimGCunref(p: pointer) {.compilerProc.} =
@ -157,6 +200,8 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
while i >= 0:
if d[i] == cell:
d[i] = d[L]
when defined(nimGcRefLeak):
ax[i] = ax[L]
dec gch.additionalRoots.len
break
dec(i)
@ -164,6 +209,16 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
when withBitvectors: incl(gch.allocated, usrToCell(p))
else: usrToCell(p).refcount = rcWhite
when defined(nimGcRefLeak):
proc writeLeaks() =
for i in 0..gch.additionalRoots.len-1:
c_fprintf(stdout, "[Heap] NEW STACK TRACE\n")
for ii in 0..MaxTraceLen-1:
let line = ax[i].lines[ii]
let file = ax[i].files[ii]
if isNil(line): break
c_fprintf(stdout, "[Heap] %s(%s)\n", file, line)
include gc_common
proc prepareDealloc(cell: PCell) =

View file

@ -79,7 +79,7 @@ template withRegion*(r: MemRegion; body: untyped) =
try:
body
finally:
r = tlRegion
#r = tlRegion
tlRegion = oldRegion
template inc(p: pointer, s: int) =

View file

@ -446,7 +446,7 @@ when defined(kwin):
print(buf);
"""
elif defined(nodejs):
elif not defined(nimOldEcho):
proc ewriteln(x: cstring) = log(x)
proc rawEcho {.compilerproc, asmNoStackFrame.} =

View file

@ -255,12 +255,21 @@ elif defined(gogc):
next_gc: uint64 # next GC (in heap_alloc time)
last_gc: uint64 # last GC (in absolute time)
pause_total_ns: uint64
pause_ns: array[256, uint64]
pause_ns: array[256, uint64] # circular buffer of recent gc pause lengths
pause_end: array[256, uint64] # circular buffer of recent gc end times (nanoseconds since 1970)
numgc: uint32
numforcedgc: uint32 # number of user-forced GCs
gc_cpu_fraction: float64 # fraction of CPU time used by GC
enablegc: cbool
debuggc: cbool
# Statistics about allocation size classes.
by_size: array[goNumSizeClasses, goMStats_inner_struct]
# Statistics below here are not exported to MemStats directly.
tinyallocs: uint64 # number of tiny allocations that didn't cause actual allocation; not exported to go directly
gc_trigger: uint64
heap_live: uint64
heap_scan: uint64
heap_marked: uint64
proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl,
importc: "runtime_ReadMemStats",

View file

@ -49,7 +49,7 @@ proc reprStrAux(result: var string, s: cstring; len: int) =
of '"': add result, "\\\""
of '\\': add result, "\\\\" # BUGFIX: forgotten
of '\10': add result, "\\10\"\n\"" # " \n " # better readability
of '\128' .. '\255', '\0'..'\9', '\11'..'\31':
of '\127' .. '\255', '\0'..'\9', '\11'..'\31':
add result, "\\" & reprInt(ord(c))
else:
result.add(c)
@ -68,7 +68,7 @@ proc reprChar(x: char): string {.compilerRtl.} =
case x
of '"': add result, "\\\""
of '\\': add result, "\\\\"
of '\128' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x))
of '\127' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x))
else: add result, x
add result, "\'"

View file

@ -44,7 +44,7 @@ proc reprChar(x: char): string {.compilerRtl.} =
case x
of '"': add(result, "\\\"")
of '\\': add(result, "\\\\")
of '\128'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) )
of '\127'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) )
else: add(result, x)
add(result, "\'")
@ -56,7 +56,7 @@ proc reprStrAux(result: var string, s: cstring, len: int) =
of '"': add(result, "\\\"")
of '\\': add(result, "\\\\")
of '\10': add(result, "\\10\"\n\"")
of '\128'..'\255', '\0'..'\9', '\11'..'\31':
of '\127'..'\255', '\0'..'\9', '\11'..'\31':
add( result, "\\" & reprInt(ord(c)) )
else:
add( result, reprInt(ord(c)) ) # Not sure about this.

View file

@ -117,6 +117,12 @@ else:
when defined(linux) and defined(amd64):
abi: array[48 div sizeof(clonglong), clonglong]
SysCondAttr {.importc: "pthread_condattr_t", pure, final
header: """#include <sys/types.h>
#include <pthread.h>""".} = object
when defined(linux) and defined(amd64):
abi: array[4 div sizeof(cint), cint] # actually a cint
SysLockType = distinct cint
proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
@ -185,7 +191,7 @@ else:
importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
else:
proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {.
proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {.
importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
importc: "pthread_cond_destroy", header: "<pthread.h>".}
@ -196,7 +202,7 @@ else:
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
when defined(ios):
proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
proc initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
cond = cast[SysCond](c_malloc(sizeof(SysCondObj)))
initSysCondAux(cond[], cond_attr)
@ -209,7 +215,7 @@ else:
template signalSysCond(cond: var SysCond) =
signalSysCondAux(cond[])
else:
template initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
template initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
initSysCondAux(cond, cond_attr)
template deinitSysCond(cond: var SysCond) =
deinitSysCondAux(cond)

View file

@ -264,7 +264,17 @@ else:
proc getThreadId*(): int =
result = int(lwp_gettid())
elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd):
elif defined(openbsd):
proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
proc getThreadId*(): int =
result = int(getthrid())
elif defined(netbsd):
proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
proc getThreadId*(): int =
result = int(lwp_self())
elif defined(macosx) or defined(freebsd):
proc pthread_threadid_np(y: pointer; x: var uint64): cint {.importc, header: "pthread.h".}
proc getThreadId*(): int =

View file

@ -291,6 +291,14 @@ const
FILE_ATTRIBUTE_TEMPORARY* = 256'i32
MAX_PATH* = 260
MOVEFILE_COPY_ALLOWED* = 0x2'i32
MOVEFILE_CREATE_HARDLINK* = 0x10'i32
MOVEFILE_DELAY_UNTIL_REBOOT* = 0x4'i32
MOVEFILE_FAIL_IF_NOT_TRACKABLE* = 0x20'i32
MOVEFILE_REPLACE_EXISTING* = 0x1'i32
MOVEFILE_WRITE_THROUGH* = 0x8'i32
type
WIN32_FIND_DATA* {.pure.} = object
dwFileAttributes*: int32
@ -342,6 +350,9 @@ when useWinUnicode:
proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {.
importc: "MoveFileW", stdcall, dynlib: "kernel32".}
proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString,
flags: DWORD): WINBOOL {.
importc: "MoveFileExW", stdcall, dynlib: "kernel32".}
proc getEnvironmentStringsW*(): WideCString {.
stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".}
@ -369,6 +380,9 @@ else:
proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {.
importc: "MoveFileA", stdcall, dynlib: "kernel32".}
proc moveFileExA*(lpExistingFileName, lpNewFileName: WideCString,
flags: DWORD): WINBOOL {.
importc: "MoveFileExA", stdcall, dynlib: "kernel32".}
proc getEnvironmentStringsA*(): cstring {.
stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".}

View file

@ -26,7 +26,7 @@ when useWinVersion:
from winlean import SocketHandle
else:
const
versions = "(|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
versions = "(|.38|.39|.41|.43|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
when defined(macosx):
const
DLLSSLName = "libssl" & versions & ".dylib"