Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf 2017-05-16 15:43:09 +02:00
commit c3c37dbb15
29 changed files with 517 additions and 277 deletions

View file

@ -332,7 +332,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
var alreadyPoppedCnt = p.inExceptBlock
for i in countup(1, howManyTrys):
if not p.module.compileToCpp:
if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
# Pop safe points generated by try
if alreadyPoppedCnt > 0:
dec alreadyPoppedCnt
@ -354,7 +354,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i])
if not p.module.compileToCpp:
if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
# Pop exceptions that was handled by the
# except-blocks we are in
for i in countdown(howManyExcepts-1, 0):

View file

@ -233,6 +233,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
var disp = newNodeI(nkIfStmt, base.info)
var ands = getSysSym("and")
var iss = getSysSym("of")
for col in countup(1, paramLen - 1):
if contains(relevantCols, col):
let param = base.typ.n.sons[col].sym
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
addSon(nilchecks, newTree(nkCall,
newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
for meth in countup(0, high(methods)):
var curr = methods[meth] # generate condition:
var cond: PNode = nil
@ -242,9 +248,6 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
addSon(isn, newSymNode(iss))
let param = base.typ.n.sons[col].sym
addSon(isn, newSymNode(param))
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
addSon(nilchecks, newTree(nkCall,
newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
if cond != nil:
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))

View file

@ -2272,7 +2272,6 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
genModule(p, n)
add(p.g.code, p.locals)
add(p.g.code, p.body)
globals.unique = p.unique
proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
for prc in globals.forwarded:

View file

@ -59,7 +59,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
u = rope(lengthOrd(field.typ))
else: internalError(n.info, "genObjectFields(nkRecCase)")
if result != nil: add(result, ", " & tnl)
addf(result, "[SetConstr($1), $2]",
addf(result, "[setConstr($1), $2]",
[u, genObjectFields(p, typ, lastSon(b))])
result = ("{kind: 3, offset: \"$1\", len: $3, " &
"typ: $2, name: $4, sons: [$5]}") % [

View file

@ -1651,8 +1651,16 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
for i in 0 .. <gp.len:
if sfImmediate notin sym.flags:
let idx = sym.typ.len + i
tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
if idx < n.len:
tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
else:
dec(evalMacroCounter)
c.callsite = nil
localError(n.info, "expected " & $gp.len &
" generic parameter(s)")
elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}:
dec(evalMacroCounter)
c.callsite = nil
globalError(n.info, "static[T] or typedesc nor supported for .immediate macros")
# temporary storage:
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)

View file

@ -175,7 +175,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result.add mapTypeToAst(t.sons[i], info)
else:
result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
of tyGenericBody, tyOrdinal:
of tyGenericBody:
if inst:
result = mapTypeToAstX(t.lastSon, info, inst, true)
else:
result = mapTypeToAst(t.lastSon, info)
of tyOrdinal:
result = mapTypeToAst(t.lastSon, info)
of tyDistinct:
if inst:

View file

@ -599,7 +599,7 @@ Result variable
A procedure that returns a value has an implicit ``result`` variable declared
that represents the return value. A ``return`` statement with no expression is a
shorthand for ``return result``. The ``result`` value is always returned
automatically at the end a procedure if there is no ``return`` statement at
automatically at the end of a procedure if there is no ``return`` statement at
the exit.
.. code-block:: nim
@ -1074,8 +1074,8 @@ at runtime by 0, the second by 1 and so on. For example:
Direction = enum
north, east, south, west
var x = south # `x` is of type `Direction`; its value is `south`
echo x # writes "south" to `stdout`
var x = south # `x` is of type `Direction`; its value is `south`
echo x # writes "south" to `stdout`
All the comparison operators can be used with enumeration types.
@ -1132,11 +1132,11 @@ A subrange type is a range of values from an integer or enumeration type
.. code-block:: nim
type
Subrange = range[0..5]
MySubrange = range[0..5]
``Subrange`` is a subrange of ``int`` which can only hold the values 0
to 5. Assigning any other value to a variable of type ``Subrange`` is a
``MySubrange`` is a subrange of ``int`` which can only hold the values 0
to 5. Assigning any other value to a variable of type ``MySubrange`` is a
compile-time or runtime error. Assignments from the base type to one of its
subrange types (and vice versa) are allowed.

View file

@ -9,7 +9,7 @@
include "system/inclrtl"
import os, oids, tables, strutils, times, heapqueue, options
import os, tables, strutils, times, heapqueue, options
import nativesockets, net, deques
@ -242,6 +242,11 @@ when defined(windows) or defined(nimdoc):
if gDisp.isNil: gDisp = newDispatcher()
result = gDisp
proc setGlobalDispatcher*(disp: PDispatcher) =
if not gDisp.isNil:
assert gDisp.callbacks.len == 0
gDisp = disp
proc register*(fd: AsyncFD) =
## Registers ``fd`` with the dispatcher.
let p = getGlobalDispatcher()
@ -931,6 +936,11 @@ else:
if gDisp.isNil: gDisp = newDispatcher()
result = gDisp
proc setGlobalDispatcher*(disp: PDispatcher) =
if not gDisp.isNil:
assert gDisp.callbacks.len == 0
gDisp = disp
proc update(fd: AsyncFD, events: set[Event]) =
let p = getGlobalDispatcher()
assert fd.SocketHandle in p.selector

View file

@ -339,13 +339,17 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
assert bytesCount == size.int32
f.offset.inc(size)
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
)
# passing -1 here should work according to MSDN, but doesn't. For more
# information see
# http://stackoverflow.com/questions/33650899/does-asynchronous-file-
# appending-in-windows-preserve-order
ol.offset = DWord(f.offset and 0xffffffff)
ol.offsetHigh = DWord(f.offset shr 32)
f.offset.inc(size)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
let ret = writeFile(f.fd.Handle, buf, size.int32, nil,
@ -364,7 +368,6 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
else:
assert bytesWritten == size.int32
f.offset.inc(size)
retFuture.complete()
else:
var written = 0
@ -410,7 +413,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
assert bytesCount == data.len.int32
f.offset.inc(data.len)
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
@ -420,6 +422,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
)
ol.offset = DWord(f.offset and 0xffffffff)
ol.offsetHigh = DWord(f.offset shr 32)
f.offset.inc(data.len)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
@ -441,7 +444,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
else:
assert bytesWritten == data.len.int32
f.offset.inc(data.len)
retFuture.complete()
else:
var written = 0

View file

@ -647,9 +647,12 @@ when defineSsl:
sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
handshake: SslHandshakeType) =
handshake: SslHandshakeType,
hostname: string = nil) =
## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname
## the server certificate should be validated against.
##
## This should be called on a connected socket, and will perform
## an SSL handshake immediately.
@ -660,6 +663,10 @@ when defineSsl:
case handshake
of handshakeAsClient:
if not hostname.isNil and not isIpAddress(hostname):
# Set the SNI address for this connection. This call can fail if
# we're not using TLSv1+.
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
sslSetConnectState(socket.sslHandle)
of handshakeAsServer:
sslSetAcceptState(socket.sslHandle)

View file

@ -269,6 +269,18 @@ proc del*[A, B](t: var Table[A, B], key: A) =
## deletes `key` from hash table `t`.
delImpl()
proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
## Deletes the ``key`` from the table.
## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
## unchanged.
var hc: Hash
var index = rawGet(t, key, hc)
result = index >= 0
if result:
shallowCopy(val, t.data[index].val)
delImplIdx(t, index)
proc enlarge[A, B](t: var Table[A, B]) =
var n: KeyValuePairSeq[A, B]
newSeq(n, len(t.data) * growthFactor)
@ -424,6 +436,13 @@ proc del*[A, B](t: TableRef[A, B], key: A) =
## deletes `key` from hash table `t`.
t[].del(key)
proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
## Deletes the ``key`` from the table.
## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
## unchanged.
result = t[].take(key, val)
proc newTable*[A, B](initialSize=64): TableRef[A, B] =
new(result)
result[] = initTable[A, B](initialSize)
@ -625,7 +644,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
while ht >= 0 and hs >= 0:
var nxtt = t.data[ht].next
var nxts = s.data[hs].next
if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
if (s.data[hs].key != t.data[ht].key) and (s.data[hs].val != t.data[ht].val):
return false
ht = nxtt
@ -829,7 +848,7 @@ proc clear*[A](t: CountTableRef[A]) =
proc clear*[A](t: var CountTable[A]) =
## Resets the table so that it is empty.
clearImpl()
iterator pairs*[A](t: CountTable[A]): (A, int) =
## iterates over any (key, value) pair in the table `t`.
for h in 0..high(t.data):
@ -1256,17 +1275,17 @@ when isMainModule:
var b = newOrderedTable[string, string](initialSize=2)
b.add("wrong?", "foo")
b.add("wrong?", "foo2")
assert a == b
assert a == b
block: #5482
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var b = newOrderedTable[string, string](initialSize=2)
b.add("wrong?", "foo")
b.add("wrong?", "foo2")
assert a == b
assert a == b
block: #5487
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var b = newOrderedTable[string, string]() # notice, default size!
b.add("wrong?", "foo")
b.add("wrong?", "foo2")
@ -1279,13 +1298,13 @@ when isMainModule:
b.add("wrong?", "foo2")
assert a == b
block:
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
block:
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
var c = newOrderedTable[string, string]() # notice, default size!
c.add("wrong?", "foo")
c.add("wrong?", "foo2")
c.add("wrong?", "foo2")
assert a == b
assert a == c

View file

@ -512,7 +512,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
raise newException(HttpRequestError,
"The proxy server rejected a CONNECT request, " &
"so a secure connection could not be established.")
sslContext.wrapConnectedSocket(s, handshakeAsClient)
sslContext.wrapConnectedSocket(s, handshakeAsClient, hostUrl.hostname)
else:
raise newException(HttpRequestError, "SSL support not available. Cannot connect via proxy over SSL")
else:
@ -1060,7 +1060,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
when defined(ssl):
if isSsl:
try:
client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
client.sslContext.wrapConnectedSocket(
client.socket, handshakeAsClient, url.hostname)
except:
client.socket.close()
raise getCurrentException()
@ -1102,7 +1103,8 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
raise newException(HttpRequestError,
"The proxy server rejected a CONNECT request, " &
"so a secure connection could not be established.")
client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
client.sslContext.wrapConnectedSocket(
client.socket, handshakeAsClient, requestUrl.hostname)
client.proxy = nil
else:
raise newException(HttpRequestError,

View file

@ -237,6 +237,180 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
raiseOSError(osLastError())
result = newSocket(fd, domain, sockType, protocol, buffered)
proc parseIPv4Address(address_str: string): IpAddress =
## Parses IPv4 adresses
## Raises EInvalidValue on errors
var
byteCount = 0
currentByte:uint16 = 0
seperatorValid = false
result.family = IpAddressFamily.IPv4
for i in 0 .. high(address_str):
if address_str[i] in strutils.Digits: # Character is a number
currentByte = currentByte * 10 +
cast[uint16](ord(address_str[i]) - ord('0'))
if currentByte > 255'u16:
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
seperatorValid = true
elif address_str[i] == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
result.address_v4[byteCount] = cast[uint8](currentByte)
currentByte = 0
byteCount.inc
seperatorValid = false
else:
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
if byteCount != 3 or not seperatorValid:
raise newException(ValueError, "Invalid IP Address")
result.address_v4[byteCount] = cast[uint8](currentByte)
proc parseIPv6Address(address_str: string): IpAddress =
## Parses IPv6 adresses
## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6
if address_str.len < 2:
raise newException(ValueError, "Invalid IP Address")
var
groupCount = 0
currentGroupStart = 0
currentShort:uint32 = 0
seperatorValid = true
dualColonGroup = -1
lastWasColon = false
v4StartPos = -1
byteCount = 0
for i,c in address_str:
if c == ':':
if not seperatorValid:
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid seperator")
if lastWasColon:
if dualColonGroup != -1:
raise newException(ValueError,
"Invalid IP Address. Address contains more than one \"::\" seperator")
dualColonGroup = groupCount
seperatorValid = false
elif i != 0 and i != high(address_str):
if groupCount >= 8:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
currentShort = 0
groupCount.inc()
if dualColonGroup != -1: seperatorValid = false
elif i == 0: # only valid if address starts with ::
if address_str[1] != ':':
raise newException(ValueError,
"Invalid IP Address. Address may not start with \":\"")
else: # i == high(address_str) - only valid if address ends with ::
if address_str[high(address_str)-1] != ':':
raise newException(ValueError,
"Invalid IP Address. Address may not end with \":\"")
lastWasColon = true
currentGroupStart = i + 1
elif c == '.': # Switch to parse IPv4 mode
if i < 3 or not seperatorValid or groupCount >= 7:
raise newException(ValueError, "Invalid IP Address")
v4StartPos = currentGroupStart
currentShort = 0
seperatorValid = false
break
elif c in strutils.HexDigits:
if c in strutils.Digits: # Normal digit
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
elif c >= 'a' and c <= 'f': # Lower case hex
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
else: # Upper case hex
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
if currentShort > 65535'u32:
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
lastWasColon = false
seperatorValid = true
else:
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
if seperatorValid: # Copy remaining data
if groupCount >= 8:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
groupCount.inc()
else: # Must parse IPv4 address
for i,c in address_str[v4StartPos..high(address_str)]:
if c in strutils.Digits: # Character is a number
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
if currentShort > 255'u32:
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
seperatorValid = true
elif c == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3:
raise newException(ValueError, "Invalid IP Address")
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
currentShort = 0
byteCount.inc()
seperatorValid = false
else: # Invalid character
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
if byteCount != 3 or not seperatorValid:
raise newException(ValueError, "Invalid IP Address")
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
groupCount += 2
# Shift and fill zeros in case of ::
if groupCount > 8:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
elif groupCount < 8: # must fill
if dualColonGroup == -1:
raise newException(ValueError,
"Invalid IP Address. The address consists of too few groups")
var toFill = 8 - groupCount # The number of groups to fill
var toShift = groupCount - dualColonGroup # Nr of known groups after ::
for i in 0..2*toShift-1: # shift
result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
for i in 0..2*toFill-1: # fill with 0s
result.address_v6[dualColonGroup*2+i] = 0
elif dualColonGroup != -1:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): IpAddress =
## Parses an IP address
## Raises EInvalidValue on error
if address_str == nil:
raise newException(ValueError, "IP Address string is nil")
if address_str.contains(':'):
return parseIPv6Address(address_str)
else:
return parseIPv4Address(address_str)
proc isIpAddress*(address_str: string): bool {.tags: [].} =
## Checks if a string is an IP address
## Returns true if it is, false otherwise
try:
discard parseIpAddress(address_str)
except ValueError:
return false
return true
when defineSsl:
CRYPTO_malloc_init()
SslLibraryInit()
@ -438,9 +612,12 @@ when defineSsl:
raiseSSLError()
proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
handshake: SslHandshakeType) =
handshake: SslHandshakeType,
hostname: string = nil) =
## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname
## the server certificate should be validated against.
##
## This should be called on a connected socket, and will perform
## an SSL handshake immediately.
@ -450,6 +627,10 @@ when defineSsl:
wrapSocket(ctx, socket)
case handshake
of handshakeAsClient:
if not hostname.isNil and not isIpAddress(hostname):
# Discard result in case OpenSSL version doesn't support SNI, or we're
# not using TLSv1+
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
let ret = SSLConnect(socket.sslHandle)
socketError(socket, ret)
of handshakeAsServer:
@ -1302,181 +1483,6 @@ proc `$`*(address: IpAddress): string =
mask = mask shr 4
printedLastGroup = true
proc parseIPv4Address(address_str: string): IpAddress =
## Parses IPv4 adresses
## Raises EInvalidValue on errors
var
byteCount = 0
currentByte:uint16 = 0
seperatorValid = false
result.family = IpAddressFamily.IPv4
for i in 0 .. high(address_str):
if address_str[i] in strutils.Digits: # Character is a number
currentByte = currentByte * 10 +
cast[uint16](ord(address_str[i]) - ord('0'))
if currentByte > 255'u16:
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
seperatorValid = true
elif address_str[i] == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
result.address_v4[byteCount] = cast[uint8](currentByte)
currentByte = 0
byteCount.inc
seperatorValid = false
else:
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
if byteCount != 3 or not seperatorValid:
raise newException(ValueError, "Invalid IP Address")
result.address_v4[byteCount] = cast[uint8](currentByte)
proc parseIPv6Address(address_str: string): IpAddress =
## Parses IPv6 adresses
## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6
if address_str.len < 2:
raise newException(ValueError, "Invalid IP Address")
var
groupCount = 0
currentGroupStart = 0
currentShort:uint32 = 0
seperatorValid = true
dualColonGroup = -1
lastWasColon = false
v4StartPos = -1
byteCount = 0
for i,c in address_str:
if c == ':':
if not seperatorValid:
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid seperator")
if lastWasColon:
if dualColonGroup != -1:
raise newException(ValueError,
"Invalid IP Address. Address contains more than one \"::\" seperator")
dualColonGroup = groupCount
seperatorValid = false
elif i != 0 and i != high(address_str):
if groupCount >= 8:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
currentShort = 0
groupCount.inc()
if dualColonGroup != -1: seperatorValid = false
elif i == 0: # only valid if address starts with ::
if address_str[1] != ':':
raise newException(ValueError,
"Invalid IP Address. Address may not start with \":\"")
else: # i == high(address_str) - only valid if address ends with ::
if address_str[high(address_str)-1] != ':':
raise newException(ValueError,
"Invalid IP Address. Address may not end with \":\"")
lastWasColon = true
currentGroupStart = i + 1
elif c == '.': # Switch to parse IPv4 mode
if i < 3 or not seperatorValid or groupCount >= 7:
raise newException(ValueError, "Invalid IP Address")
v4StartPos = currentGroupStart
currentShort = 0
seperatorValid = false
break
elif c in strutils.HexDigits:
if c in strutils.Digits: # Normal digit
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
elif c >= 'a' and c <= 'f': # Lower case hex
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
else: # Upper case hex
currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
if currentShort > 65535'u32:
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
lastWasColon = false
seperatorValid = true
else:
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
if seperatorValid: # Copy remaining data
if groupCount >= 8:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
groupCount.inc()
else: # Must parse IPv4 address
for i,c in address_str[v4StartPos..high(address_str)]:
if c in strutils.Digits: # Character is a number
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
if currentShort > 255'u32:
raise newException(ValueError,
"Invalid IP Address. Value is out of range")
seperatorValid = true
elif c == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3:
raise newException(ValueError, "Invalid IP Address")
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
currentShort = 0
byteCount.inc()
seperatorValid = false
else: # Invalid character
raise newException(ValueError,
"Invalid IP Address. Address contains an invalid character")
if byteCount != 3 or not seperatorValid:
raise newException(ValueError, "Invalid IP Address")
result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
groupCount += 2
# Shift and fill zeros in case of ::
if groupCount > 8:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
elif groupCount < 8: # must fill
if dualColonGroup == -1:
raise newException(ValueError,
"Invalid IP Address. The address consists of too few groups")
var toFill = 8 - groupCount # The number of groups to fill
var toShift = groupCount - dualColonGroup # Nr of known groups after ::
for i in 0..2*toShift-1: # shift
result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
for i in 0..2*toFill-1: # fill with 0s
result.address_v6[dualColonGroup*2+i] = 0
elif dualColonGroup != -1:
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): IpAddress =
## Parses an IP address
## Raises EInvalidValue on error
if address_str == nil:
raise newException(ValueError, "IP Address string is nil")
if address_str.contains(':'):
return parseIPv6Address(address_str)
else:
return parseIPv4Address(address_str)
proc isIpAddress*(address_str: string): bool {.tags: [].} =
## Checks if a string is an IP address
## Returns true if it is, false otherwise
try:
discard parseIpAddress(address_str)
except ValueError:
return false
return true
proc dial*(address: string, port: Port,
protocol = IPPROTO_TCP, buffered = true): Socket
{.tags: [ReadIOEffect, WriteIOEffect].} =

View file

@ -335,7 +335,8 @@ proc execProcesses*(cmds: openArray[string],
if afterRunEvent != nil: afterRunEvent(i, p)
close(p)
proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
proc select*(readfds: var seq[Process], timeout = 500): int
{.benign, deprecated.}
## `select` with a sensible Nim interface. `timeout` is in milliseconds.
## Specify -1 for no timeout. Returns the number of processes that are
## ready to read from. The processes that are ready to be read from are
@ -343,6 +344,9 @@ proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
##
## **Warning**: This function may give unexpected or completely wrong
## results on Windows.
##
## **Deprecated since version 0.17.0**: This procedure isn't cross-platform
## and so should not be used in newly written code.
when not defined(useNimRtl):
proc execProcess(command: string,

View file

@ -201,7 +201,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
## Finds the first occurrence of ``first``, then returns everything from there
## up to ``second``(if ``second`` is '\0', then ``first`` is used).
## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
var i = skipUntil(s, first, start)+1+start
result = ""
discard s.parseUntil(result, if second == '\0': first else: second, i)

View file

@ -1881,6 +1881,8 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
## of significant digits to be printed.
## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nim's ``float`` type.
##
## If ``precision == 0``, it tries to format it nicely.
result = formatBiggestFloat(f, format, precision, decimalSep)
proc trimZeros*(x: var string) {.noSideEffect.} =

View file

@ -165,8 +165,22 @@ when someGcc and hasThreadSupport:
template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
elif defined(vcc) and hasThreadSupport:
proc addAndFetch*(p: ptr int, val: int): int {.
importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
when defined(cpp):
when sizeof(int) == 8:
proc addAndFetch*(p: ptr int, val: int): int {.
importcpp: "_InterlockedExchangeAdd64(static_cast<NI volatile *>(#), #)",
header: "<intrin.h>".}
else:
proc addAndFetch*(p: ptr int, val: int): int {.
importcpp: "_InterlockedExchangeAdd(static_cast<NI volatile *>(#), #)",
header: "<intrin.h>".}
else:
when sizeof(int) == 8:
proc addAndFetch*(p: ptr int, val: int): int {.
importc: "_InterlockedExchangeAdd64", header: "<intrin.h>".}
else:
proc addAndFetch*(p: ptr int, val: int): int {.
importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
@ -180,6 +194,7 @@ proc atomicInc*(memLoc: var int, x: int = 1): int =
result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
elif defined(vcc) and hasThreadSupport:
result = addAndFetch(memLoc.addr, x)
inc(result, x)
else:
inc(memLoc, x)
result = memLoc
@ -192,6 +207,7 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED)
elif defined(vcc) and hasThreadSupport:
result = addAndFetch(memLoc.addr, -x)
dec(result, x)
else:
dec(memLoc, x)
result = memLoc

View file

@ -299,8 +299,13 @@ proc raiseExceptionAux(e: ref Exception) =
proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
if e.name.isNil: e.name = ename
when hasSomeStackTrace:
e.trace = ""
rawWriteStackTrace(e.trace)
if e.trace.isNil:
e.trace = ""
rawWriteStackTrace(e.trace)
elif framePtr != nil:
e.trace.add "[[reraised from:\n"
auxWriteStackTrace(framePtr, e.trace)
e.trace.add "]]\n"
raiseExceptionAux(e)
proc reraiseException() {.compilerRtl.} =

View file

@ -99,7 +99,7 @@ elif defined(genode):
else:
type
SysLock {.importc: "pthread_mutex_t", pure, final,
SysLockObj {.importc: "pthread_mutex_t", pure, final,
header: """#include <sys/types.h>
#include <pthread.h>""".} = object
when defined(linux) and defined(amd64):
@ -111,7 +111,7 @@ else:
when defined(linux) and defined(amd64):
abi: array[4 div sizeof(cint), cint] # actually a cint
SysCond {.importc: "pthread_cond_t", pure, final,
SysCondObj {.importc: "pthread_cond_t", pure, final,
header: """#include <sys/types.h>
#include <pthread.h>""".} = object
when defined(linux) and defined(amd64):
@ -119,8 +119,62 @@ else:
SysLockType = distinct cint
proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) {.
proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
proc deinitSysAux(L: var SysLockObj) {.noSideEffect,
importc: "pthread_mutex_destroy", header: "<pthread.h>".}
proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
importc: "pthread_mutex_lock", header: "<pthread.h>".}
proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
when defined(ios):
# iOS will behave badly if sync primitives are moved in memory. In order
# to prevent this once and for all, we're doing an extra malloc when
# initializing the primitive.
type
SysLock = ptr SysLockObj
SysCond = ptr SysCondObj
when not declared(c_malloc):
proc c_malloc(size: csize): pointer {.
importc: "malloc", header: "<stdlib.h>".}
proc c_free(p: pointer) {.
importc: "free", header: "<stdlib.h>".}
proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
L = cast[SysLock](c_malloc(sizeof(SysLockObj)))
initSysLockAux(L[], attr)
proc deinitSys(L: var SysLock) =
deinitSysAux(L[])
c_free(L)
template acquireSys(L: var SysLock) =
acquireSysAux(L[])
template tryAcquireSys(L: var SysLock): bool =
tryAcquireSysAux(L[]) == 0'i32
template releaseSys(L: var SysLock) =
releaseSysAux(L[])
else:
type
SysLock = SysLockObj
SysCond = SysCondObj
template initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
initSysLockAux(L, attr)
template deinitSys(L: var SysLock) =
deinitSysAux(L)
template acquireSys(L: var SysLock) =
acquireSysAux(L)
template tryAcquireSys(L: var SysLock): bool =
tryAcquireSysAux(L) == 0'i32
template releaseSys(L: var SysLock) =
releaseSysAux(L)
when insideRLocksModule:
proc SysLockType_Reentrant: SysLockType =
@ -130,27 +184,39 @@ else:
proc setSysLockType(a: var SysLockAttr, t: SysLockType) {.
importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
proc acquireSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_lock", header: "<pthread.h>".}
proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
proc tryAcquireSys(L: var SysLock): bool {.inline.} =
result = tryAcquireSysAux(L) == 0'i32
proc releaseSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
proc deinitSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_destroy", header: "<pthread.h>".}
when not insideRLocksModule:
proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) {.
else:
proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {.
importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
proc signalSysCond(cond: var SysCond) {.
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
proc deinitSysCond(cond: var SysCond) {.noSideEffect,
proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
importc: "pthread_cond_destroy", header: "<pthread.h>".}
proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj) {.
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
proc signalSysCondAux(cond: var SysCondObj) {.
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
when defined(ios):
proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
cond = cast[SysCond](c_malloc(sizeof(SysCondObj)))
initSysCondAux(cond[], cond_attr)
proc deinitSysCond(cond: var SysCond) =
deinitSysCondAux(cond[])
c_free(cond)
template waitSysCond(cond: var SysCond, lock: var SysLock) =
waitSysCondAux(cond[], lock[])
template signalSysCond(cond: var SysCond) =
signalSysCondAux(cond[])
else:
template initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
initSysCondAux(cond, cond_attr)
template deinitSysCond(cond: var SysCond) =
deinitSysCondAux(cond)
template waitSysCond(cond: var SysCond, lock: var SysLock) =
waitSysCondAux(cond, lock)
template signalSysCond(cond: var SysCond) =
signalSysCondAux(cond)
{.pop.}

View file

@ -9,7 +9,7 @@
include "system/inclrtl"
import os, oids, tables, strutils, times, heapqueue, lists, options
import os, tables, strutils, times, heapqueue, lists, options
import nativesockets, net, deques
@ -219,6 +219,11 @@ when defined(windows) or defined(nimdoc):
if gDisp.isNil: gDisp = newDispatcher()
result = gDisp
proc setGlobalDispatcher*(disp: PDispatcher) =
if not gDisp.isNil:
assert gDisp.callbacks.len == 0
gDisp = disp
proc register*(fd: AsyncFD) =
## Registers ``fd`` with the dispatcher.
let p = getGlobalDispatcher()
@ -1080,6 +1085,11 @@ else:
if gDisp.isNil: gDisp = newDispatcher()
result = gDisp
proc setGlobalDispatcher*(disp: PDispatcher) =
if not gDisp.isNil:
assert gDisp.callbacks.len == 0
gDisp = disp
proc register*(fd: AsyncFD) =
let p = getGlobalDispatcher()
var data = newAsyncData()

View file

@ -595,6 +595,8 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
raise newException(IOError,
"Cannot find Nim standard library: Nim compiler not in PATH")
gPrefixDir = binaryPath.splitPath().head.parentDir()
if not dirExists(gPrefixDir / "lib"): gPrefixDir = ""
#msgs.writelnHook = proc (line: string) = log(line)
myLog("START " & gProjectFull)

View file

@ -0,0 +1,17 @@
discard """
output: '''string 1
string 2
string 3'''
"""
# bug #5532
import os, asyncfile, asyncdispatch
removeFile("test.txt")
let f = openAsync("test.txt", fmWrite)
var futs = newSeq[Future[void]]()
for i in 1..3:
futs.add(f.write("string " & $i & "\n"))
waitFor(all(futs))
f.close()
echo readFile("test.txt")

View file

@ -235,6 +235,21 @@ block withKeyTest:
except KeyError:
discard
block takeTest:
var t = initTable[string, int]()
t["key"] = 123
var val = 0
assert(t.take("key", val))
assert(val == 123)
val = -1
assert(not t.take("key", val))
assert(val == -1)
assert(not t.take("otherkey", val))
assert(val == -1)
proc orderedTableSortTest() =
var t = initOrderedTable[string, int](2)
for key, val in items(data): t[key] = val

View file

@ -21,49 +21,67 @@ proc symToIdent(x: NimNode): NimNode =
for c in x:
result.add symToIdent(c)
# check getTypeInst and getTypeImpl for given symbol x
macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed =
# check that getTypeInst(x) equals inst0
let inst = x.getTypeInst
let impl = x.getTypeImpl
let inst0r = inst0.symToIdent.treeRepr
let instr = inst.symToIdent.treeRepr
#echo inst0r
let inst0r = inst0.symToIdent.treeRepr
#echo instr
#echo inst0r
doAssert(instr == inst0r)
# check that getTypeImpl(x) is correct
# if implX is nil then compare to inst0
# else we expect implX to be a type definition
# and we extract the implementation from that
let impl = x.getTypeImpl
var impl0 =
if implX.kind == nnkNilLit: inst0
else: implX[0][2]
let impl0r = impl0.symToIdent.treerepr
let implr = impl.symToIdent.treerepr
#echo impl0r
let impl0r = impl0.symToIdent.treerepr
#echo implr
#echo impl0r
doAssert(implr == impl0r)
template echoString(s:string) = echo s.replace("\n","\n ")
result = newStmtList()
#template echoString(s: string) = echo s.replace("\n","\n ")
#result.add getAst(echoString(" " & inst0.repr))
#result.add getAst(echoString(" " & inst.repr))
#result.add getAst(echoString(" " & impl0.repr))
#result.add getAst(echoString(" " & impl.repr))
if recurse:
template testDecl(n, m :typed) =
# now test using a newly formed variable of type getTypeInst(x)
template testDecl(n,m: typed) =
testV(n, false):
type _ = m
result.add getAst(testDecl(inst.symToIdent, impl.symToIdent))
# test with a variable (instance) of type
template testV(inst, recurse, impl) =
block:
#echo "testV(" & astToStr(inst) & ", " & $recurse & "):" & astToStr(impl)
var x: inst
testX(x, inst, recurse, impl)
template testT(inst, recurse) =
block:
type myType = inst
testV(myType, recurse):
type _ = inst
# test with a newly created typedesc (myType)
# using the passed type as the implementation
template testT(impl, recurse) =
block:
type myType = impl
testV(myType, recurse):
type _ = impl
# test a built-in type whose instance is equal to the implementation
template test(inst) =
testT(inst, false)
testV(inst, true, nil)
template test(inst, impl) = testV(inst, true, impl)
# test a custom type with provided implementation
template test(inst, impl) =
testV(inst, true, impl)
type
Model = object of RootObj
@ -87,9 +105,12 @@ type
value:T
Foo[N:static[int],T] = object
Bar[N:static[int],T] = object
#baz:Foo[N+1,GenericObject[T]]
#baz:Foo[N+1,GenericObject[T]] # currently fails
baz:Foo[N,GenericObject[T]]
Generic[T] = seq[int]
Concrete = Generic[int]
test(bool)
test(char)
test(int)
@ -97,13 +118,17 @@ test(float)
test(ptr int)
test(ref int)
test(array[1..10,Bar[2,Foo[3,float]]])
test(array[MyEnum,Bar[2,Foo[3,float]]])
test(distinct Bar[2,Foo[3,float]])
test(tuple[a:int,b:Foo[-1,float]])
#test(MyEnum):
# type _ = enum
# valueA, valueB, valueC
test(set[MyEnum])
test(seq[int])
test(set[MyEnum])
test(proc (a: int, b: Foo[2,float]))
test(proc (a: int, b: Foo[2,float]): Bar[3,int])
test(MyEnum):
type _ = enum
valueA, valueB, valueC
test(Bar[2,Foo[3,float]]):
type _ = object
baz: Foo[2, GenericObject[Foo[3, float]]]
@ -118,8 +143,12 @@ test(Tree):
value: int
left: ref Tree
right: ref Tree
test(proc (a: int, b: Foo[2,float]))
test(proc (a: int, b: Foo[2,float]): Bar[3,int])
test(Concrete):
type _ = Generic[int]
test(Generic[int]):
type _ = seq[int]
test(Generic[float]):
type _ = seq[int]
# bug #4862
static:

View file

@ -104,15 +104,17 @@ proc syncTest() =
client.close()
# Timeout test.
client = newHttpClient(timeout = 1)
try:
resp = client.request("http://example.com/")
doAssert false, "TimeoutError should have been raised."
except TimeoutError:
discard
except:
doAssert false, "TimeoutError should have been raised."
when false:
# Disabled for now because it causes troubles with AppVeyor
# Timeout test.
client = newHttpClient(timeout = 1)
try:
resp = client.request("http://example.com/")
doAssert false, "TimeoutError should have been raised."
except TimeoutError:
discard
except:
doAssert false, "TimeoutError should have been raised."
proc makeIPv6HttpServer(hostname: string, port: Port): AsyncFD =
let fd = newNativeSocket(AF_INET6)

View file

@ -9,8 +9,11 @@
import parseutils, strutils, os, osproc, streams, parsecfg
const
cmdTemplate* = r"compiler" / "nim $target --lib:lib --hints:on -d:testing $options $file"
var compilerPrefix* = "compiler" / "nim "
proc cmdTemplate*(): string =
compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file"
type
TTestAction* = enum
@ -100,7 +103,7 @@ proc specDefaults*(result: var TSpec) =
result.outp = ""
result.nimout = ""
result.ccodeCheck = ""
result.cmd = cmdTemplate
result.cmd = cmdTemplate()
result.line = 0
result.column = 0
result.tfile = ""
@ -173,7 +176,7 @@ proc parseSpec*(filename: string): TSpec =
raise newException(ValueError, "cannot interpret as a bool: " & e.value)
of "cmd":
if e.value.startsWith("nim "):
result.cmd = "compiler" / e.value
result.cmd = compilerPrefix & e.value[4..^1]
else:
result.cmd = e.value
of "ccodecheck": result.ccodeCheck = e.value

View file

@ -34,6 +34,7 @@ Options:
--failing only show failing/ignored tests
--pedantic return non-zero status code if there are failures
--targets:"c c++ js objc" run tests for specified targets (default: all)
--nim:path use a particular nim executable (default: compiler/nim)
""" % resultsFile
type
@ -367,7 +368,7 @@ proc testNoSpec(r: var TResults, test: TTest) =
# does not extract the spec because the file is not supposed to have any
#let tname = test.name.addFileExt(".nim")
inc(r.total)
let given = callCompiler(cmdTemplate, test.name, test.options, test.target)
let given = callCompiler(cmdTemplate(), test.name, test.options, test.target)
r.addResult(test, "", given.msg, given.err)
if given.err == reSuccess: inc(r.passed)
@ -376,7 +377,7 @@ proc testC(r: var TResults, test: TTest) =
let tname = test.name.addFileExt(".c")
inc(r.total)
styledEcho "Processing ", fgCyan, extractFilename(tname)
var given = callCCompiler(cmdTemplate, test.name & ".c", test.options, test.target)
var given = callCCompiler(cmdTemplate(), test.name & ".c", test.options, test.target)
if given.err != reSuccess:
r.addResult(test, "", given.msg, given.err)
elif test.action == actionRun:
@ -424,6 +425,7 @@ proc main() =
of "failing": optFailing = true
of "pedantic": optPedantic = true
of "targets": targets = parseTargets(p.val.string)
of "nim": compilerPrefix = p.val.string
else: quit Usage
p.next()
if p.kind != cmdArgument: quit Usage

View file

@ -7,7 +7,11 @@ when defined(windows):
proc progress(status: DownloadStatus, progress: uint, total: uint,
message: string) {.procvar, gcsafe.} =
echo "Downloading " & url
echo clamp(int(progress.BiggestInt*100 div total.BiggestInt), 0, 100), "%"
let t = total.BiggestInt
if t != 0:
echo clamp(int(progress.BiggestInt*100 div t), 0, 100), "%"
else:
echo "0%"
downloadToFile(url, file, {optUseCache}, progress)
echo "100%"

View file

@ -45,6 +45,8 @@ Changes affecting backwards compatibility
AST that is the same as what is used to define an enum. Previously the AST
returned had a repeated ``EnumTy`` node and was missing the initial pragma
node (which is currently empty for an enum).
- ``macros.getTypeImpl`` now correctly returns the implementation for a symbol
of type ``tyGenericBody``.
- If the dispatcher parameter's value used in multi method is ``nil``,
a ``NilError`` exception is raised. The old behavior was that the method
would be a ``nop`` then.