summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--appveyor.yml1
-rw-r--r--compiler/ast.nim1
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/pragmas.nim1
-rw-r--r--compiler/semstmts.nim10
-rw-r--r--config/nim.cfg3
-rw-r--r--lib/js/asyncjs.nim4
-rw-r--r--lib/pure/async.nim6
-rw-r--r--lib/pure/asyncnet.nim2
-rw-r--r--lib/pure/net.nim41
-rw-r--r--tests/async/config.nims2
-rw-r--r--tests/async/tjsandnativeasync.nim30
-rw-r--r--tests/gc/gcleak.nim3
-rw-r--r--tests/gc/gcleak2.nim4
-rw-r--r--tests/gc/gcleak4.nim2
-rw-r--r--tests/js/tasync.nim1
-rw-r--r--tests/pragmas/tcustom_pragma.nim10
-rw-r--r--tests/testament/categories.nim3
18 files changed, 98 insertions, 27 deletions
diff --git a/appveyor.yml b/appveyor.yml
index be2cc50d3..a79d32e41 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -24,6 +24,7 @@ environment:
     #   platform: x86
 
 install:
+  - ps: Install-Product node 8 # node 8 or later is required to test js async stuff
   - MKDIR %CD%\DIST
   - MKDIR %CD%\DIST\PCRE
   - nuget install pcre -Verbosity quiet -Version 8.33.0.1 -OutputDirectory %CD%\DIST\PCRE
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 69f2eb1c7..5c70bda18 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -979,6 +979,7 @@ const
   nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
                    nkClosedSymChoice}
 
+  nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
   nkLiterals* = {nkCharLit..nkTripleStrLit}
   nkLambdaKinds* = {nkLambda, nkDo}
   declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index a52214e73..0be2899be 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -112,3 +112,4 @@ proc initDefines*() =
   defineSymbol("nimNewRoof")
   defineSymbol("nimHasRunnableExamples")
   defineSymbol("nimNewDot")
+  defineSymbol("nimHasNilChecks")
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index b6229796f..810c4c416 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -17,7 +17,6 @@ import
 const
   FirstCallConv* = wNimcall
   LastCallConv* = wNoconv
-  nkPragmaCallKinds = {nkExprColonExpr, nkCall, nkCallStrLit}
 
 const
   procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 339c02715..f46d314cc 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1143,7 +1143,7 @@ proc semProcAnnotation(c: PContext, prc: PNode;
   if n == nil or n.kind == nkEmpty: return
   for i in countup(0, n.len-1):
     var it = n.sons[i]
-    var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
+    var key = if it.kind in nkPragmaCallKinds and it.len >= 1: it.sons[0] else: it
     let m = lookupMacro(c, key)
     if m == nil:
       if key.kind == nkIdent and key.ident.id == ord(wDelegator):
@@ -1164,10 +1164,12 @@ proc semProcAnnotation(c: PContext, prc: PNode;
     if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
       prc.sons[pragmasPos] = emptyNode
 
-    if it.kind == nkExprColonExpr:
-      # pass pragma argument to the macro too:
-      x.add(it.sons[1])
+    if it.kind in nkPragmaCallKinds and it.len > 1:
+      # pass pragma arguments to the macro too:
+      for i in 1..<it.len:
+        x.add(it.sons[i])
     x.add(prc)
+    
     # recursion assures that this works for multiple macro annotations too:
     result = semExpr(c, x)
     # since a proc annotation can set pragmas, we process these here again.
diff --git a/config/nim.cfg b/config/nim.cfg
index a146c4ebf..9fddce90a 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -59,6 +59,9 @@ path="$lib/pure"
   debugger:off
   line_dir:off
   dead_code_elim:on
+  @if nimHasNilChecks:
+    nilchecks:off
+  @end
 @end
 
 @if release:
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim
index ec410ee39..3c488dcfe 100644
--- a/lib/js/asyncjs.nim
+++ b/lib/js/asyncjs.nim
@@ -139,3 +139,7 @@ macro async*(arg: untyped): untyped =
 proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".}
   ## A helper for wrapping callback-based functions
   ## into promises and async procedures
+
+proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importcpp: "(new Promise(#))".}
+  ## A helper for wrapping callback-based functions
+  ## into promises and async procedures
diff --git a/lib/pure/async.nim b/lib/pure/async.nim
new file mode 100644
index 000000000..97b29f81d
--- /dev/null
+++ b/lib/pure/async.nim
@@ -0,0 +1,6 @@
+when defined(js):
+    import asyncjs
+    export asyncjs
+else:
+    import asyncmacro, asyncfutures
+    export asyncmacro, asyncfutures
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 5be457d2a..93399bb40 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -277,6 +277,7 @@ template readInto(buf: pointer, size: int, socket: AsyncSocket,
                   flags: set[SocketFlag]): int =
   ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that
   ## this is a template and not a proc.
+  assert(not socket.closed, "Cannot `recv` on a closed socket")
   var res = 0
   if socket.isSsl:
     when defineSsl:
@@ -403,6 +404,7 @@ proc send*(socket: AsyncSocket, buf: pointer, size: int,
   ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all
   ## data has been sent.
   assert socket != nil
+  assert(not socket.closed, "Cannot `send` on a closed socket")
   if socket.isSsl:
     when defineSsl:
       sslLoop(socket, flags,
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 083e70c8d..f348b7c51 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -868,6 +868,7 @@ proc close*(socket: Socket) =
         socket.sslHandle = nil
 
     socket.fd.close()
+    socket.fd = osInvalidSocket
 
 when defined(posix):
   from posix import TCP_NODELAY
@@ -1005,15 +1006,25 @@ proc select(readfd: Socket, timeout = 500): int =
   var fds = @[readfd.fd]
   result = select(fds, timeout)
 
-proc readIntoBuf(socket: Socket, flags: int32): int =
+proc isClosed(socket: Socket): bool =
+  socket.fd == osInvalidSocket
+
+proc uniRecv(socket: Socket, buffer: pointer, size, flags: cint): int =
+  ## Handles SSL and non-ssl recv in a nice package.
+  ##
+  ## In particular handles the case where socket has been closed properly
+  ## for both SSL and non-ssl.
   result = 0
+  assert(not socket.isClosed, "Cannot `recv` on a closed socket")
   when defineSsl:
-    if socket.isSSL:
-      result = SSLRead(socket.sslHandle, addr(socket.buffer), int(socket.buffer.high))
-    else:
-      result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags)
-  else:
-    result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags)
+    if socket.isSsl:
+      return SSLRead(socket.sslHandle, buffer, size)
+
+  return recv(socket.fd, buffer, size, flags)
+
+proc readIntoBuf(socket: Socket, flags: int32): int =
+  result = 0
+  result = uniRecv(socket, addr(socket.buffer), socket.buffer.high, flags)
   if result < 0:
     # Save it in case it gets reset (the Nim codegen occasionally may call
     # Win API functions which reset it).
@@ -1059,16 +1070,16 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
   else:
     when defineSsl:
       if socket.isSSL:
-        if socket.sslHasPeekChar:
+        if socket.sslHasPeekChar: # TODO: Merge this peek char mess into uniRecv
           copyMem(data, addr(socket.sslPeekChar), 1)
           socket.sslHasPeekChar = false
           if size-1 > 0:
             var d = cast[cstring](data)
-            result = SSLRead(socket.sslHandle, addr(d[1]), size-1) + 1
+            result = uniRecv(socket, addr(d[1]), cint(size-1), 0'i32) + 1
           else:
             result = 1
         else:
-          result = SSLRead(socket.sslHandle, data, size)
+          result = uniRecv(socket, data, size.cint, 0'i32)
       else:
         result = recv(socket.fd, data, size.cint, 0'i32)
     else:
@@ -1145,7 +1156,11 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
   ##
   ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
   data.setLen(size)
-  result = recv(socket, cstring(data), size, timeout)
+  result =
+    if timeout == -1:
+      recv(socket, cstring(data), size)
+    else:
+      recv(socket, cstring(data), size, timeout)
   if result < 0:
     data.setLen(0)
     let lastError = getSocketError(socket)
@@ -1182,7 +1197,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
     when defineSsl:
       if socket.isSSL:
         if not socket.sslHasPeekChar:
-          result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
+          result = uniRecv(socket, addr(socket.sslPeekChar), 1, 0'i32)
           socket.sslHasPeekChar = true
 
         c = socket.sslPeekChar
@@ -1316,6 +1331,7 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
   ##
   ## **Note**: This is a low-level version of ``send``. You likely should use
   ## the version below.
+  assert(not socket.isClosed, "Cannot `send` on a closed socket")
   when defineSsl:
     if socket.isSSL:
       return SSLWrite(socket.sslHandle, cast[cstring](data), size)
@@ -1360,6 +1376,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
   ## which is defined below.
   ##
   ## **Note:** This proc is not available for SSL sockets.
+  assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
   var aiList = getAddrInfo(address, port, af)
 
   # try all possibilities:
diff --git a/tests/async/config.nims b/tests/async/config.nims
deleted file mode 100644
index 97c2e0aa4..000000000
--- a/tests/async/config.nims
+++ /dev/null
@@ -1,2 +0,0 @@
-when defined(upcoming):
-  patchFile("stdlib", "asyncdispatch", "$lib/upcoming/asyncdispatch")
diff --git a/tests/async/tjsandnativeasync.nim b/tests/async/tjsandnativeasync.nim
new file mode 100644
index 000000000..45839899f
--- /dev/null
+++ b/tests/async/tjsandnativeasync.nim
@@ -0,0 +1,30 @@
+discard """
+  output: '''hi
+bye'''
+"""
+
+import async, times
+when defined(js):
+    proc sleepAsync(t: int): Future[void] =
+        var promise = newPromise() do(resolve: proc()):
+            {.emit: """
+            setTimeout(function(){
+                `resolve`();
+            }, `t`);
+            """.}
+        result = promise
+else:
+    from asyncdispatch import sleepAsync, waitFor
+
+proc foo() {.async.} =
+    echo "hi"
+    var s = epochTime()
+    await sleepAsync(500)
+    var e = epochTime()
+    doAssert(e - s > 0.1)
+    echo "bye"
+
+when defined(js):
+    discard foo()
+else:
+    waitFor foo()
diff --git a/tests/gc/gcleak.nim b/tests/gc/gcleak.nim
index 4e47db609..8852a8d91 100644
--- a/tests/gc/gcleak.nim
+++ b/tests/gc/gcleak.nim
@@ -13,11 +13,10 @@ proc MakeObj(): TTestObj =
   result.x = "Hello"
 
 for i in 1 .. 1_000_000:
-  when defined(gcMarkAndSweep):
+  when defined(gcMarkAndSweep) or defined(boehmgc):
     GC_fullcollect()
   var obj = MakeObj()
   if getOccupiedMem() > 300_000: quit("still a leak!")
 #  echo GC_getstatistics()
 
 echo "no leak: ", getOccupiedMem()
-
diff --git a/tests/gc/gcleak2.nim b/tests/gc/gcleak2.nim
index 101421683..facb8a008 100644
--- a/tests/gc/gcleak2.nim
+++ b/tests/gc/gcleak2.nim
@@ -16,7 +16,7 @@ proc MakeObj(): TTestObj =
 
 proc inProc() =
   for i in 1 .. 1_000_000:
-    when defined(gcMarkAndSweep):
+    when defined(gcMarkAndSweep) or defined(boehmgc):
       GC_fullcollect()
     var obj: TTestObj
     obj = MakeObj()
@@ -24,5 +24,3 @@ proc inProc() =
 
 inProc()
 echo "no leak: ", getOccupiedMem()
-
-
diff --git a/tests/gc/gcleak4.nim b/tests/gc/gcleak4.nim
index d93a13854..e9b17e557 100644
--- a/tests/gc/gcleak4.nim
+++ b/tests/gc/gcleak4.nim
@@ -38,7 +38,7 @@ proc newPlus(a, b: ref TExpr): ref TPlusExpr =
   result.b = b
   result.op2 = $getOccupiedMem()
 
-const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 500_000
+const Limit = when compileOption("gc", "markAndSweep") or compileOption("gc", "boehm"): 5*1024*1024 else: 500_000
 
 for i in 0..100_000:
   var s: array[0..11, ref TExpr]
diff --git a/tests/js/tasync.nim b/tests/js/tasync.nim
index 34ef97b8b..76db38b27 100644
--- a/tests/js/tasync.nim
+++ b/tests/js/tasync.nim
@@ -1,5 +1,4 @@
 discard """
-  disabled: true
   output: '''
 x
 e
diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim
index a2380522f..415ae6a32 100644
--- a/tests/pragmas/tcustom_pragma.nim
+++ b/tests/pragmas/tcustom_pragma.nim
@@ -31,6 +31,11 @@ block: # A bit more advanced case
       d {.alternativeKey("df", 5).}: float
       e {.alternativeKey(V = 5).}: seq[bool] 
 
+
+  proc myproc(x: int, s: string) {.alternativeKey(V = 5), serializationKey"myprocSS".} = 
+    echo x, s
+
+
   var s: MySerializable
 
   const aDefVal = s.a.getCustomPragmaVal(defaultValue)
@@ -41,3 +46,8 @@ block: # A bit more advanced case
 
   const cSerKey = getCustomPragmaVal(s.field.c, serializationKey)
   static: assert(cSerKey == "cc")
+
+  const procSerKey = getCustomPragmaVal(myproc, serializationKey)
+  static: assert(procSerKey == "myprocSS")
+
+  static: assert(hasCustomPragma(myproc, alternativeKey))
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 5c845fc5c..90468e627 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -226,7 +226,8 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
                    "actiontable/tactiontable", "method/tmultim1",
                    "method/tmultim3", "method/tmultim4",
                    "varres/tvarres0", "varres/tvarres3", "varres/tvarres4",
-                   "varres/tvartup", "misc/tints", "misc/tunsignedinc"]:
+                   "varres/tvartup", "misc/tints", "misc/tunsignedinc",
+                   "async/tjsandnativeasync"]:
     test "tests/" & testfile & ".nim"
 
   for testfile in ["strutils", "json", "random", "times", "logging"]: