diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/core/allocators.nim | 34 | ||||
-rw-r--r-- | lib/core/macros.nim | 6 | ||||
-rw-r--r-- | lib/core/seqs.nim | 55 | ||||
-rw-r--r-- | lib/core/typeinfo.nim | 1 | ||||
-rw-r--r-- | lib/js/jsffi.nim | 67 | ||||
-rw-r--r-- | lib/nimrtl.nim | 1 | ||||
-rw-r--r-- | lib/posix/posix.nim | 6 | ||||
-rw-r--r-- | lib/pure/asyncnet.nim | 2 | ||||
-rw-r--r-- | lib/pure/collections/sequtils.nim | 160 | ||||
-rw-r--r-- | lib/pure/httpclient.nim | 120 | ||||
-rw-r--r-- | lib/pure/os.nim | 9 | ||||
-rw-r--r-- | lib/pure/osproc.nim | 13 | ||||
-rw-r--r-- | lib/pure/parseutils.nim | 4 | ||||
-rw-r--r-- | lib/pure/random.nim | 4 | ||||
-rw-r--r-- | lib/pure/times.nim | 80 | ||||
-rw-r--r-- | lib/pure/unicode.nim | 7 | ||||
-rw-r--r-- | lib/system.nim | 21 | ||||
-rw-r--r-- | lib/system/dyncalls.nim | 7 | ||||
-rw-r--r-- | lib/system/excpt.nim | 9 | ||||
-rw-r--r-- | lib/system/gc.nim | 92 | ||||
-rw-r--r-- | lib/system/gc2.nim | 41 | ||||
-rw-r--r-- | lib/system/gc_ms.nim | 24 | ||||
-rw-r--r-- | lib/system/gc_regions.nim | 4 | ||||
-rw-r--r-- | lib/system/jssys.nim | 7 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 16 | ||||
-rw-r--r-- | lib/system/reprjs.nim | 4 |
26 files changed, 440 insertions, 354 deletions
diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim index f652f0d85..5189bb762 100644 --- a/lib/core/allocators.nim +++ b/lib/core/allocators.nim @@ -11,19 +11,36 @@ type AllocatorFlag* {.pure.} = enum ## flags describing the properties of the allocator ThreadLocal ## the allocator is thread local only. ZerosMem ## the allocator always zeros the memory on an allocation - Allocator* = ptr object {.inheritable.} + Allocator* = ptr AllocatorObj + AllocatorObj* {.inheritable.} = object alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.} dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.} realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.} deallocAll*: proc (a: Allocator) {.nimcall.} flags*: set[AllocatorFlag] + allocCount: int + deallocCount: int var localAllocator {.threadvar.}: Allocator sharedAllocator: Allocator + allocatorStorage {.threadvar.}: AllocatorObj proc getLocalAllocator*(): Allocator = result = localAllocator + if result == nil: + result = addr allocatorStorage + result.alloc = proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.} = + result = system.alloc(size) + inc a.allocCount + result.dealloc = proc (a: Allocator; p: pointer; size: int) {.nimcall.} = + system.dealloc(p) + inc a.deallocCount + result.realloc = proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.} = + result = system.realloc(p, newSize) + result.deallocAll = nil + result.flags = {ThreadLocal} + localAllocator = result proc setLocalAllocator*(a: Allocator) = localAllocator = a @@ -34,15 +51,6 @@ proc getSharedAllocator*(): Allocator = proc setSharedAllocator*(a: Allocator) = sharedAllocator = a -when false: - proc alloc*(size: int; alignment: int = 8): pointer = - let a = getCurrentAllocator() - result = a.alloc(a, size, alignment) - - proc dealloc*(p: pointer; size: int) = - let a = getCurrentAllocator() - a.dealloc(a, p, size) - - proc realloc*(p: pointer; oldSize, newSize: int): pointer = - let a = getCurrentAllocator() - result = a.realloc(a, p, oldSize, newSize) +proc allocCounters*(): (int, int) = + let a = getLocalAllocator() + result = (a.allocCount, a.deallocCount) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d74d86bf6..f45ca3f82 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -274,6 +274,12 @@ when defined(nimHasSymOwnerInMacro): ## result is also mnde of kind nnkSym if owner exists otherwise ## nnkNilLit is returned +when defined(nimHasInstantiationOfInMacro): + proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.} + ## check if proc symbol is instance of the generic proc symbol + ## useful to check proc symbols against generic symbols + ## returned by `bindSym` + proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} ## with 'getType' you can access the node's `type`:idx:. A Nim type is ## mapped to a Nim AST too, so it's slightly confusing but it means the same diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index fb81a30de..a41ef10ab 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -85,7 +85,7 @@ type proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} = # we have to use type erasure here as Nim does not support generic # compilerProcs. Oh well, this will all be inlined anyway. - if cap <= 0: + if cap > 0: let region = getLocalAllocator() var p = cast[ptr PayloadBase](region.alloc(region, cap * elemSize + sizeof(int) + sizeof(Allocator))) p.region = region @@ -94,23 +94,25 @@ proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} = else: result = nil -proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} = - if len+addlen <= len: - result = p - elif p == nil: - result = newSeqPayload(len+addlen, elemSize) - else: - # Note: this means we cannot support things that have internal pointers as - # they get reallocated here. This needs to be documented clearly. - var p = cast[ptr PayloadBase](p) - let region = if p.region == nil: getLocalAllocator() else: p.region - let cap = max(resize(p.cap), len+addlen) - var q = cast[ptr PayloadBase](region.realloc(region, p, - sizeof(int) + sizeof(Allocator) + elemSize * p.cap, - sizeof(int) + sizeof(Allocator) + elemSize * cap)) - q.region = region - q.cap = cap - result = q +proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {. + compilerRtl, noSideEffect.} = + {.noSideEffect.}: + if len+addlen <= len: + result = p + elif p == nil: + result = newSeqPayload(len+addlen, elemSize) + else: + # Note: this means we cannot support things that have internal pointers as + # they get reallocated here. This needs to be documented clearly. + var p = cast[ptr PayloadBase](p) + let region = if p.region == nil: getLocalAllocator() else: p.region + let cap = max(resize(p.cap), len+addlen) + var q = cast[ptr PayloadBase](region.realloc(region, p, + sizeof(int) + sizeof(Allocator) + elemSize * p.cap, + sizeof(int) + sizeof(Allocator) + elemSize * cap)) + q.region = region + q.cap = cap + result = q proc shrink*[T](x: var seq[T]; newLen: Natural) = sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'" @@ -124,18 +126,19 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) = let oldLen = x.len if newLen <= oldLen: return var xu = cast[ptr NimSeqV2[T]](addr x) - - xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))) + if xu.p == nil or xu.p.cap < newLen: + xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))) xu.len = newLen for i in oldLen .. newLen-1: - x.data[i] = value + xu.p.data[i] = value proc setLen[T](s: var seq[T], newlen: Natural) = - if newlen < s.len: - shrink(s, newLen) - else: - var v: T # get the default value of 'v' - grow(s, newLen, v) + {.noSideEffect.}: + if newlen < s.len: + shrink(s, newLen) + else: + var v: T # get the default value of 'v' + grow(s, newLen, v) when false: proc resize[T](s: var NimSeqV2[T]) = diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 14613c50d..cfb8f8f5d 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -106,6 +106,7 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = of 1: d = ze(cast[ptr int8](a +% n.offset)[]) of 2: d = ze(cast[ptr int16](a +% n.offset)[]) of 4: d = int(cast[ptr int32](a +% n.offset)[]) + of 8: d = int(cast[ptr int64](a +% n.offset)[]) else: assert(false) return d diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 307fe2382..e1c59803d 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -69,10 +69,25 @@ template mangleJsName(name: cstring): cstring = inc nameCounter "mangledName" & $nameCounter +# only values that can be mapped 1 to 1 with cstring should be keys: they have an injective function with cstring + +proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importcpp: "parseInt(#)".} + +proc toJsKey*[T: enum](text: cstring, t: type T): T = + T(text.toJsKey(int)) + +proc toJsKey*(text: cstring, t: type cstring): cstring = + text + +proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importcpp: "parseFloat(#)".} + type + JsKey* = concept a, type T + cstring.toJsKey(T) is type(a) + JsObject* = ref object of JsRoot ## Dynamically typed wrapper around a JavaScript object. - JsAssoc*[K, V] = ref object of JsRoot + JsAssoc*[K: JsKey, V] = ref object of JsRoot ## Statically typed wrapper around a JavaScript object. js* = JsObject @@ -104,7 +119,7 @@ type proc newJsObject*: JsObject {. importcpp: "{@}" .} ## Creates a new empty JsObject -proc newJsAssoc*[K, V]: JsAssoc[K, V] {. importcpp: "{@}" .} +proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {. importcpp: "{@}" .} ## Creates a new empty JsAssoc with key type `K` and value type `V`. # Checks @@ -176,21 +191,19 @@ proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .} proc `[]=`*[T](obj: JsObject, field: int, val: T) {. importcpp: setImpl .} ## Set the value of a property of name `field` in a JsObject `obj` to `v`. -proc `[]`*[K: not string, V](obj: JsAssoc[K, V], field: K): V - {. importcpp: getImpl .} - ## Return the value of a property of name `field` from a JsAssoc `obj`. - -proc `[]`*[V](obj: JsAssoc[string, V], field: cstring): V +proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V {. importcpp: getImpl .} ## Return the value of a property of name `field` from a JsAssoc `obj`. -proc `[]=`*[K: not string, V](obj: JsAssoc[K, V], field: K, val: V) +proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V) {. importcpp: setImpl .} ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. -proc `[]=`*[V](obj: JsAssoc[string, V], field: cstring, val: V) - {. importcpp: setImpl .} - ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. +proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = + obj[cstring(field)] + +proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) = + obj[cstring(field)] = val proc `==`*(x, y: JsRoot): bool {. importcpp: "(# === #)" .} ## Compare two JsObjects or JsAssocs. Be careful though, as this is comparison @@ -277,7 +290,7 @@ macro `.()`*(obj: JsObject, result[0][3].add newIdentDefs(paramName, newIdentNode(!"JsObject")) result[1].add args[idx].copyNimTree -macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], +macro `.`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped): V = ## Experimental dot accessor (get) for type JsAssoc. ## Returns the value of a property of name `field` from a JsObject `x`. @@ -293,7 +306,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], {. importcpp: `importString`, gensym .} helper(`obj`) -macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], +macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped, value: V): untyped = ## Experimental dot accessor (set) for type JsAssoc. @@ -310,7 +323,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], {. importcpp: `importString`, gensym .} helper(`obj`, `value`) -macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V], +macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], field: untyped, args: varargs[untyped]): auto = ## Experimental "method call" operator for type JsAssoc. @@ -354,24 +367,18 @@ iterator keys*(obj: JsObject): cstring = yield k {.emit: "}".} -iterator pairs*[K, V](assoc: JsAssoc[K, V]): (K,V) = +iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) = ## Yields tuples of type ``(K, V)``, with the first entry ## being a `key` in the JsAssoc and the second being its corresponding value. - when K is string: - var k: cstring - else: - var k: K + var k: cstring var v: V {.emit: "for (var `k` in `assoc`) {".} {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} {.emit: " `v`=`assoc`[`k`];".} - when K is string: - yield ($k, v) - else: - yield (k, v) + yield (k.toJsKey(K), v) {.emit: "}".} -iterator items*[K,V](assoc: JSAssoc[K,V]): V = +iterator items*[K, V](assoc: JSAssoc[K, V]): V = ## Yields the `values` in a JsAssoc. var v: V {.emit: "for (var k in `assoc`) {".} @@ -380,18 +387,12 @@ iterator items*[K,V](assoc: JSAssoc[K,V]): V = yield v {.emit: "}".} -iterator keys*[K,V](assoc: JSAssoc[K,V]): K = +iterator keys*[K: JsKey, V](assoc: JSAssoc[K, V]): K = ## Yields the `keys` in a JsAssoc. - when K is string: - var k: cstring - else: - var k: K + var k: cstring {.emit: "for (var `k` in `assoc`) {".} {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} - when K is string: - yield $k - else: - yield k + yield k.toJsKey(K) {.emit: "}".} # Literal generation diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim index 4e4cf7e0e..335207f54 100644 --- a/lib/nimrtl.nim +++ b/lib/nimrtl.nim @@ -33,4 +33,3 @@ when not defined(createNimRtl): import parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes, os, osproc, times - diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 0c66aa2b9..175f6a61d 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -823,6 +823,12 @@ proc CMSG_NXTHDR*(mhdr: ptr Tmsghdr, cmsg: ptr Tcmsghdr): ptr Tcmsghdr {. proc CMSG_FIRSTHDR*(mhdr: ptr Tmsghdr): ptr Tcmsghdr {. importc, header: "<sys/socket.h>".} +proc CMSG_SPACE*(len: csize): csize {. + importc, header: "<sys/socket.h>".} + +proc CMSG_LEN*(len: csize): csize {. + importc, header: "<sys/socket.h>".} + const INVALID_SOCKET* = SocketHandle(-1) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 59ef06459..e33ddeaf0 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -218,7 +218,7 @@ when defineSsl: var data = await recv(socket.fd.AsyncFD, BufferSize, flags) let length = len(data) if length > 0: - let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint) + let ret = bioWrite(socket.bioIn, addr data[0], length.cint) if ret < 0: raiseSSLError() elif length == 0: diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index e8ea675f5..be10780ff 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -504,36 +504,76 @@ template anyIt*(s, pred: untyped): bool = break result -template toSeq*(iter: untyped): untyped = - ## Transforms any iterator into a sequence. - ## - ## Example: - ## - ## .. code-block:: - ## let - ## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - ## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: - ## if x mod 2 == 1: - ## result = true) - ## assert odd_numbers == @[1, 3, 5, 7, 9] - - # Note: see also `mapIt` for explanation of some of the implementation - # subtleties. - when compiles(iter.len): +template toSeq1(s: not iterator): untyped = + # overload for typed but not iterator + type outType = type(items(s)) + when compiles(s.len): block: - evalOnceAs(iter2, iter, true) - var result = newSeq[type(iter)](iter2.len) + evalOnceAs(s2, s, compiles((let _ = s))) var i = 0 - for x in iter2: - result[i] = x - inc i + var result = newSeq[outType](s2.len) + for it in s2: + result[i] = it + i += 1 result else: - var result: seq[type(iter)] = @[] - for x in iter: - result.add(x) + var result: seq[outType] = @[] + for it in s: + result.add(it) + result + +template toSeq2(iter: iterator): untyped = + # overload for iterator + evalOnceAs(iter2, iter(), false) + when compiles(iter2.len): + var i = 0 + var result = newSeq[type(iter2)](iter2.len) + for x in iter2: + result[i] = x + inc i + result + else: + type outType = type(iter2()) + var result: seq[outType] = @[] + when compiles(iter2()): + evalOnceAs(iter4, iter, false) + let iter3=iter4() + for x in iter3(): + result.add(x) + else: + for x in iter2(): + result.add(x) result +template toSeq*(iter: untyped): untyped = + ## Transforms any iterable into a sequence. + runnableExamples: + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric, proc(x: int): bool = x mod 2 == 1)) + doAssert odd_numbers == @[1, 3, 5, 7, 9] + + when compiles(toSeq1(iter)): + toSeq1(iter) + elif compiles(toSeq2(iter)): + toSeq2(iter) + else: + # overload for untyped, eg: `toSeq(myInlineIterator(3))` + when compiles(iter.len): + block: + evalOnceAs(iter2, iter, true) + var result = newSeq[type(iter)](iter2.len) + var i = 0 + for x in iter2: + result[i] = x + inc i + result + else: + var result: seq[type(iter)] = @[] + for x in iter: + result.add(x) + result + template foldl*(sequence, operation: untyped): untyped = ## Template to fold a sequence from left to right, returning the accumulation. ## @@ -1033,12 +1073,72 @@ when isMainModule: assert anyIt(anumbers, it > 9) == false block: # toSeq test - let - numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: - if x mod 2 == 1: - result = true) - assert odd_numbers == @[1, 3, 5, 7, 9] + block: + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: + if x mod 2 == 1: + result = true) + assert odd_numbers == @[1, 3, 5, 7, 9] + + block: + doAssert [1,2].toSeq == @[1,2] + doAssert @[1,2].toSeq == @[1,2] + + doAssert @[1,2].toSeq == @[1,2] + doAssert toSeq(@[1,2]) == @[1,2] + + block: + iterator myIter(seed:int):auto= + for i in 0..<seed: + yield i + doAssert toSeq(myIter(2)) == @[0, 1] + + block: + iterator myIter():auto{.inline.}= + yield 1 + yield 2 + + doAssert myIter.toSeq == @[1,2] + doAssert toSeq(myIter) == @[1,2] + + block: + iterator myIter():int {.closure.} = + yield 1 + yield 2 + + doAssert myIter.toSeq == @[1,2] + doAssert toSeq(myIter) == @[1,2] + + block: + proc myIter():auto= + iterator ret():int{.closure.}= + yield 1 + yield 2 + result = ret + + doAssert myIter().toSeq == @[1,2] + doAssert toSeq(myIter()) == @[1,2] + + block: + proc myIter(n:int):auto= + var counter = 0 + iterator ret():int{.closure.}= + while counter<n: + yield counter + counter.inc + result = ret + + block: + let myIter3 = myIter(3) + doAssert myIter3.toSeq == @[0,1,2] + block: + let myIter3 = myIter(3) + doAssert toSeq(myIter3) == @[0,1,2] + block: + # makes sure this does not hang forever + doAssert myIter(3).toSeq == @[0,1,2] + doAssert toSeq(myIter(3)) == @[0,1,2] block: # tests https://github.com/nim-lang/Nim/issues/7187 diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 841985605..b7498b1c5 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -1208,7 +1208,9 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, for i in 1..client.maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) - result = await client.requestAux(redirectTo, httpMethod, body, headers) + # Guarantee method for HTTP 307: see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307 + var meth = if result.status == "307": httpMethod else: "GET" + result = await client.requestAux(redirectTo, meth, body, headers) lastURL = redirectTo @@ -1227,36 +1229,49 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## be closed. result = await request(client, url, $httpMethod, body, headers) +proc responseContent(resp: Response | AsyncResponse): Future[string] {.multisync.} = + ## Returns the content of a response as a string. + ## + ## A ``HttpRequestError`` will be raised if the server responds with a + ## client error (status code 4xx) or a server error (status code 5xx). + if resp.code.is4xx or resp.code.is5xx: + raise newException(HttpRequestError, resp.status) + else: + return await resp.bodyStream.readAll() + +proc head*(client: HttpClient | AsyncHttpClient, + url: string): Future[Response | AsyncResponse] {.multisync.} = + ## Connects to the hostname specified by the URL and performs a HEAD request. + ## + ## This procedure uses httpClient values such as ``client.maxRedirects``. + result = await client.request(url, HttpHEAD) + proc get*(client: HttpClient | AsyncHttpClient, url: string): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a GET request. ## - ## This procedure will follow redirects up to a maximum number of redirects - ## specified in ``client.maxRedirects``. + ## This procedure uses httpClient values such as ``client.maxRedirects``. result = await client.request(url, HttpGET) proc getContent*(client: HttpClient | AsyncHttpClient, url: string): Future[string] {.multisync.} = - ## Connects to the hostname specified by the URL and performs a GET request. - ## - ## This procedure will follow redirects up to a maximum number of redirects - ## specified in ``client.maxRedirects``. - ## - ## A ``HttpRequestError`` will be raised if the server responds with a - ## client error (status code 4xx) or a server error (status code 5xx). + ## Connects to the hostname specified by the URL and returns the content of a GET request. let resp = await get(client, url) - if resp.code.is4xx or resp.code.is5xx: - raise newException(HttpRequestError, resp.status) - else: - return await resp.bodyStream.readAll() + return await responseContent(resp) -proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", - multipart: MultipartData = nil): Future[Response | AsyncResponse] - {.multisync.} = - ## Connects to the hostname specified by the URL and performs a POST request. - ## - ## This procedure will follow redirects up to a maximum number of redirects - ## specified in ``client.maxRedirects``. +proc delete*(client: HttpClient | AsyncHttpClient, + url: string): Future[Response | AsyncResponse] {.multisync.} = + ## Connects to the hostname specified by the URL and performs a DELETE request. + ## This procedure uses httpClient values such as ``client.maxRedirects``. + result = await client.request(url, HttpDELETE) + +proc deleteContent*(client: HttpClient | AsyncHttpClient, + url: string): Future[string] {.multisync.} = + ## Connects to the hostname specified by the URL and returns the content of a DELETE request. + let resp = await delete(client, url) + return await responseContent(resp) + +proc makeRequestContent(body = "", multipart: MultipartData = nil): (string, HttpHeaders) = let (mpContentType, mpBody) = format(multipart) # TODO: Support FutureStream for `body` parameter. template withNewLine(x): untyped = @@ -1265,38 +1280,59 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", else: x var xb = mpBody.withNewLine() & body - var headers = newHttpHeaders() if multipart != nil: headers["Content-Type"] = mpContentType headers["Content-Length"] = $len(xb) + return (xb, headers) - result = await client.requestAux(url, $HttpPOST, xb, headers) - # Handle redirects. - var lastURL = url - for i in 1..client.maxRedirects: - if result.status.redirection(): - let redirectTo = getNewLocation(lastURL, result.headers) - var meth = if result.status != "307": HttpGet else: HttpPost - result = await client.requestAux(redirectTo, $meth, xb, headers) - lastURL = redirectTo +proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", + multipart: MultipartData = nil): Future[Response | AsyncResponse] + {.multisync.} = + ## Connects to the hostname specified by the URL and performs a POST request. + ## This procedure uses httpClient values such as ``client.maxRedirects``. + var (xb, headers) = makeRequestContent(body, multipart) + result = await client.request(url, $HttpPOST, xb, headers) proc postContent*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[string] {.multisync.} = - ## Connects to the hostname specified by the URL and performs a POST request. - ## - ## This procedure will follow redirects up to a maximum number of redirects - ## specified in ``client.maxRedirects``. - ## - ## A ``HttpRequestError`` will be raised if the server responds with a - ## client error (status code 4xx) or a server error (status code 5xx). + ## Connects to the hostname specified by the URL and returns the content of a POST request. let resp = await post(client, url, body, multipart) - if resp.code.is4xx or resp.code.is5xx: - raise newException(HttpRequestError, resp.status) - else: - return await resp.bodyStream.readAll() + return await responseContent(resp) + +proc put*(client: HttpClient | AsyncHttpClient, url: string, body = "", + multipart: MultipartData = nil): Future[Response | AsyncResponse] + {.multisync.} = + ## Connects to the hostname specified by the URL and performs a PUT request. + ## This procedure uses httpClient values such as ``client.maxRedirects``. + var (xb, headers) = makeRequestContent(body, multipart) + result = await client.request(url, $HttpPUT, xb, headers) + +proc putContent*(client: HttpClient | AsyncHttpClient, url: string, + body = "", + multipart: MultipartData = nil): Future[string] + {.multisync.} = + ## Connects to the hostname specified by the URL andreturns the content of a PUT request. + let resp = await put(client, url, body, multipart) + return await responseContent(resp) + +proc patch*(client: HttpClient | AsyncHttpClient, url: string, body = "", + multipart: MultipartData = nil): Future[Response | AsyncResponse] + {.multisync.} = + ## Connects to the hostname specified by the URL and performs a PATCH request. + ## This procedure uses httpClient values such as ``client.maxRedirects``. + var (xb, headers) = makeRequestContent(body, multipart) + result = await client.request(url, $HttpPATCH, xb, headers) + +proc patchContent*(client: HttpClient | AsyncHttpClient, url: string, + body = "", + multipart: MultipartData = nil): Future[string] + {.multisync.} = + ## Connects to the hostname specified by the URL and returns the content of a PATCH request. + let resp = await patch(client, url, body, multipart) + return await responseContent(resp) proc downloadFile*(client: HttpClient, url: string, filename: string) = ## Downloads ``url`` and saves it to ``filename``. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 0e43e18ca..31610a59e 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2424,6 +2424,15 @@ proc isHidden*(path: string): bool {.noNimScript.} = let fileName = lastPathPart(path) result = len(fileName) >= 2 and fileName[0] == '.' and fileName != ".." +proc getCurrentProcessId*(): int {.noNimScript.} = + ## return current process ID. See also ``osproc.processID(p: Process)``. + when defined(windows): + proc GetCurrentProcessId(): DWORD {.stdcall, dynlib: "kernel32", + importc: "GetCurrentProcessId".} + result = GetCurrentProcessId().int + else: + result = getpid() + {.pop.} proc setLastModificationTime*(file: string, t: times.Time) {.noNimScript.} = diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 0cf2171de..b2239b9c5 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -64,6 +64,7 @@ const poUseShell* {.deprecated.} = poUsePath ## Deprecated alias for poUsePath. proc execProcess*(command: string, + workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut, @@ -154,7 +155,7 @@ proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].} ## Returns true iff the process `p` is still running. Returns immediately. proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = - ## returns `p`'s process ID. + ## returns `p`'s process ID. See also ``os.getCurrentProcessId()``. return p.id proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, @@ -349,12 +350,13 @@ proc select*(readfds: var seq[Process], timeout = 500): int when not defined(useNimRtl): proc execProcess(command: string, + workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}): TaintedString = - var p = startProcess(command, args=args, env=env, options=options) + var p = startProcess(command, workingDir=workingDir, args=args, env=env, options=options) var outp = outputStream(p) result = TaintedString"" var line = newStringOfCap(120).TaintedString @@ -1323,6 +1325,12 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## let (outp, errC) = execCmdEx("nim c -r mytestfile.nim") var p = startProcess(command, options=options + {poEvalCommand}) var outp = outputStream(p) + + # There is no way to provide input for the child process + # anymore. Closing it will create EOF on stdin instead of eternal + # blocking. + close inputStream(p) + result = (TaintedString"", -1) var line = newStringOfCap(120).TaintedString while true: @@ -1333,3 +1341,4 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { result[1] = peekExitCode(p) if result[1] != -1: break close(p) + diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index f68baaf6b..fb4bc19af 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -129,8 +129,8 @@ proc parseIdent*(s: string, ident: var string, start = 0): int = result = i-start proc parseIdent*(s: string, start = 0): string = - ## parses an identifier and stores it in ``ident``. - ## Returns the parsed identifier or an empty string in case of an error. + ## parses an identifier and returns it or an empty string in + ## case of an error. result = "" var i = start if i < s.len and s[i] in IdentStartChars: diff --git a/lib/pure/random.nim b/lib/pure/random.nim index a2c2c1f88..c458d51eb 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -231,4 +231,8 @@ when isMainModule: except RangeError: discard + + # don't use causes integer overflow + doAssert compiles(random[int](low(int) .. high(int))) + main() diff --git a/lib/pure/times.nim b/lib/pure/times.nim index b8f76276a..010551b5a 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -22,7 +22,7 @@ let time = cpuTime() sleep(100) # replace this with something to be timed - echo "Time taken: ",cpuTime() - time + echo "Time taken: ", cpuTime() - time echo "My formatted time: ", format(now(), "d MMMM yyyy HH:mm") echo "Using predefined formats: ", getClockStr(), " ", getDateStr() @@ -158,7 +158,9 @@ when defined(posix): type CTime = posix.Time - var CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid + var + realTimeClockId {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid + cpuClockId {.importc: "CLOCK_THREAD_CPUTIME_ID", header: "<time.h>".}: Clockid proc gettimeofday(tp: var Timeval, unused: pointer = nil) {. importc: "gettimeofday", header: "<sys/time.h>".} @@ -745,8 +747,7 @@ proc abs*(a: Duration): Duration = initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanosecond) proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = - ## Converts a broken-down time structure to - ## calendar time representation. + ## Converts a ``DateTime`` to a ``Time`` representing the same point in time. let epochDay = toEpochday(dt.monthday, dt.month, dt.year) var seconds = epochDay * secondsInDay seconds.inc dt.hour * secondsInHour @@ -840,6 +841,11 @@ proc `$`*(zone: Timezone): string = proc `==`*(zone1, zone2: Timezone): bool = ## Two ``Timezone``'s are considered equal if their name is equal. + if system.`==`(zone1, zone2): + return true + if zone1.isNil or zone2.isNil: + return false + runnableExamples: doAssert local() == local() doAssert local() != utc() @@ -1047,7 +1053,7 @@ proc local*(t: Time): DateTime = t.inZone(local()) proc getTime*(): Time {.tags: [TimeEffect], benign.} = - ## Gets the current time as a ``Time`` with nanosecond resolution. + ## Gets the current time as a ``Time`` with up to nanosecond resolution. when defined(JS): let millis = newDate().getTime() let seconds = convert(Milliseconds, Seconds, millis) @@ -1061,7 +1067,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} = result = initTime(a.tv_sec.int64, convert(Microseconds, Nanoseconds, a.tv_usec.int)) elif defined(posix): var ts: Timespec - discard clock_gettime(CLOCK_REALTIME, ts) + discard clock_gettime(realTimeClockId, ts) result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) elif defined(windows): var f: FILETIME @@ -1141,16 +1147,16 @@ proc `-`*(ti1, ti2: TimeInterval): TimeInterval = result = ti1 + (-ti2) proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current date as a string of the format ``YYYY-MM-DD``. - var ti = now() - result = $ti.year & '-' & intToStr(ord(ti.month), 2) & - '-' & intToStr(ti.monthday, 2) + ## Gets the current local date as a string of the format ``YYYY-MM-DD``. + var dt = now() + result = $dt.year & '-' & intToStr(ord(dt.month), 2) & + '-' & intToStr(dt.monthday, 2) proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current clock time as a string of the format ``HH:MM:SS``. - var ti = now() - result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) & - ':' & intToStr(ti.second, 2) + ## Gets the current local clock time as a string of the format ``HH:MM:SS``. + var dt = now() + result = intToStr(dt.hour, 2) & ':' & intToStr(dt.minute, 2) & + ':' & intToStr(dt.second, 2) proc toParts* (ti: TimeInterval): TimeIntervalParts = ## Converts a `TimeInterval` into an array consisting of its time units, @@ -1382,7 +1388,6 @@ proc `==`*(a, b: DateTime): bool = ## Returns true if ``a == b``, that is if both dates represent the same point in time. return a.toTime == b.toTime - proc isStaticInterval(interval: TimeInterval): bool = interval.years == 0 and interval.months == 0 and interval.days == 0 and interval.weeks == 0 @@ -1397,28 +1402,20 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration = hours = interval.hours) proc between*(startDt, endDt: DateTime): TimeInterval = - ## Evaluate difference between two dates in ``TimeInterval`` format, so, it - ## will be relative. + ## Gives the difference between ``startDt`` and ``endDt`` as a + ## ``TimeInterval``. ## - ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in - ## different ``TimeZone's``. - ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC. + ## **Warning:** This proc currently gives very few guarantees about the + ## result. ``a + between(a, b) == b`` is **not** true in general + ## (it's always true when UTC is used however). Neither is it guaranteed that + ## all components in the result will have the same sign. The behavior of this + ## proc might change in the future. runnableExamples: - var a = initDateTime(year = 2018, month = Month(3), monthday = 25, - hour = 0, minute = 59, second = 59, nanosecond = 1, - zone = utc()).local - var b = initDateTime(year = 2018, month = Month(3), monthday = 25, - hour = 1, minute = 1, second = 1, nanosecond = 0, - zone = utc()).local - doAssert between(a, b) == initTimeInterval( - nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1) - - a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc()) - b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz") - doAssert between(a, b) == initTimeInterval(hours=1, days=2) - ## Though, here correct answer should be 1 day 25 hours (cause this day in - ## this tz is actually 26 hours). That's why operating different TZ is - ## discouraged + var a = initDateTime(25, mMar, 2015, 12, 0, 0, utc()) + var b = initDateTime(1, mApr, 2017, 15, 0, 15, utc()) + var ti = initTimeInterval(years = 2, days = 7, hours = 3, seconds = 15) + doAssert between(a, b) == ti + doAssert between(a, b) == -between(b, a) var startDt = startDt.utc() var endDt = endDt.utc() @@ -1546,7 +1543,6 @@ proc `*=`*[T: TimesMutableTypes, U](a: var T, b: U) = var dur = initDuration(seconds = 1) dur *= 5 doAssert dur == initDuration(seconds = 5) - a = a * b # @@ -1810,7 +1806,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) = of UUUU: result.add $dt.year of z, zz, zzz, zzzz: - if dt.timezone.name == "Etc/UTC": + if dt.timezone != nil and dt.timezone.name == "Etc/UTC": result.add 'Z' else: result.add if -dt.utcOffset >= 0: '+' else: '-' @@ -2343,7 +2339,15 @@ when not defined(JS): fib.add(fib[^1] + fib[^2]) echo "CPU time [s] ", cpuTime() - t0 echo "Fib is [s] ", fib - result = toFloat(int(getClock())) / toFloat(clocksPerSec) + when defined(posix): + # 'clocksPerSec' is a compile-time constant, possibly a + # rather awful one, so use clock_gettime instead + var ts: Timespec + discard clock_gettime(cpuClockId, ts) + result = toFloat(ts.tv_sec.int) + + toFloat(ts.tv_nsec.int) / 1_000_000_000 + else: + result = toFloat(int(getClock())) / toFloat(clocksPerSec) proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} = ## gets time after the UNIX epoch (1970) in seconds. It is a float diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 664765954..712cc46c8 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1941,9 +1941,9 @@ proc strip*(s: string, leading = true, trailing = true, e_i = l_i - 1 break dec(i) - let newLen = e_i - s_i + let newLen = e_i - s_i + 1 result = newStringOfCap(newLen) - if e_i > s_i: + if newLen > 0: result.add s[s_i .. e_i] proc repeat*(c: Rune, count: Natural): string {.noSideEffect, @@ -2161,6 +2161,9 @@ when isMainModule: doAssert s.split(' '.Rune, maxsplit = 1) == @["", "this is an example "] block stripTests: + doAssert(strip("") == "") + doAssert(strip(" ") == "") + doAssert(strip("y") == "y") doAssert(strip(" foofoofoo ") == "foofoofoo") doAssert(strip("sfoofoofoos", runes = ['s'.Rune]) == "foofoofoo") diff --git a/lib/system.nim b/lib/system.nim index ace3f5e38..ea767e27a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -373,7 +373,7 @@ when defined(nimArrIdx): x: S) {.noSideEffect, magic: "ArrPut".} when defined(nimNewRuntime): - proc `=destroy`*[T](x: var T) {.inline, magic: "Asgn".} = + proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} = ## generic `destructor`:idx: implementation that can be overriden. discard proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} = @@ -2522,12 +2522,13 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} = when not defined(JS): proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = result = cast[pointer](x) - else: - proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} = - asm """return `x`""" - if seqToPtr(x) == seqToPtr(y): - return true + if seqToPtr(x) == seqToPtr(y): + return true + else: + var sameObject = false + asm """`sameObject` = `x` === `y`""" + if sameObject: return true when not defined(nimNoNil): if x.isNil or y.isNil: @@ -3460,6 +3461,7 @@ when not defined(JS): #and not defined(nimscript): of 1: d = ze(cast[ptr int8](a +% n.offset)[]) of 2: d = ze(cast[ptr int16](a +% n.offset)[]) of 4: d = int(cast[ptr int32](a +% n.offset)[]) + of 8: d = int(cast[ptr int64](a +% n.offset)[]) else: sysAssert(false, "getDiscriminant: invalid n.typ.size") return d @@ -4017,6 +4019,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not ## perform deep copies of `s`. This is only useful for optimization ## purposes. + if s.len == 0: return when not defined(JS) and not defined(nimscript): var s = cast[PGenericSeq](s) s.reserved = s.reserved or seqShallowFlag @@ -4027,6 +4030,8 @@ proc shallow*(s: var string) {.noSideEffect, inline.} = ## purposes. when not defined(JS) and not defined(nimscript) and not defined(gcDestructors): var s = cast[PGenericSeq](s) + if s == nil: + s = cast[PGenericSeq](newString(0)) # string literals cannot become 'shallow': if (s.reserved and strlitFlag) == 0: s.reserved = s.reserved or seqShallowFlag @@ -4206,6 +4211,10 @@ when hasAlloc and not defined(nimscript) and not defined(JS) and ## for the implementation of ``spawn``. discard + proc deepCopy*[T](y: T): T = + ## Convenience wrapper around `deepCopy` overload. + deepCopy(result, y) + include "system/deepcopy" proc procCall*(x: untyped) {.magic: "ProcCall", compileTime.} = diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index d6c361b2c..70bdc429b 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -17,13 +17,6 @@ const NilLibHandle: LibHandle = nil -proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {. - importc: "fwrite", header: "<stdio.h>".} - -proc rawWrite(f: File, s: string|cstring) = - # we cannot throw an exception here! - discard c_fwrite(cstring(s), 1, s.len, f) - proc nimLoadLibraryError(path: string) = # carefully written to avoid memory allocation: stderr.rawWrite("could not load: ") diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 778137668..a6da8f5a3 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -17,8 +17,15 @@ var ## instead of stdmsg.write when printing stacktrace. ## Unstable API. +proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {. + importc: "fwrite", header: "<stdio.h>".} + +proc rawWrite(f: File, s: string|cstring) = + # we cannot throw an exception here! + discard c_fwrite(cstring(s), 1, s.len, f) + when not defined(windows) or not defined(guiapp): - proc writeToStdErr(msg: cstring) = write(stdmsg, msg) + proc writeToStdErr(msg: cstring) = rawWrite(stdmsg, msg) else: proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {. diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 74ac68eea..fb20edbbb 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -100,14 +100,6 @@ var when not defined(useNimRtl): instantiateForRegion(gch.region) -template acquire(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) - -template release(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) - template gcAssert(cond: bool, msg: string) = when defined(useGcAssert): if not cond: @@ -177,15 +169,6 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} # we need the prototype here for debugging purposes -when hasThreadSupport and hasSharedHeap: - template `--`(x: untyped): untyped = atomicDec(x, rcIncrement) <% rcIncrement - template `++`(x: untyped) = discard atomicInc(x, rcIncrement) -else: - template `--`(x: untyped): untyped = - dec(x, rcIncrement) - x <% rcIncrement - template `++`(x: untyped) = inc(x, rcIncrement) - proc incRef(c: PCell) {.inline.} = gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr") c.refcount = c.refcount +% rcIncrement @@ -194,28 +177,19 @@ proc incRef(c: PCell) {.inline.} = proc nimGCref(p: pointer) {.compilerProc.} = # we keep it from being collected by pretending it's not even allocated: - add(gch.additionalRoots, usrToCell(p)) - incRef(usrToCell(p)) - -proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = - # we MUST access gch as a global here, because this crosses DLL boundaries! - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) + let c = usrToCell(p) + add(gch.additionalRoots, c) + incRef(c) proc rtlAddZCT(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) addZCT(gch.zct, c) - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) proc decRef(c: PCell) {.inline.} = gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr") gcAssert(c.refcount >=% rcIncrement, "decRef") - if --c.refcount: + c.refcount = c.refcount -% rcIncrement + if c.refcount <% rcIncrement: rtlAddZCT(c) proc nimGCunref(p: pointer) {.compilerProc.} = @@ -239,19 +213,9 @@ template beforeDealloc(gch: var GcHeap; c: PCell; msg: typed) = if gch.decStack.d[i] == c: sysAssert(false, msg) -proc GC_addCycleRoot*[T](p: ref T) {.inline.} = - ## adds 'p' to the cycle candidate set for the cycle collector. It is - ## necessary if you used the 'acyclic' pragma for optimization - ## purposes and need to break cycles manually. - rtlAddCycleRoot(usrToCell(cast[pointer](p))) - proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle") - var c = usrToCell(p) - gcAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr") - if --c.refcount: - rtlAddZCT(c) - sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2") + decRef(usrToCell(p)) sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5") proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} = @@ -265,17 +229,8 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = if dest[] != nil: decRef(usrToCell(dest[])) dest[] = src -proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = - # the code generator calls this proc if it is known at compile time that no - # cycle is possible. - if src != nil: - var c = usrToCell(src) - ++c.refcount - if dest[] != nil: - var c = usrToCell(dest[]) - if --c.refcount: - rtlAddZCT(c) - dest[] = src +proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} = # unsureAsgnRef updates the reference counters only if dest is not on the @@ -440,7 +395,6 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = # generates a new object and sets its reference counter to 0 incTypeSize typ, size sysAssert(allocInv(gch.region), "rawNewObj begin") - acquire(gch) gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) @@ -457,7 +411,6 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = when logGC: writeCell("new cell", res) track("rawNewObj", res, size) gcTrace(res, csAllocated) - release(gch) when useCellIds: inc gch.idGenerator res.id = gch.idGenerator * 1000_000 + gch.gcThreadId @@ -488,7 +441,6 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = # generates a new object and sets its reference counter to 1 incTypeSize typ, size sysAssert(allocInv(gch.region), "newObjRC1 begin") - acquire(gch) gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") @@ -504,7 +456,6 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = when logGC: writeCell("new cell", res) track("newObjRC1", res, size) gcTrace(res, csAllocated) - release(gch) when useCellIds: inc gch.idGenerator res.id = gch.idGenerator * 1000_000 + gch.gcThreadId @@ -521,7 +472,6 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = when defined(memProfiler): nimProfile(size) proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = - acquire(gch) collectCT(gch) var ol = usrToCell(old) sysAssert(ol.typ != nil, "growObj: 1") @@ -577,7 +527,6 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = else: sysAssert(ol.typ != nil, "growObj: 5") zeroMem(ol, sizeof(Cell)) - release(gch) when useCellIds: inc gch.idGenerator res.id = gch.idGenerator * 1000_000 + gch.gcThreadId @@ -665,11 +614,9 @@ proc doOperation(p: pointer, op: WalkOp) = # c_fprintf(stdout, "[GC] decref bug: %p", c) gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef") gcAssert(c.refcount >=% rcIncrement, "doOperation 2") - #c.refcount = c.refcount -% rcIncrement when logGC: writeCell("decref (from doOperation)", c) track("waZctDecref", p, 0) decRef(c) - #if c.refcount <% rcIncrement: addZCT(gch.zct, c) of waPush: add(gch.tempStack, c) of waMarkGlobal: @@ -707,13 +654,13 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) if objStart != nil: # mark the cell: - objStart.refcount = objStart.refcount +% rcIncrement + incRef(objStart) add(gch.decStack, objStart) when false: if isAllocatedPtr(gch.region, cell): sysAssert false, "allocated pointer but not interior?" # mark the cell: - cell.refcount = cell.refcount +% rcIncrement + incRef(cell) add(gch.decStack, cell) sysAssert(allocInv(gch.region), "gcMark end") @@ -787,11 +734,6 @@ proc unmarkStackAndRegisters(gch: var GcHeap) = for i in 0..gch.decStack.len-1: sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters" decRef(d[i]) - #var c = d[i] - # XXX no need for an atomic dec here: - #if --c.refcount: - # addZCT(gch.zct, c) - #sysAssert c.typ != nil, "unmarkStackAndRegisters 2" gch.decStack.len = 0 proc collectCTBody(gch: var GcHeap) = @@ -850,13 +792,11 @@ when withRealTime: gch.maxPause = MaxPauseInUs.toNano proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) = - acquire(gch) gch.maxPause = us.toNano if (gch.zct.len >= ZctThreshold or (cycleGC and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or strongAdvice: collectCTBody(gch) - release(gch) proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} = if stackSize >= 0: @@ -880,18 +820,12 @@ when withRealTime: when not defined(useNimRtl): proc GC_disable() = - when hasThreadSupport and hasSharedHeap: - discard atomicInc(gch.recGcLock, 1) - else: - inc(gch.recGcLock) + inc(gch.recGcLock) proc GC_enable() = if gch.recGcLock <= 0: raise newException(AssertionError, "API usage error: GC_enable called but GC is already enabled") - when hasThreadSupport and hasSharedHeap: - discard atomicDec(gch.recGcLock, 1) - else: - dec(gch.recGcLock) + dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard @@ -904,12 +838,10 @@ when not defined(useNimRtl): # set to the max value to suppress the cycle detector proc GC_fullCollect() = - acquire(gch) var oldThreshold = gch.cycleThreshold gch.cycleThreshold = 0 # forces cycle collection collectCT(gch) gch.cycleThreshold = oldThreshold - release(gch) proc GC_getStatistics(): string = result = "[GC] total memory: " & $(getTotalMem()) & "\n" & diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 283919503..522b2742b 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -112,14 +112,6 @@ var when not defined(useNimRtl): instantiateForRegion(gch.region) -template acquire(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) - -template release(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) - # Which color to use for new objects is tricky: When we're marking, # they have to be *white* so that everything is marked that is only # reachable from them. However, when we are sweeping, they have to @@ -204,10 +196,6 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} # we need the prototype here for debugging purposes -proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = - # we MUST access gch as a global here, because this crosses DLL boundaries! - discard - proc nimGCref(p: pointer) {.compilerProc.} = let cell = usrToCell(p) markAsEscaped(cell) @@ -240,13 +228,8 @@ template markGrey(x: PCell) = x.setColor(rcGrey) add(gch.greyStack, x) -proc GC_addCycleRoot*[T](p: ref T) {.inline.} = - ## adds 'p' to the cycle candidate set for the cycle collector. It is - ## necessary if you used the 'acyclic' pragma for optimization - ## purposes and need to break cycles manually. - discard - -template asgnRefImpl = +proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = + # the code generator calls this proc! gcAssert(not isOnStack(dest), "asgnRef") # BUGFIX: first incRef then decRef! if src != nil: @@ -255,12 +238,8 @@ template asgnRefImpl = markGrey(s) dest[] = src -proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = - # the code generator calls this proc! - asgnRefImpl() - -proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = - asgnRefImpl() +proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} = # unsureAsgnRef marks 'src' as grey only if dest is not on the @@ -396,7 +375,6 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = result = newSeq(typ, len) proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = - acquire(gch) collectCT(gch) var ol = usrToCell(old) sysAssert(ol.typ != nil, "growObj: 1") @@ -420,7 +398,6 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = when useCellIds: inc gch.idGenerator res.id = gch.idGenerator - release(gch) result = cellToUsr(res) when defined(memProfiler): nimProfile(newsize-oldsize) @@ -732,16 +709,10 @@ when withRealTime: when not defined(useNimRtl): proc GC_disable() = - when hasThreadSupport and hasSharedHeap: - discard atomicInc(gch.recGcLock, 1) - else: - inc(gch.recGcLock) + inc(gch.recGcLock) proc GC_enable() = if gch.recGcLock > 0: - when hasThreadSupport and hasSharedHeap: - discard atomicDec(gch.recGcLock, 1) - else: - dec(gch.recGcLock) + dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 96221b175..d8cb3e2d0 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -85,14 +85,6 @@ var when not defined(useNimRtl): instantiateForRegion(gch.region) -template acquire(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - acquireSys(HeapLock) - -template release(gch: GcHeap) = - when hasThreadSupport and hasSharedHeap: - releaseSys(HeapLock) - template gcAssert(cond: bool, msg: string) = when defined(useGcAssert): if not cond: @@ -276,7 +268,6 @@ proc forAllChildren(cell: PCell, op: WalkOp) = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = # generates a new object and sets its reference counter to 0 incTypeSize typ, size - acquire(gch) gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch, size + sizeof(Cell)) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) @@ -288,7 +279,6 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = res.filename = framePtr.prev.filename res.line = framePtr.prev.line res.refcount = 0 - release(gch) when withBitvectors: incl(gch.allocated, res) when useCellIds: inc gch.idGenerator @@ -333,7 +323,6 @@ when not defined(gcDestructors): when defined(memProfiler): nimProfile(size) proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = - acquire(gch) collectCT(gch, newsize + sizeof(Cell)) var ol = usrToCell(old) sysAssert(ol.typ != nil, "growObj: 1") @@ -353,7 +342,6 @@ when not defined(gcDestructors): when useCellIds: inc gch.idGenerator res.id = gch.idGenerator - release(gch) result = cellToUsr(res) when defined(memProfiler): nimProfile(newsize-oldsize) @@ -503,18 +491,12 @@ proc collectCT(gch: var GcHeap; size: int) = when not defined(useNimRtl): proc GC_disable() = - when hasThreadSupport and hasSharedHeap: - atomicInc(gch.recGcLock, 1) - else: - inc(gch.recGcLock) + inc(gch.recGcLock) proc GC_enable() = if gch.recGcLock <= 0: raise newException(AssertionError, "API usage error: GC_enable called but GC is already enabled") - when hasThreadSupport and hasSharedHeap: - atomicDec(gch.recGcLock, 1) - else: - dec(gch.recGcLock) + dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard @@ -530,12 +512,10 @@ when not defined(useNimRtl): gch.tracing = true proc GC_fullCollect() = - acquire(gch) var oldThreshold = gch.cycleThreshold gch.cycleThreshold = 0 # forces cycle collection collectCT(gch, 0) gch.cycleThreshold = oldThreshold - release(gch) proc GC_getStatistics(): string = result = "[GC] total memory: " & $getTotalMem() & "\n" & diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim index 06fded86b..8a1446944 100644 --- a/lib/system/gc_regions.nim +++ b/lib/system/gc_regions.nim @@ -339,8 +339,8 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src -proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src +proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) proc alloc(size: Natural): pointer = result = c_malloc(size) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 5ac0ca8b2..8be19e5b8 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -519,8 +519,11 @@ proc nimCopyAux(dest, src: JSRef, n: ptr TNimNode) {.compilerproc.} = `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); """ of nkList: - for i in 0..n.len-1: - nimCopyAux(dest, src, n.sons[i]) + asm """ + for (var i = 0; i < `n`.sons.length; i++) { + nimCopyAux(`dest`, `src`, `n`.sons[i]); + } + """ of nkCase: asm """ `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 683e84f7d..89bc11d2c 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -170,8 +170,8 @@ when defined(boehmgc): dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src - proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) type MemRegion = object @@ -326,8 +326,8 @@ elif defined(gogc): writebarrierptr(dest, src) proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = writebarrierptr(dest, src) - proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - writebarrierptr(dest, src) + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) type MemRegion = object @@ -416,8 +416,8 @@ elif defined(nogc) and defined(useMalloc): dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src - proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) type MemRegion = object @@ -473,8 +473,8 @@ elif defined(nogc): dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src - proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, + deprecated: "old compiler compat".} = asgnRef(dest, src) var allocator {.rtlThreadVar.}: MemRegion instantiateForRegion(allocator) diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 7cb25a252..fb231bbed 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -64,7 +64,9 @@ proc reprStrAux(result: var string, s: cstring, len: int) = proc reprStr(s: string): string {.compilerRtl.} = result = "" - if cast[pointer](s).isNil: + var sIsNil = false + asm """`sIsNil` = `s` === null""" + if sIsNil: # cast[pointer](s).isNil: # Handle nil strings here because they don't have a length field in js # TODO: check for null/undefined before generating call to length in js? # Also: c backend repr of a nil string is <pointer>"", but repr of an |