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 var alreadyPoppedCnt = p.inExceptBlock
for i in countup(1, howManyTrys): 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 # Pop safe points generated by try
if alreadyPoppedCnt > 0: if alreadyPoppedCnt > 0:
dec alreadyPoppedCnt dec alreadyPoppedCnt
@ -354,7 +354,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
for i in countdown(howManyTrys-1, 0): for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i]) 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 # Pop exceptions that was handled by the
# except-blocks we are in # except-blocks we are in
for i in countdown(howManyExcepts-1, 0): 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 disp = newNodeI(nkIfStmt, base.info)
var ands = getSysSym("and") var ands = getSysSym("and")
var iss = getSysSym("of") 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)): for meth in countup(0, high(methods)):
var curr = methods[meth] # generate condition: var curr = methods[meth] # generate condition:
var cond: PNode = nil var cond: PNode = nil
@ -242,9 +248,6 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
addSon(isn, newSymNode(iss)) addSon(isn, newSymNode(iss))
let param = base.typ.n.sons[col].sym let param = base.typ.n.sons[col].sym
addSon(isn, newSymNode(param)) 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])) addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
if cond != nil: if cond != nil:
var a = newNodeIT(nkCall, base.info, getSysType(tyBool)) var a = newNodeIT(nkCall, base.info, getSysType(tyBool))

View file

@ -2272,7 +2272,6 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
genModule(p, n) genModule(p, n)
add(p.g.code, p.locals) add(p.g.code, p.locals)
add(p.g.code, p.body) add(p.g.code, p.body)
globals.unique = p.unique
proc wholeCode(graph: ModuleGraph; m: BModule): Rope = proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
for prc in globals.forwarded: 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)) u = rope(lengthOrd(field.typ))
else: internalError(n.info, "genObjectFields(nkRecCase)") else: internalError(n.info, "genObjectFields(nkRecCase)")
if result != nil: add(result, ", " & tnl) if result != nil: add(result, ", " & tnl)
addf(result, "[SetConstr($1), $2]", addf(result, "[setConstr($1), $2]",
[u, genObjectFields(p, typ, lastSon(b))]) [u, genObjectFields(p, typ, lastSon(b))])
result = ("{kind: 3, offset: \"$1\", len: $3, " & result = ("{kind: 3, offset: \"$1\", len: $3, " &
"typ: $2, name: $4, sons: [$5]}") % [ "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: for i in 0 .. <gp.len:
if sfImmediate notin sym.flags: if sfImmediate notin sym.flags:
let idx = sym.typ.len + i 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}: 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") globalError(n.info, "static[T] or typedesc nor supported for .immediate macros")
# temporary storage: # temporary storage:
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) #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) result.add mapTypeToAst(t.sons[i], info)
else: else:
result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion) 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) result = mapTypeToAst(t.lastSon, info)
of tyDistinct: of tyDistinct:
if inst: if inst:

View file

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

View file

@ -9,7 +9,7 @@
include "system/inclrtl" include "system/inclrtl"
import os, oids, tables, strutils, times, heapqueue, options import os, tables, strutils, times, heapqueue, options
import nativesockets, net, deques import nativesockets, net, deques
@ -242,6 +242,11 @@ when defined(windows) or defined(nimdoc):
if gDisp.isNil: gDisp = newDispatcher() if gDisp.isNil: gDisp = newDispatcher()
result = gDisp result = gDisp
proc setGlobalDispatcher*(disp: PDispatcher) =
if not gDisp.isNil:
assert gDisp.callbacks.len == 0
gDisp = disp
proc register*(fd: AsyncFD) = proc register*(fd: AsyncFD) =
## Registers ``fd`` with the dispatcher. ## Registers ``fd`` with the dispatcher.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
@ -931,6 +936,11 @@ else:
if gDisp.isNil: gDisp = newDispatcher() if gDisp.isNil: gDisp = newDispatcher()
result = gDisp result = gDisp
proc setGlobalDispatcher*(disp: PDispatcher) =
if not gDisp.isNil:
assert gDisp.callbacks.len == 0
gDisp = disp
proc update(fd: AsyncFD, events: set[Event]) = proc update(fd: AsyncFD, events: set[Event]) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
assert fd.SocketHandle in p.selector 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 not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
assert bytesCount == size.int32 assert bytesCount == size.int32
f.offset.inc(size)
retFuture.complete() retFuture.complete()
else: else:
retFuture.fail(newException(OSError, osErrorMsg(errcode))) 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.offset = DWord(f.offset and 0xffffffff)
ol.offsetHigh = DWord(f.offset shr 32) ol.offsetHigh = DWord(f.offset shr 32)
f.offset.inc(size)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten. # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
let ret = writeFile(f.fd.Handle, buf, size.int32, nil, 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()))) retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
else: else:
assert bytesWritten == size.int32 assert bytesWritten == size.int32
f.offset.inc(size)
retFuture.complete() retFuture.complete()
else: else:
var written = 0 var written = 0
@ -410,7 +413,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
if not retFuture.finished: if not retFuture.finished:
if errcode == OSErrorCode(-1): if errcode == OSErrorCode(-1):
assert bytesCount == data.len.int32 assert bytesCount == data.len.int32
f.offset.inc(data.len)
retFuture.complete() retFuture.complete()
else: else:
retFuture.fail(newException(OSError, osErrorMsg(errcode))) 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.offset = DWord(f.offset and 0xffffffff)
ol.offsetHigh = DWord(f.offset shr 32) ol.offsetHigh = DWord(f.offset shr 32)
f.offset.inc(data.len)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten. # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil, 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()))) retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
else: else:
assert bytesWritten == data.len.int32 assert bytesWritten == data.len.int32
f.offset.inc(data.len)
retFuture.complete() retFuture.complete()
else: else:
var written = 0 var written = 0

View file

@ -647,9 +647,12 @@ when defineSsl:
sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut) sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket, proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
handshake: SslHandshakeType) = handshake: SslHandshakeType,
hostname: string = nil) =
## Wraps a connected socket in an SSL context. This function effectively ## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket. ## 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 ## This should be called on a connected socket, and will perform
## an SSL handshake immediately. ## an SSL handshake immediately.
@ -660,6 +663,10 @@ when defineSsl:
case handshake case handshake
of handshakeAsClient: 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) sslSetConnectState(socket.sslHandle)
of handshakeAsServer: of handshakeAsServer:
sslSetAcceptState(socket.sslHandle) 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`. ## deletes `key` from hash table `t`.
delImpl() 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]) = proc enlarge[A, B](t: var Table[A, B]) =
var n: KeyValuePairSeq[A, B] var n: KeyValuePairSeq[A, B]
newSeq(n, len(t.data) * growthFactor) 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`. ## deletes `key` from hash table `t`.
t[].del(key) 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] = proc newTable*[A, B](initialSize=64): TableRef[A, B] =
new(result) new(result)
result[] = initTable[A, B](initialSize) result[] = initTable[A, B](initialSize)
@ -625,7 +644,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
while ht >= 0 and hs >= 0: while ht >= 0 and hs >= 0:
var nxtt = t.data[ht].next var nxtt = t.data[ht].next
var nxts = s.data[hs].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): if (s.data[hs].key != t.data[ht].key) and (s.data[hs].val != t.data[ht].val):
return false return false
ht = nxtt ht = nxtt
@ -829,7 +848,7 @@ proc clear*[A](t: CountTableRef[A]) =
proc clear*[A](t: var CountTable[A]) = proc clear*[A](t: var CountTable[A]) =
## Resets the table so that it is empty. ## Resets the table so that it is empty.
clearImpl() clearImpl()
iterator pairs*[A](t: CountTable[A]): (A, int) = iterator pairs*[A](t: CountTable[A]): (A, int) =
## iterates over any (key, value) pair in the table `t`. ## iterates over any (key, value) pair in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
@ -1256,17 +1275,17 @@ when isMainModule:
var b = newOrderedTable[string, string](initialSize=2) var b = newOrderedTable[string, string](initialSize=2)
b.add("wrong?", "foo") b.add("wrong?", "foo")
b.add("wrong?", "foo2") b.add("wrong?", "foo2")
assert a == b assert a == b
block: #5482 block: #5482
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var b = newOrderedTable[string, string](initialSize=2) var b = newOrderedTable[string, string](initialSize=2)
b.add("wrong?", "foo") b.add("wrong?", "foo")
b.add("wrong?", "foo2") b.add("wrong?", "foo2")
assert a == b assert a == b
block: #5487 block: #5487
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var b = newOrderedTable[string, string]() # notice, default size! var b = newOrderedTable[string, string]() # notice, default size!
b.add("wrong?", "foo") b.add("wrong?", "foo")
b.add("wrong?", "foo2") b.add("wrong?", "foo2")
@ -1279,13 +1298,13 @@ when isMainModule:
b.add("wrong?", "foo2") b.add("wrong?", "foo2")
assert a == b assert a == b
block: block:
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable() var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
var c = newOrderedTable[string, string]() # notice, default size! var c = newOrderedTable[string, string]() # notice, default size!
c.add("wrong?", "foo") c.add("wrong?", "foo")
c.add("wrong?", "foo2") c.add("wrong?", "foo2")
assert a == b assert a == b
assert a == c assert a == c

View file

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

View file

@ -237,6 +237,180 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
raiseOSError(osLastError()) raiseOSError(osLastError())
result = newSocket(fd, domain, sockType, protocol, buffered) 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: when defineSsl:
CRYPTO_malloc_init() CRYPTO_malloc_init()
SslLibraryInit() SslLibraryInit()
@ -438,9 +612,12 @@ when defineSsl:
raiseSSLError() raiseSSLError()
proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket, proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
handshake: SslHandshakeType) = handshake: SslHandshakeType,
hostname: string = nil) =
## Wraps a connected socket in an SSL context. This function effectively ## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket. ## 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 ## This should be called on a connected socket, and will perform
## an SSL handshake immediately. ## an SSL handshake immediately.
@ -450,6 +627,10 @@ when defineSsl:
wrapSocket(ctx, socket) wrapSocket(ctx, socket)
case handshake case handshake
of handshakeAsClient: 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) let ret = SSLConnect(socket.sslHandle)
socketError(socket, ret) socketError(socket, ret)
of handshakeAsServer: of handshakeAsServer:
@ -1302,181 +1483,6 @@ proc `$`*(address: IpAddress): string =
mask = mask shr 4 mask = mask shr 4
printedLastGroup = true 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, proc dial*(address: string, port: Port,
protocol = IPPROTO_TCP, buffered = true): Socket protocol = IPPROTO_TCP, buffered = true): Socket
{.tags: [ReadIOEffect, WriteIOEffect].} = {.tags: [ReadIOEffect, WriteIOEffect].} =

View file

@ -335,7 +335,8 @@ proc execProcesses*(cmds: openArray[string],
if afterRunEvent != nil: afterRunEvent(i, p) if afterRunEvent != nil: afterRunEvent(i, p)
close(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. ## `select` with a sensible Nim interface. `timeout` is in milliseconds.
## Specify -1 for no timeout. Returns the number of processes that are ## 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 ## 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 ## **Warning**: This function may give unexpected or completely wrong
## results on Windows. ## 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): when not defined(useNimRtl):
proc execProcess(command: string, 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 = proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
## Finds the first occurrence of ``first``, then returns everything from there ## 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 var i = skipUntil(s, first, start)+1+start
result = "" result = ""
discard s.parseUntil(result, if second == '\0': first else: second, i) 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. ## of significant digits to be printed.
## `precision`'s default value is the maximum number of meaningful digits ## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nim's ``float`` type. ## after the decimal point for Nim's ``float`` type.
##
## If ``precision == 0``, it tries to format it nicely.
result = formatBiggestFloat(f, format, precision, decimalSep) result = formatBiggestFloat(f, format, precision, decimalSep)
proc trimZeros*(x: var string) {.noSideEffect.} = proc trimZeros*(x: var string) {.noSideEffect.} =

View file

@ -165,8 +165,22 @@ when someGcc and hasThreadSupport:
template fence*() = atomicThreadFence(ATOMIC_SEQ_CST) template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
elif defined(vcc) and hasThreadSupport: elif defined(vcc) and hasThreadSupport:
proc addAndFetch*(p: ptr int, val: int): int {. when defined(cpp):
importc: "_InterlockedExchangeAdd", header: "<intrin.h>".} 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>".} 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) result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
elif defined(vcc) and hasThreadSupport: elif defined(vcc) and hasThreadSupport:
result = addAndFetch(memLoc.addr, x) result = addAndFetch(memLoc.addr, x)
inc(result, x)
else: else:
inc(memLoc, x) inc(memLoc, x)
result = memLoc result = memLoc
@ -192,6 +207,7 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED) result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED)
elif defined(vcc) and hasThreadSupport: elif defined(vcc) and hasThreadSupport:
result = addAndFetch(memLoc.addr, -x) result = addAndFetch(memLoc.addr, -x)
dec(result, x)
else: else:
dec(memLoc, x) dec(memLoc, x)
result = memLoc result = memLoc

View file

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

View file

@ -99,7 +99,7 @@ elif defined(genode):
else: else:
type type
SysLock {.importc: "pthread_mutex_t", pure, final, SysLockObj {.importc: "pthread_mutex_t", pure, final,
header: """#include <sys/types.h> header: """#include <sys/types.h>
#include <pthread.h>""".} = object #include <pthread.h>""".} = object
when defined(linux) and defined(amd64): when defined(linux) and defined(amd64):
@ -111,7 +111,7 @@ else:
when defined(linux) and defined(amd64): when defined(linux) and defined(amd64):
abi: array[4 div sizeof(cint), cint] # actually a cint 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> header: """#include <sys/types.h>
#include <pthread.h>""".} = object #include <pthread.h>""".} = object
when defined(linux) and defined(amd64): when defined(linux) and defined(amd64):
@ -119,8 +119,62 @@ else:
SysLockType = distinct cint 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.} 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: when insideRLocksModule:
proc SysLockType_Reentrant: SysLockType = proc SysLockType_Reentrant: SysLockType =
@ -130,27 +184,39 @@ else:
proc setSysLockType(a: var SysLockAttr, t: SysLockType) {. proc setSysLockType(a: var SysLockAttr, t: SysLockType) {.
importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.} importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
proc acquireSys(L: var SysLock) {.noSideEffect, else:
importc: "pthread_mutex_lock", header: "<pthread.h>".} proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {.
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) {.
importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
proc waitSysCond(cond: var SysCond, lock: var SysLock) {. proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
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,
importc: "pthread_cond_destroy", header: "<pthread.h>".} 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.} {.pop.}

View file

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

View file

@ -595,6 +595,8 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
raise newException(IOError, raise newException(IOError,
"Cannot find Nim standard library: Nim compiler not in PATH") "Cannot find Nim standard library: Nim compiler not in PATH")
gPrefixDir = binaryPath.splitPath().head.parentDir() gPrefixDir = binaryPath.splitPath().head.parentDir()
if not dirExists(gPrefixDir / "lib"): gPrefixDir = ""
#msgs.writelnHook = proc (line: string) = log(line) #msgs.writelnHook = proc (line: string) = log(line)
myLog("START " & gProjectFull) 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: except KeyError:
discard 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() = proc orderedTableSortTest() =
var t = initOrderedTable[string, int](2) var t = initOrderedTable[string, int](2)
for key, val in items(data): t[key] = val for key, val in items(data): t[key] = val

View file

@ -21,49 +21,67 @@ proc symToIdent(x: NimNode): NimNode =
for c in x: for c in x:
result.add symToIdent(c) result.add symToIdent(c)
# check getTypeInst and getTypeImpl for given symbol x
macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed = macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed =
# check that getTypeInst(x) equals inst0
let inst = x.getTypeInst let inst = x.getTypeInst
let impl = x.getTypeImpl
let inst0r = inst0.symToIdent.treeRepr
let instr = inst.symToIdent.treeRepr let instr = inst.symToIdent.treeRepr
#echo inst0r let inst0r = inst0.symToIdent.treeRepr
#echo instr #echo instr
#echo inst0r
doAssert(instr == 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 = var impl0 =
if implX.kind == nnkNilLit: inst0 if implX.kind == nnkNilLit: inst0
else: implX[0][2] else: implX[0][2]
let impl0r = impl0.symToIdent.treerepr
let implr = impl.symToIdent.treerepr let implr = impl.symToIdent.treerepr
#echo impl0r let impl0r = impl0.symToIdent.treerepr
#echo implr #echo implr
#echo impl0r
doAssert(implr == impl0r) doAssert(implr == impl0r)
template echoString(s:string) = echo s.replace("\n","\n ")
result = newStmtList() result = newStmtList()
#template echoString(s: string) = echo s.replace("\n","\n ")
#result.add getAst(echoString(" " & inst0.repr)) #result.add getAst(echoString(" " & inst0.repr))
#result.add getAst(echoString(" " & inst.repr)) #result.add getAst(echoString(" " & inst.repr))
#result.add getAst(echoString(" " & impl0.repr)) #result.add getAst(echoString(" " & impl0.repr))
#result.add getAst(echoString(" " & impl.repr)) #result.add getAst(echoString(" " & impl.repr))
if recurse: 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): testV(n, false):
type _ = m type _ = m
result.add getAst(testDecl(inst.symToIdent, impl.symToIdent)) result.add getAst(testDecl(inst.symToIdent, impl.symToIdent))
# test with a variable (instance) of type
template testV(inst, recurse, impl) = template testV(inst, recurse, impl) =
block: block:
#echo "testV(" & astToStr(inst) & ", " & $recurse & "):" & astToStr(impl) #echo "testV(" & astToStr(inst) & ", " & $recurse & "):" & astToStr(impl)
var x: inst var x: inst
testX(x, inst, recurse, impl) 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) = template test(inst) =
testT(inst, false) testT(inst, false)
testV(inst, true, nil) 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 type
Model = object of RootObj Model = object of RootObj
@ -87,9 +105,12 @@ type
value:T value:T
Foo[N:static[int],T] = object Foo[N:static[int],T] = object
Bar[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]] baz:Foo[N,GenericObject[T]]
Generic[T] = seq[int]
Concrete = Generic[int]
test(bool) test(bool)
test(char) test(char)
test(int) test(int)
@ -97,13 +118,17 @@ test(float)
test(ptr int) test(ptr int)
test(ref int) test(ref int)
test(array[1..10,Bar[2,Foo[3,float]]]) 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(distinct Bar[2,Foo[3,float]])
test(tuple[a:int,b:Foo[-1,float]]) test(tuple[a:int,b:Foo[-1,float]])
#test(MyEnum):
# type _ = enum
# valueA, valueB, valueC
test(set[MyEnum])
test(seq[int]) 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]]): test(Bar[2,Foo[3,float]]):
type _ = object type _ = object
baz: Foo[2, GenericObject[Foo[3, float]]] baz: Foo[2, GenericObject[Foo[3, float]]]
@ -118,8 +143,12 @@ test(Tree):
value: int value: int
left: ref Tree left: ref Tree
right: ref Tree right: ref Tree
test(proc (a: int, b: Foo[2,float])) test(Concrete):
test(proc (a: int, b: Foo[2,float]): Bar[3,int]) type _ = Generic[int]
test(Generic[int]):
type _ = seq[int]
test(Generic[float]):
type _ = seq[int]
# bug #4862 # bug #4862
static: static:

View file

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

View file

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

View file

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

View file

@ -7,7 +7,11 @@ when defined(windows):
proc progress(status: DownloadStatus, progress: uint, total: uint, proc progress(status: DownloadStatus, progress: uint, total: uint,
message: string) {.procvar, gcsafe.} = message: string) {.procvar, gcsafe.} =
echo "Downloading " & url 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) downloadToFile(url, file, {optUseCache}, progress)
echo "100%" 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 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 returned had a repeated ``EnumTy`` node and was missing the initial pragma
node (which is currently empty for an enum). 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``, - If the dispatcher parameter's value used in multi method is ``nil``,
a ``NilError`` exception is raised. The old behavior was that the method a ``NilError`` exception is raised. The old behavior was that the method
would be a ``nop`` then. would be a ``nop`` then.