Merge branch 'devel' into araq
This commit is contained in:
commit
c3c37dbb15
29 changed files with 517 additions and 277 deletions
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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]}") % [
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
12
doc/tut1.rst
12
doc/tut1.rst
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
358
lib/pure/net.nim
358
lib/pure/net.nim
|
|
@ -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].} =
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.} =
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.} =
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
17
tests/async/tasyncfilewrite.nim
Normal file
17
tests/async/tasyncfilewrite.nim
Normal 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")
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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%"
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue