summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/asyncdispatch.nim369
-rw-r--r--lib/pure/asyncmacro.nim373
-rw-r--r--lib/pure/logging.nim244
-rw-r--r--lib/upcoming/asyncdispatch.nim368
4 files changed, 520 insertions, 834 deletions
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 79bc1b96d..6bea8e817 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -9,7 +9,7 @@
 
 include "system/inclrtl"
 
-import os, oids, tables, strutils, macros, times, heapqueue
+import os, oids, tables, strutils, times, heapqueue
 
 import nativesockets, net, queues
 
@@ -827,14 +827,17 @@ when defined(windows) or defined(nimdoc):
     var retFuture = newFuture[void]("send")
 
     var dataBuf: TWSABuf
-    dataBuf.buf = data # since this is not used in a callback, this is fine
+    dataBuf.buf = data
     dataBuf.len = data.len.ULONG
+    GC_ref(data) # we need to protect data until send operation is completed
+                 # or failed.
 
     var bytesReceived, lowFlags: Dword
     var ol = PCustomOverlapped()
     GC_ref(ol)
     ol.data = CompletionData(fd: socket, cb:
       proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+        GC_unref(data) # if operation completed `data` must be released.
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             retFuture.complete()
@@ -851,6 +854,8 @@ when defined(windows) or defined(nimdoc):
       let err = osLastError()
       if err.int32 != ERROR_IO_PENDING:
         GC_unref(ol)
+        GC_unref(data) # if operation failed `data` must be released, because
+                       # completion routine will not be called.
         if flags.isDisconnectionError(err):
           retFuture.complete()
         else:
@@ -1577,365 +1582,7 @@ proc accept*(socket: AsyncFD,
   return retFut
 
 # -- Await Macro
-
-proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
-  # Skips a nest of StmtList's.
-  result = node
-  if node[0].kind == nnkStmtList:
-    result = skipUntilStmtList(node[0])
-
-proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
-  result = node
-  if node[0].kind == nnkStmtList:
-    result = node[0]
-
-template createCb(retFutureSym, iteratorNameSym,
-                   name: untyped) =
-  var nameIterVar = iteratorNameSym
-  #{.push stackTrace: off.}
-  proc cb {.closure,gcsafe.} =
-    try:
-      if not nameIterVar.finished:
-        var next = nameIterVar()
-        if next == nil:
-          assert retFutureSym.finished, "Async procedure's (" &
-                 name & ") return Future was not finished."
-        else:
-          next.callback = cb
-    except:
-      if retFutureSym.finished:
-        # Take a look at tasyncexceptions for the bug which this fixes.
-        # That test explains it better than I can here.
-        raise
-      else:
-        retFutureSym.fail(getCurrentException())
-  cb()
-  #{.pop.}
-proc generateExceptionCheck(futSym,
-    tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
-  if tryStmt.kind == nnkNilLit:
-    result = rootReceiver
-  else:
-    var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
-    let errorNode = newDotExpr(futSym, newIdentNode("error"))
-    for i in 1 .. <tryStmt.len:
-      let exceptBranch = tryStmt[i]
-      if exceptBranch[0].kind == nnkStmtList:
-        exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
-      else:
-        var exceptIdentCount = 0
-        var ifCond: NimNode
-        for i in 0 .. <exceptBranch.len:
-          let child = exceptBranch[i]
-          if child.kind == nnkIdent:
-            let cond = infix(errorNode, "of", child)
-            if exceptIdentCount == 0:
-              ifCond = cond
-            else:
-              ifCond = infix(ifCond, "or", cond)
-          else:
-            break
-          exceptIdentCount.inc
-
-        expectKind(exceptBranch[exceptIdentCount], nnkStmtList)
-        exceptionChecks.add((ifCond, exceptBranch[exceptIdentCount]))
-    # -> -> else: raise futSym.error
-    exceptionChecks.add((newIdentNode("true"),
-        newNimNode(nnkRaiseStmt).add(errorNode)))
-    # Read the future if there is no error.
-    # -> else: futSym.read
-    let elseNode = newNimNode(nnkElse, fromNode)
-    elseNode.add newNimNode(nnkStmtList, fromNode)
-    elseNode[0].add rootReceiver
-
-    let ifBody = newStmtList()
-    ifBody.add newCall(newIdentNode("setCurrentException"), errorNode)
-    ifBody.add newIfStmt(exceptionChecks)
-    ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit())
-
-    result = newIfStmt(
-      (newDotExpr(futSym, newIdentNode("failed")), ifBody)
-    )
-    result.add elseNode
-
-template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
-                rootReceiver: expr, fromNode: NimNode) =
-  ## Params:
-  ##    futureVarNode: The NimNode which is a symbol identifying the Future[T]
-  ##                   variable to yield.
-  ##    fromNode: Used for better debug information (to give context).
-  ##    valueReceiver: The node which defines an expression that retrieves the
-  ##                   future's value.
-  ##
-  ##    rootReceiver: ??? TODO
-  # -> yield future<x>
-  result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
-  # -> future<x>.read
-  valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
-  result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver,
-      fromNode)
-
-template createVar(result: var NimNode, futSymName: string,
-                   asyncProc: NimNode,
-                   valueReceiver, rootReceiver: expr,
-                   fromNode: NimNode) =
-  result = newNimNode(nnkStmtList, fromNode)
-  var futSym = genSym(nskVar, "future")
-  result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
-  useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
-
-proc processBody(node, retFutureSym: NimNode,
-                 subTypeIsVoid: bool,
-                 tryStmt: NimNode): NimNode {.compileTime.} =
-  #echo(node.treeRepr)
-  result = node
-  case node.kind
-  of nnkReturnStmt:
-    result = newNimNode(nnkStmtList, node)
-    if node[0].kind == nnkEmpty:
-      if not subTypeIsVoid:
-        result.add newCall(newIdentNode("complete"), retFutureSym,
-            newIdentNode("result"))
-      else:
-        result.add newCall(newIdentNode("complete"), retFutureSym)
-    else:
-      let x = node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt)
-      if x.kind == nnkYieldStmt: result.add x
-      else:
-        result.add newCall(newIdentNode("complete"), retFutureSym, x)
-
-    result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
-    return # Don't process the children of this return stmt
-  of nnkCommand, nnkCall:
-    if node[0].kind == nnkIdent and node[0].ident == !"await":
-      case node[1].kind
-      of nnkIdent, nnkInfix, nnkDotExpr:
-        # await x
-        # await x or y
-        result = newNimNode(nnkYieldStmt, node).add(node[1]) # -> yield x
-      of nnkCall, nnkCommand:
-        # await foo(p, x)
-        # await foo p, x
-        var futureValue: NimNode
-        result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
-                  futureValue, node)
-      else:
-        error("Invalid node kind in 'await', got: " & $node[1].kind)
-    elif node.len > 1 and node[1].kind == nnkCommand and
-         node[1][0].kind == nnkIdent and node[1][0].ident == !"await":
-      # foo await x
-      var newCommand = node
-      result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
-                newCommand, node)
-
-  of nnkVarSection, nnkLetSection:
-    case node[0][2].kind
-    of nnkCommand:
-      if node[0][2][0].kind == nnkIdent and node[0][2][0].ident == !"await":
-        # var x = await y
-        var newVarSection = node # TODO: Should this use copyNimNode?
-        result.createVar("future" & $node[0][0].ident, node[0][2][1],
-          newVarSection[0][2], newVarSection, node)
-    else: discard
-  of nnkAsgn:
-    case node[1].kind
-    of nnkCommand:
-      if node[1][0].ident == !"await":
-        # x = await y
-        var newAsgn = node
-        result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn, node)
-    else: discard
-  of nnkDiscardStmt:
-    # discard await x
-    if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
-          node[0][0].ident == !"await":
-      var newDiscard = node
-      result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
-                newDiscard[0], newDiscard, node)
-  of nnkTryStmt:
-    # try: await x; except: ...
-    result = newNimNode(nnkStmtList, node)
-    template wrapInTry(n, tryBody: expr) =
-      var temp = n
-      n[0] = tryBody
-      tryBody = temp
-
-      # Transform ``except`` body.
-      # TODO: Could we perform some ``await`` transformation here to get it
-      # working in ``except``?
-      tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, nil)
-
-    proc processForTry(n: NimNode, i: var int,
-                       res: NimNode): bool {.compileTime.} =
-      ## Transforms the body of the tryStmt. Does not transform the
-      ## body in ``except``.
-      ## Returns true if the tryStmt node was transformed into an ifStmt.
-      result = false
-      var skipped = n.skipStmtList()
-      while i < skipped.len:
-        var processed = processBody(skipped[i], retFutureSym,
-                                    subTypeIsVoid, n)
-
-        # Check if we transformed the node into an exception check.
-        # This suggests skipped[i] contains ``await``.
-        if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
-          processed = processed.skipUntilStmtList()
-          expectKind(processed, nnkStmtList)
-          expectKind(processed[2][1], nnkElse)
-          i.inc
-
-          if not processForTry(n, i, processed[2][1][0]):
-            # We need to wrap the nnkElse nodes back into a tryStmt.
-            # As they are executed if an exception does not happen
-            # inside the awaited future.
-            # The following code will wrap the nodes inside the
-            # original tryStmt.
-            wrapInTry(n, processed[2][1][0])
-
-          res.add processed
-          result = true
-        else:
-          res.add skipped[i]
-          i.inc
-    var i = 0
-    if not processForTry(node, i, result):
-      # If the tryStmt hasn't been transformed we can just put the body
-      # back into it.
-      wrapInTry(node, result)
-    return
-  else: discard
-
-  for i in 0 .. <result.len:
-    result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, nil)
-
-proc getName(node: NimNode): string {.compileTime.} =
-  case node.kind
-  of nnkPostfix:
-    return $node[1].ident
-  of nnkIdent:
-    return $node.ident
-  of nnkEmpty:
-    return "anonymous"
-  else:
-    error("Unknown name.")
-
-proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
-  ## This macro transforms a single procedure into a closure iterator.
-  ## The ``async`` macro supports a stmtList holding multiple async procedures.
-  if prc.kind notin {nnkProcDef, nnkLambda}:
-      error("Cannot transform this node kind into an async proc." &
-            " Proc definition or lambda node expected.")
-
-  hint("Processing " & prc[0].getName & " as an async proc.")
-
-  let returnType = prc[3][0]
-  var baseType: NimNode
-  # Verify that the return type is a Future[T]
-  if returnType.kind == nnkBracketExpr:
-    let fut = repr(returnType[0])
-    if fut != "Future":
-      error("Expected return type of 'Future' got '" & fut & "'")
-    baseType = returnType[1]
-  elif returnType.kind in nnkCallKinds and $returnType[0] == "[]":
-    let fut = repr(returnType[1])
-    if fut != "Future":
-      error("Expected return type of 'Future' got '" & fut & "'")
-    baseType = returnType[2]
-  elif returnType.kind == nnkEmpty:
-    baseType = returnType
-  else:
-    error("Expected return type of 'Future' got '" & repr(returnType) & "'")
-
-  let subtypeIsVoid = returnType.kind == nnkEmpty or
-        (baseType.kind == nnkIdent and returnType[1].ident == !"void")
-
-  var outerProcBody = newNimNode(nnkStmtList, prc[6])
-
-  # -> var retFuture = newFuture[T]()
-  var retFutureSym = genSym(nskVar, "retFuture")
-  var subRetType =
-    if returnType.kind == nnkEmpty: newIdentNode("void")
-    else: baseType
-  outerProcBody.add(
-    newVarStmt(retFutureSym,
-      newCall(
-        newNimNode(nnkBracketExpr, prc[6]).add(
-          newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
-          subRetType),
-      newLit(prc[0].getName)))) # Get type from return type of this proc
-
-  # -> iterator nameIter(): FutureBase {.closure.} =
-  # ->   {.push warning[resultshadowed]: off.}
-  # ->   var result: T
-  # ->   {.pop.}
-  # ->   <proc_body>
-  # ->   complete(retFuture, result)
-  var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
-  var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil)
-  # don't do anything with forward bodies (empty)
-  if procBody.kind != nnkEmpty:
-    if not subtypeIsVoid:
-      procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
-        newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
-          newIdentNode("warning"), newIdentNode("resultshadowed")),
-        newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
-
-      procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add(
-        newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
-
-      procBody.insert(2, newNimNode(nnkPragma).add(
-        newIdentNode("pop"))) # -> {.pop.})
-
-      procBody.add(
-        newCall(newIdentNode("complete"),
-          retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
-    else:
-      # -> complete(retFuture)
-      procBody.add(newCall(newIdentNode("complete"), retFutureSym))
-
-    var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
-                                  procBody, nnkIteratorDef)
-    closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
-    outerProcBody.add(closureIterator)
-
-    # -> createCb(retFuture)
-    #var cbName = newIdentNode("cb")
-    var procCb = getAst createCb(retFutureSym, iteratorNameSym,
-                         newStrLitNode(prc[0].getName))
-    outerProcBody.add procCb
-
-    # -> return retFuture
-    outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym)
-
-  result = prc
-
-  # Remove the 'async' pragma.
-  for i in 0 .. <result[4].len:
-    if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
-      result[4].del(i)
-  result[4] = newEmptyNode()
-  if subtypeIsVoid:
-    # Add discardable pragma.
-    if returnType.kind == nnkEmpty:
-      # Add Future[void]
-      result[3][0] = parseExpr("Future[void]")
-  if procBody.kind != nnkEmpty:
-    result[6] = outerProcBody
-  #echo(treeRepr(result))
-  #if prc[0].getName == "testInfix":
-  #  echo(toStrLit(result))
-
-macro async*(prc: untyped): untyped =
-  ## Macro which processes async procedures into the appropriate
-  ## iterators and yield statements.
-  if prc.kind == nnkStmtList:
-    for oneProc in prc:
-      result = newStmtList()
-      result.add asyncSingleProc(oneProc)
-  else:
-    result = asyncSingleProc(prc)
-  when defined(nimDumpAsync):
-    echo repr result
+include asyncmacro
 
 proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
   ## Reads a line of data from ``socket``. Returned future will complete once
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
new file mode 100644
index 000000000..28e3e2a16
--- /dev/null
+++ b/lib/pure/asyncmacro.nim
@@ -0,0 +1,373 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## AsyncMacro
+## *************
+## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
+
+import macros
+
+proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
+  # Skips a nest of StmtList's.
+  result = node
+  if node[0].kind == nnkStmtList:
+    result = skipUntilStmtList(node[0])
+
+proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
+  result = node
+  if node[0].kind == nnkStmtList:
+    result = node[0]
+
+template createCb(retFutureSym, iteratorNameSym,
+                   name: untyped) =
+  var nameIterVar = iteratorNameSym
+  #{.push stackTrace: off.}
+  proc cb {.closure,gcsafe.} =
+    try:
+      if not nameIterVar.finished:
+        var next = nameIterVar()
+        if next == nil:
+          assert retFutureSym.finished, "Async procedure's (" &
+                 name & ") return Future was not finished."
+        else:
+          next.callback = cb
+    except:
+      if retFutureSym.finished:
+        # Take a look at tasyncexceptions for the bug which this fixes.
+        # That test explains it better than I can here.
+        raise
+      else:
+        retFutureSym.fail(getCurrentException())
+  cb()
+  #{.pop.}
+proc generateExceptionCheck(futSym,
+    tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
+  if tryStmt.kind == nnkNilLit:
+    result = rootReceiver
+  else:
+    var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
+    let errorNode = newDotExpr(futSym, newIdentNode("error"))
+    for i in 1 .. <tryStmt.len:
+      let exceptBranch = tryStmt[i]
+      if exceptBranch[0].kind == nnkStmtList:
+        exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
+      else:
+        var exceptIdentCount = 0
+        var ifCond: NimNode
+        for i in 0 .. <exceptBranch.len:
+          let child = exceptBranch[i]
+          if child.kind == nnkIdent:
+            let cond = infix(errorNode, "of", child)
+            if exceptIdentCount == 0:
+              ifCond = cond
+            else:
+              ifCond = infix(ifCond, "or", cond)
+          else:
+            break
+          exceptIdentCount.inc
+
+        expectKind(exceptBranch[exceptIdentCount], nnkStmtList)
+        exceptionChecks.add((ifCond, exceptBranch[exceptIdentCount]))
+    # -> -> else: raise futSym.error
+    exceptionChecks.add((newIdentNode("true"),
+        newNimNode(nnkRaiseStmt).add(errorNode)))
+    # Read the future if there is no error.
+    # -> else: futSym.read
+    let elseNode = newNimNode(nnkElse, fromNode)
+    elseNode.add newNimNode(nnkStmtList, fromNode)
+    elseNode[0].add rootReceiver
+
+    let ifBody = newStmtList()
+    ifBody.add newCall(newIdentNode("setCurrentException"), errorNode)
+    ifBody.add newIfStmt(exceptionChecks)
+    ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit())
+
+    result = newIfStmt(
+      (newDotExpr(futSym, newIdentNode("failed")), ifBody)
+    )
+    result.add elseNode
+
+template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
+                rootReceiver: expr, fromNode: NimNode) =
+  ## Params:
+  ##    futureVarNode: The NimNode which is a symbol identifying the Future[T]
+  ##                   variable to yield.
+  ##    fromNode: Used for better debug information (to give context).
+  ##    valueReceiver: The node which defines an expression that retrieves the
+  ##                   future's value.
+  ##
+  ##    rootReceiver: ??? TODO
+  # -> yield future<x>
+  result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
+  # -> future<x>.read
+  valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
+  result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver,
+      fromNode)
+
+template createVar(result: var NimNode, futSymName: string,
+                   asyncProc: NimNode,
+                   valueReceiver, rootReceiver: expr,
+                   fromNode: NimNode) =
+  result = newNimNode(nnkStmtList, fromNode)
+  var futSym = genSym(nskVar, "future")
+  result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
+  useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
+
+proc processBody(node, retFutureSym: NimNode,
+                 subTypeIsVoid: bool,
+                 tryStmt: NimNode): NimNode {.compileTime.} =
+  #echo(node.treeRepr)
+  result = node
+  case node.kind
+  of nnkReturnStmt:
+    result = newNimNode(nnkStmtList, node)
+    if node[0].kind == nnkEmpty:
+      if not subTypeIsVoid:
+        result.add newCall(newIdentNode("complete"), retFutureSym,
+            newIdentNode("result"))
+      else:
+        result.add newCall(newIdentNode("complete"), retFutureSym)
+    else:
+      let x = node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt)
+      if x.kind == nnkYieldStmt: result.add x
+      else:
+        result.add newCall(newIdentNode("complete"), retFutureSym, x)
+
+    result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
+    return # Don't process the children of this return stmt
+  of nnkCommand, nnkCall:
+    if node[0].kind == nnkIdent and node[0].ident == !"await":
+      case node[1].kind
+      of nnkIdent, nnkInfix, nnkDotExpr:
+        # await x
+        # await x or y
+        result = newNimNode(nnkYieldStmt, node).add(node[1]) # -> yield x
+      of nnkCall, nnkCommand:
+        # await foo(p, x)
+        # await foo p, x
+        var futureValue: NimNode
+        result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
+                  futureValue, node)
+      else:
+        error("Invalid node kind in 'await', got: " & $node[1].kind)
+    elif node.len > 1 and node[1].kind == nnkCommand and
+         node[1][0].kind == nnkIdent and node[1][0].ident == !"await":
+      # foo await x
+      var newCommand = node
+      result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
+                newCommand, node)
+
+  of nnkVarSection, nnkLetSection:
+    case node[0][2].kind
+    of nnkCommand:
+      if node[0][2][0].kind == nnkIdent and node[0][2][0].ident == !"await":
+        # var x = await y
+        var newVarSection = node # TODO: Should this use copyNimNode?
+        result.createVar("future" & $node[0][0].ident, node[0][2][1],
+          newVarSection[0][2], newVarSection, node)
+    else: discard
+  of nnkAsgn:
+    case node[1].kind
+    of nnkCommand:
+      if node[1][0].ident == !"await":
+        # x = await y
+        var newAsgn = node
+        result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn, node)
+    else: discard
+  of nnkDiscardStmt:
+    # discard await x
+    if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
+          node[0][0].ident == !"await":
+      var newDiscard = node
+      result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
+                newDiscard[0], newDiscard, node)
+  of nnkTryStmt:
+    # try: await x; except: ...
+    result = newNimNode(nnkStmtList, node)
+    template wrapInTry(n, tryBody: expr) =
+      var temp = n
+      n[0] = tryBody
+      tryBody = temp
+
+      # Transform ``except`` body.
+      # TODO: Could we perform some ``await`` transformation here to get it
+      # working in ``except``?
+      tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, nil)
+
+    proc processForTry(n: NimNode, i: var int,
+                       res: NimNode): bool {.compileTime.} =
+      ## Transforms the body of the tryStmt. Does not transform the
+      ## body in ``except``.
+      ## Returns true if the tryStmt node was transformed into an ifStmt.
+      result = false
+      var skipped = n.skipStmtList()
+      while i < skipped.len:
+        var processed = processBody(skipped[i], retFutureSym,
+                                    subTypeIsVoid, n)
+
+        # Check if we transformed the node into an exception check.
+        # This suggests skipped[i] contains ``await``.
+        if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
+          processed = processed.skipUntilStmtList()
+          expectKind(processed, nnkStmtList)
+          expectKind(processed[2][1], nnkElse)
+          i.inc
+
+          if not processForTry(n, i, processed[2][1][0]):
+            # We need to wrap the nnkElse nodes back into a tryStmt.
+            # As they are executed if an exception does not happen
+            # inside the awaited future.
+            # The following code will wrap the nodes inside the
+            # original tryStmt.
+            wrapInTry(n, processed[2][1][0])
+
+          res.add processed
+          result = true
+        else:
+          res.add skipped[i]
+          i.inc
+    var i = 0
+    if not processForTry(node, i, result):
+      # If the tryStmt hasn't been transformed we can just put the body
+      # back into it.
+      wrapInTry(node, result)
+    return
+  else: discard
+
+  for i in 0 .. <result.len:
+    result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, nil)
+
+proc getName(node: NimNode): string {.compileTime.} =
+  case node.kind
+  of nnkPostfix:
+    return $node[1].ident
+  of nnkIdent:
+    return $node.ident
+  of nnkEmpty:
+    return "anonymous"
+  else:
+    error("Unknown name.")
+
+proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
+  ## This macro transforms a single procedure into a closure iterator.
+  ## The ``async`` macro supports a stmtList holding multiple async procedures.
+  if prc.kind notin {nnkProcDef, nnkLambda}:
+      error("Cannot transform this node kind into an async proc." &
+            " Proc definition or lambda node expected.")
+
+  hint("Processing " & prc[0].getName & " as an async proc.")
+
+  let returnType = prc[3][0]
+  var baseType: NimNode
+  # Verify that the return type is a Future[T]
+  if returnType.kind == nnkBracketExpr:
+    let fut = repr(returnType[0])
+    if fut != "Future":
+      error("Expected return type of 'Future' got '" & fut & "'")
+    baseType = returnType[1]
+  elif returnType.kind in nnkCallKinds and $returnType[0] == "[]":
+    let fut = repr(returnType[1])
+    if fut != "Future":
+      error("Expected return type of 'Future' got '" & fut & "'")
+    baseType = returnType[2]
+  elif returnType.kind == nnkEmpty:
+    baseType = returnType
+  else:
+    error("Expected return type of 'Future' got '" & repr(returnType) & "'")
+
+  let subtypeIsVoid = returnType.kind == nnkEmpty or
+        (baseType.kind == nnkIdent and returnType[1].ident == !"void")
+
+  var outerProcBody = newNimNode(nnkStmtList, prc[6])
+
+  # -> var retFuture = newFuture[T]()
+  var retFutureSym = genSym(nskVar, "retFuture")
+  var subRetType =
+    if returnType.kind == nnkEmpty: newIdentNode("void")
+    else: baseType
+  outerProcBody.add(
+    newVarStmt(retFutureSym,
+      newCall(
+        newNimNode(nnkBracketExpr, prc[6]).add(
+          newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
+          subRetType),
+      newLit(prc[0].getName)))) # Get type from return type of this proc
+
+  # -> iterator nameIter(): FutureBase {.closure.} =
+  # ->   {.push warning[resultshadowed]: off.}
+  # ->   var result: T
+  # ->   {.pop.}
+  # ->   <proc_body>
+  # ->   complete(retFuture, result)
+  var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
+  var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil)
+  # don't do anything with forward bodies (empty)
+  if procBody.kind != nnkEmpty:
+    if not subtypeIsVoid:
+      procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
+        newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
+          newIdentNode("warning"), newIdentNode("resultshadowed")),
+        newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
+
+      procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add(
+        newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
+
+      procBody.insert(2, newNimNode(nnkPragma).add(
+        newIdentNode("pop"))) # -> {.pop.})
+
+      procBody.add(
+        newCall(newIdentNode("complete"),
+          retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
+    else:
+      # -> complete(retFuture)
+      procBody.add(newCall(newIdentNode("complete"), retFutureSym))
+
+    var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
+                                  procBody, nnkIteratorDef)
+    closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
+    outerProcBody.add(closureIterator)
+
+    # -> createCb(retFuture)
+    #var cbName = newIdentNode("cb")
+    var procCb = getAst createCb(retFutureSym, iteratorNameSym,
+                         newStrLitNode(prc[0].getName))
+    outerProcBody.add procCb
+
+    # -> return retFuture
+    outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym)
+
+  result = prc
+
+  # Remove the 'async' pragma.
+  for i in 0 .. <result[4].len:
+    if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
+      result[4].del(i)
+  result[4] = newEmptyNode()
+  if subtypeIsVoid:
+    # Add discardable pragma.
+    if returnType.kind == nnkEmpty:
+      # Add Future[void]
+      result[3][0] = parseExpr("Future[void]")
+  if procBody.kind != nnkEmpty:
+    result[6] = outerProcBody
+  #echo(treeRepr(result))
+  #if prc[0].getName == "testInfix":
+  #  echo(toStrLit(result))
+
+macro async*(prc: untyped): untyped =
+  ## Macro which processes async procedures into the appropriate
+  ## iterators and yield statements.
+  if prc.kind == nnkStmtList:
+    for oneProc in prc:
+      result = newStmtList()
+      result.add asyncSingleProc(oneProc)
+  else:
+    result = asyncSingleProc(prc)
+  when defined(nimDumpAsync):
+    echo repr result
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index 6a27e6af8..b23b1e5bb 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -47,7 +47,9 @@
 ## **Warning:** The global list of handlers is a thread var, this means that
 ## the handlers must be re-added in each thread.
 
-import strutils, os, times
+import strutils, times
+when not defined(js):
+  import os
 
 type
   Level* = enum  ## logging level
@@ -77,21 +79,24 @@ type
   ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
                                         ## console
 
-  FileLogger* = ref object of Logger ## logger that writes the messages to a file
-    file*: File  ## the wrapped file.
+when not defined(js):
+  type
+    FileLogger* = ref object of Logger ## logger that writes the messages to a file
+      file*: File  ## the wrapped file.
 
-  RollingFileLogger* = ref object of FileLogger ## logger that writes the
-                                                ## messages to a file and
-                                                ## performs log rotation
-    maxLines: int # maximum number of lines
-    curLine : int
-    baseName: string # initial filename
-    baseMode: FileMode # initial file mode
-    logFiles: int # how many log files already created, e.g. basename.1, basename.2...
-    bufSize: int # size of output buffer (-1: use system defaults, 0: unbuffered, >0: fixed buffer size)
+    RollingFileLogger* = ref object of FileLogger ## logger that writes the
+                                                  ## messages to a file and
+                                                  ## performs log rotation
+      maxLines: int # maximum number of lines
+      curLine : int
+      baseName: string # initial filename
+      baseMode: FileMode # initial file mode
+      logFiles: int # how many log files already created, e.g. basename.1, basename.2...
+      bufSize: int # size of output buffer (-1: use system defaults, 0: unbuffered, >0: fixed buffer size)
 
-{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
-    PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
+  {.deprecated: [PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
+
+{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger].}
 
 var
   level {.threadvar.}: Level   ## global log filter
@@ -112,7 +117,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
     else:
       inc(i)
       var v = ""
-      var app = getAppFilename()
+      let app = when defined(js): "" else: getAppFilename()
       while frmt[i] in IdentChars:
         v.add(toLower(frmt[i]))
         inc(i)
@@ -121,8 +126,10 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
       of "time": result.add(getClockStr())
       of "datetime": result.add(getDateStr() & "T" & getClockStr())
       of "app":  result.add(app)
-      of "appdir": result.add(app.splitFile.dir)
-      of "appname": result.add(app.splitFile.name)
+      of "appdir":
+        when not defined(js): result.add(app.splitFile.dir)
+      of "appname":
+        when not defined(js): result.add(app.splitFile.name)
       of "levelid": result.add(LevelNames[level][0])
       of "levelname": result.add(LevelNames[level])
       else: discard
@@ -139,19 +146,13 @@ method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
 method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
   ## Logs to the console using ``logger`` only.
   if level >= logging.level and level >= logger.levelThreshold:
-    writeLine(stdout, substituteLog(logger.fmtStr, level, args))
-    if level in {lvlError, lvlFatal}: flushFile(stdout)
-
-method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) =
-  ## Logs to a file using ``logger`` only.
-  if level >= logging.level and level >= logger.levelThreshold:
-    writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
-    if level in {lvlError, lvlFatal}: flushFile(logger.file)
-
-proc defaultFilename*(): string =
-  ## Returns the default filename for a logger.
-  var (path, name, _) = splitFile(getAppFilename())
-  result = changeFileExt(path / name, "log")
+    let ln = substituteLog(logger.fmtStr, level, args)
+    when defined(js):
+      let cln: cstring = ln
+      {.emit: "console.log(`cln`);".}
+    else:
+      writeLine(stdout, ln)
+      if level in {lvlError, lvlFatal}: flushFile(stdout)
 
 proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): ConsoleLogger =
   ## Creates a new console logger. This logger logs to the console.
@@ -159,87 +160,99 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): Console
   result.fmtStr = fmtStr
   result.levelThreshold = levelThreshold
 
-proc newFileLogger*(filename = defaultFilename(),
-                    mode: FileMode = fmAppend,
-                    levelThreshold = lvlAll,
-                    fmtStr = defaultFmtStr,
-                    bufSize: int = -1): FileLogger =
-  ## Creates a new file logger. This logger logs to a file.
-  ## Use ``bufSize`` as size of the output buffer when writing the file
-  ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
-  new(result)
-  result.levelThreshold = levelThreshold
-  result.file = open(filename, mode, bufSize = bufSize)
-  result.fmtStr = fmtStr
-
-# ------
-
-proc countLogLines(logger: RollingFileLogger): int =
-  result = 0
-  for line in logger.file.lines():
-    result.inc()
-
-proc countFiles(filename: string): int =
-  # Example: file.log.1
-  result = 0
-  let (dir, name, ext) = splitFile(filename)
-  for kind, path in walkDir(dir):
-    if kind == pcFile:
-      let llfn = name & ext & ExtSep
-      if path.extractFilename.startsWith(llfn):
-        let numS = path.extractFilename[llfn.len .. ^1]
-        try:
-          let num = parseInt(numS)
-          if num > result:
-            result = num
-        except ValueError: discard
-
-proc newRollingFileLogger*(filename = defaultFilename(),
-                           mode: FileMode = fmReadWrite,
-                           levelThreshold = lvlAll,
-                           fmtStr = defaultFmtStr,
-                           maxLines = 1000,
-                           bufSize: int = -1): RollingFileLogger =
-  ## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
-  ## a new log file will be started and the old will be renamed.
-  ## Use ``bufSize`` as size of the output buffer when writing the file
-  ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
-  new(result)
-  result.levelThreshold = levelThreshold
-  result.fmtStr = fmtStr
-  result.maxLines = maxLines
-  result.bufSize = bufSize
-  result.file = open(filename, mode, bufSize=result.bufSize)
-  result.curLine = 0
-  result.baseName = filename
-  result.baseMode = mode
-
-  result.logFiles = countFiles(filename)
-
-  if mode == fmAppend:
-    # We need to get a line count because we will be appending to the file.
-    result.curLine = countLogLines(result)
-
-proc rotate(logger: RollingFileLogger) =
-  let (dir, name, ext) = splitFile(logger.baseName)
-  for i in countdown(logger.logFiles, 0):
-    let srcSuff = if i != 0: ExtSep & $i else: ""
-    moveFile(dir / (name & ext & srcSuff),
-             dir / (name & ext & ExtSep & $(i+1)))
-
-method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) =
-  ## Logs to a file using rolling ``logger`` only.
-  if level >= logging.level and level >= logger.levelThreshold:
-    if logger.curLine >= logger.maxLines:
-      logger.file.close()
-      rotate(logger)
-      logger.logFiles.inc
-      logger.curLine = 0
-      logger.file = open(logger.baseName, logger.baseMode, bufSize = logger.bufSize)
-
-    writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
-    if level in {lvlError, lvlFatal}: flushFile(logger.file)
-    logger.curLine.inc
+when not defined(js):
+  method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) =
+    ## Logs to a file using ``logger`` only.
+    if level >= logging.level and level >= logger.levelThreshold:
+      writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
+      if level in {lvlError, lvlFatal}: flushFile(logger.file)
+
+  proc defaultFilename*(): string =
+    ## Returns the default filename for a logger.
+    var (path, name, _) = splitFile(getAppFilename())
+    result = changeFileExt(path / name, "log")
+
+  proc newFileLogger*(filename = defaultFilename(),
+                      mode: FileMode = fmAppend,
+                      levelThreshold = lvlAll,
+                      fmtStr = defaultFmtStr,
+                      bufSize: int = -1): FileLogger =
+    ## Creates a new file logger. This logger logs to a file.
+    ## Use ``bufSize`` as size of the output buffer when writing the file
+    ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
+    new(result)
+    result.levelThreshold = levelThreshold
+    result.file = open(filename, mode, bufSize = bufSize)
+    result.fmtStr = fmtStr
+
+  # ------
+
+  proc countLogLines(logger: RollingFileLogger): int =
+    result = 0
+    for line in logger.file.lines():
+      result.inc()
+
+  proc countFiles(filename: string): int =
+    # Example: file.log.1
+    result = 0
+    let (dir, name, ext) = splitFile(filename)
+    for kind, path in walkDir(dir):
+      if kind == pcFile:
+        let llfn = name & ext & ExtSep
+        if path.extractFilename.startsWith(llfn):
+          let numS = path.extractFilename[llfn.len .. ^1]
+          try:
+            let num = parseInt(numS)
+            if num > result:
+              result = num
+          except ValueError: discard
+
+  proc newRollingFileLogger*(filename = defaultFilename(),
+                            mode: FileMode = fmReadWrite,
+                            levelThreshold = lvlAll,
+                            fmtStr = defaultFmtStr,
+                            maxLines = 1000,
+                            bufSize: int = -1): RollingFileLogger =
+    ## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
+    ## a new log file will be started and the old will be renamed.
+    ## Use ``bufSize`` as size of the output buffer when writing the file
+    ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
+    new(result)
+    result.levelThreshold = levelThreshold
+    result.fmtStr = fmtStr
+    result.maxLines = maxLines
+    result.bufSize = bufSize
+    result.file = open(filename, mode, bufSize=result.bufSize)
+    result.curLine = 0
+    result.baseName = filename
+    result.baseMode = mode
+
+    result.logFiles = countFiles(filename)
+
+    if mode == fmAppend:
+      # We need to get a line count because we will be appending to the file.
+      result.curLine = countLogLines(result)
+
+  proc rotate(logger: RollingFileLogger) =
+    let (dir, name, ext) = splitFile(logger.baseName)
+    for i in countdown(logger.logFiles, 0):
+      let srcSuff = if i != 0: ExtSep & $i else: ""
+      moveFile(dir / (name & ext & srcSuff),
+              dir / (name & ext & ExtSep & $(i+1)))
+
+  method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) =
+    ## Logs to a file using rolling ``logger`` only.
+    if level >= logging.level and level >= logger.levelThreshold:
+      if logger.curLine >= logger.maxLines:
+        logger.file.close()
+        rotate(logger)
+        logger.logFiles.inc
+        logger.curLine = 0
+        logger.file = open(logger.baseName, logger.baseMode, bufSize = logger.bufSize)
+
+      writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
+      if level in {lvlError, lvlFatal}: flushFile(logger.file)
+      logger.curLine.inc
 
 # --------
 
@@ -323,10 +336,11 @@ proc getLogFilter*(): Level =
 
 when not defined(testing) and isMainModule:
   var L = newConsoleLogger()
-  var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
-  var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
+  when not defined(js):
+    var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
+    var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
+    addHandler(fL)
+    addHandler(rL)
   addHandler(L)
-  addHandler(fL)
-  addHandler(rL)
   for i in 0 .. 25:
     info("hello", i)
diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim
index 3c11e082f..ce44e8a6a 100644
--- a/lib/upcoming/asyncdispatch.nim
+++ b/lib/upcoming/asyncdispatch.nim
@@ -9,7 +9,7 @@
 
 include "system/inclrtl"
 
-import os, oids, tables, strutils, macros, times, heapqueue
+import os, oids, tables, strutils, times, heapqueue
 
 import nativesockets, net, queues
 
@@ -796,14 +796,17 @@ when defined(windows) or defined(nimdoc):
     var retFuture = newFuture[void]("send")
 
     var dataBuf: TWSABuf
-    dataBuf.buf = data # since this is not used in a callback, this is fine
+    dataBuf.buf = data
     dataBuf.len = data.len.ULONG
+    GC_ref(data) # we need to protect data until send operation is completed
+                 # or failed.
 
     var bytesReceived, lowFlags: Dword
     var ol = PCustomOverlapped()
     GC_ref(ol)
     ol.data = CompletionData(fd: socket, cb:
       proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+        GC_unref(data) # if operation completed `data` must be released.
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             retFuture.complete()
@@ -820,6 +823,8 @@ when defined(windows) or defined(nimdoc):
       let err = osLastError()
       if err.int32 != ERROR_IO_PENDING:
         GC_unref(ol)
+        GC_unref(data) # if operation failed `data` must be released, because
+                       # completion routine will not be called.
         if flags.isDisconnectionError(err):
           retFuture.complete()
         else:
@@ -952,7 +957,7 @@ when defined(windows) or defined(nimdoc):
     let dwLocalAddressLength = Dword(sizeof (Sockaddr_in) + 16)
     let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in) + 16)
 
-    template completeAccept(): stmt {.immediate, dirty.} =
+    template completeAccept() {.dirty.} =
       var listenSock = socket
       let setoptRet = setsockopt(clientSock, SOL_SOCKET,
           SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
@@ -972,7 +977,7 @@ when defined(windows) or defined(nimdoc):
          client: clientSock.AsyncFD)
       )
 
-    template failAccept(errcode): stmt =
+    template failAccept(errcode) =
       if flags.isDisconnectionError(errcode):
         var newAcceptFut = acceptAddr(socket, flags)
         newAcceptFut.callback =
@@ -1748,360 +1753,7 @@ proc accept*(socket: AsyncFD,
   return retFut
 
 # -- Await Macro
-
-proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
-  # Skips a nest of StmtList's.
-  result = node
-  if node[0].kind == nnkStmtList:
-    result = skipUntilStmtList(node[0])
-
-proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
-  result = node
-  if node[0].kind == nnkStmtList:
-    result = node[0]
-
-template createCb(retFutureSym, iteratorNameSym,
-                   name: expr): stmt {.immediate.} =
-  var nameIterVar = iteratorNameSym
-  #{.push stackTrace: off.}
-  proc cb {.closure,gcsafe.} =
-    try:
-      if not nameIterVar.finished:
-        var next = nameIterVar()
-        if next == nil:
-          assert retFutureSym.finished, "Async procedure's (" &
-                 name & ") return Future was not finished."
-        else:
-          next.callback = cb
-    except:
-      if retFutureSym.finished:
-        # Take a look at tasyncexceptions for the bug which this fixes.
-        # That test explains it better than I can here.
-        raise
-      else:
-        retFutureSym.fail(getCurrentException())
-  cb()
-  #{.pop.}
-proc generateExceptionCheck(futSym,
-    tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
-  if tryStmt.kind == nnkNilLit:
-    result = rootReceiver
-  else:
-    var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
-    let errorNode = newDotExpr(futSym, newIdentNode("error"))
-    for i in 1 .. <tryStmt.len:
-      let exceptBranch = tryStmt[i]
-      if exceptBranch[0].kind == nnkStmtList:
-        exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
-      else:
-        var exceptIdentCount = 0
-        var ifCond: NimNode
-        for i in 0 .. <exceptBranch.len:
-          let child = exceptBranch[i]
-          if child.kind == nnkIdent:
-            let cond = infix(errorNode, "of", child)
-            if exceptIdentCount == 0:
-              ifCond = cond
-            else:
-              ifCond = infix(ifCond, "or", cond)
-          else:
-            break
-          exceptIdentCount.inc
-
-        expectKind(exceptBranch[exceptIdentCount], nnkStmtList)
-        exceptionChecks.add((ifCond, exceptBranch[exceptIdentCount]))
-    # -> -> else: raise futSym.error
-    exceptionChecks.add((newIdentNode("true"),
-        newNimNode(nnkRaiseStmt).add(errorNode)))
-    # Read the future if there is no error.
-    # -> else: futSym.read
-    let elseNode = newNimNode(nnkElse, fromNode)
-    elseNode.add newNimNode(nnkStmtList, fromNode)
-    elseNode[0].add rootReceiver
-
-    let ifBody = newStmtList()
-    ifBody.add newCall(newIdentNode("setCurrentException"), errorNode)
-    ifBody.add newIfStmt(exceptionChecks)
-    ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit())
-
-    result = newIfStmt(
-      (newDotExpr(futSym, newIdentNode("failed")), ifBody)
-    )
-    result.add elseNode
-
-template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
-                rootReceiver: expr, fromNode: NimNode) =
-  ## Params:
-  ##    futureVarNode: The NimNode which is a symbol identifying the Future[T]
-  ##                   variable to yield.
-  ##    fromNode: Used for better debug information (to give context).
-  ##    valueReceiver: The node which defines an expression that retrieves the
-  ##                   future's value.
-  ##
-  ##    rootReceiver: ??? TODO
-  # -> yield future<x>
-  result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
-  # -> future<x>.read
-  valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
-  result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver,
-      fromNode)
-
-template createVar(result: var NimNode, futSymName: string,
-                   asyncProc: NimNode,
-                   valueReceiver, rootReceiver: expr,
-                   fromNode: NimNode) =
-  result = newNimNode(nnkStmtList, fromNode)
-  var futSym = genSym(nskVar, "future")
-  result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
-  useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
-
-proc processBody(node, retFutureSym: NimNode,
-                 subTypeIsVoid: bool,
-                 tryStmt: NimNode): NimNode {.compileTime.} =
-  #echo(node.treeRepr)
-  result = node
-  case node.kind
-  of nnkReturnStmt:
-    result = newNimNode(nnkStmtList, node)
-    if node[0].kind == nnkEmpty:
-      if not subTypeIsVoid:
-        result.add newCall(newIdentNode("complete"), retFutureSym,
-            newIdentNode("result"))
-      else:
-        result.add newCall(newIdentNode("complete"), retFutureSym)
-    else:
-      result.add newCall(newIdentNode("complete"), retFutureSym,
-        node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt))
-
-    result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
-    return # Don't process the children of this return stmt
-  of nnkCommand, nnkCall:
-    if node[0].kind == nnkIdent and node[0].ident == !"await":
-      case node[1].kind
-      of nnkIdent, nnkInfix, nnkDotExpr:
-        # await x
-        # await x or y
-        result = newNimNode(nnkYieldStmt, node).add(node[1]) # -> yield x
-      of nnkCall, nnkCommand:
-        # await foo(p, x)
-        # await foo p, x
-        var futureValue: NimNode
-        result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
-                  futureValue, node)
-      else:
-        error("Invalid node kind in 'await', got: " & $node[1].kind)
-    elif node.len > 1 and node[1].kind == nnkCommand and
-         node[1][0].kind == nnkIdent and node[1][0].ident == !"await":
-      # foo await x
-      var newCommand = node
-      result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
-                newCommand, node)
-
-  of nnkVarSection, nnkLetSection:
-    case node[0][2].kind
-    of nnkCommand:
-      if node[0][2][0].kind == nnkIdent and node[0][2][0].ident == !"await":
-        # var x = await y
-        var newVarSection = node # TODO: Should this use copyNimNode?
-        result.createVar("future" & $node[0][0].ident, node[0][2][1],
-          newVarSection[0][2], newVarSection, node)
-    else: discard
-  of nnkAsgn:
-    case node[1].kind
-    of nnkCommand:
-      if node[1][0].ident == !"await":
-        # x = await y
-        var newAsgn = node
-        result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn, node)
-    else: discard
-  of nnkDiscardStmt:
-    # discard await x
-    if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
-          node[0][0].ident == !"await":
-      var newDiscard = node
-      result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
-                newDiscard[0], newDiscard, node)
-  of nnkTryStmt:
-    # try: await x; except: ...
-    result = newNimNode(nnkStmtList, node)
-    template wrapInTry(n, tryBody: expr) =
-      var temp = n
-      n[0] = tryBody
-      tryBody = temp
-
-      # Transform ``except`` body.
-      # TODO: Could we perform some ``await`` transformation here to get it
-      # working in ``except``?
-      tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, nil)
-
-    proc processForTry(n: NimNode, i: var int,
-                       res: NimNode): bool {.compileTime.} =
-      ## Transforms the body of the tryStmt. Does not transform the
-      ## body in ``except``.
-      ## Returns true if the tryStmt node was transformed into an ifStmt.
-      result = false
-      var skipped = n.skipStmtList()
-      while i < skipped.len:
-        var processed = processBody(skipped[i], retFutureSym,
-                                    subTypeIsVoid, n)
-
-        # Check if we transformed the node into an exception check.
-        # This suggests skipped[i] contains ``await``.
-        if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
-          processed = processed.skipUntilStmtList()
-          expectKind(processed, nnkStmtList)
-          expectKind(processed[2][1], nnkElse)
-          i.inc
-
-          if not processForTry(n, i, processed[2][1][0]):
-            # We need to wrap the nnkElse nodes back into a tryStmt.
-            # As they are executed if an exception does not happen
-            # inside the awaited future.
-            # The following code will wrap the nodes inside the
-            # original tryStmt.
-            wrapInTry(n, processed[2][1][0])
-
-          res.add processed
-          result = true
-        else:
-          res.add skipped[i]
-          i.inc
-    var i = 0
-    if not processForTry(node, i, result):
-      # If the tryStmt hasn't been transformed we can just put the body
-      # back into it.
-      wrapInTry(node, result)
-    return
-  else: discard
-
-  for i in 0 .. <result.len:
-    result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, nil)
-
-proc getName(node: NimNode): string {.compileTime.} =
-  case node.kind
-  of nnkPostfix:
-    return $node[1].ident
-  of nnkIdent:
-    return $node.ident
-  of nnkEmpty:
-    return "anonymous"
-  else:
-    error("Unknown name.")
-
-proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
-  ## This macro transforms a single procedure into a closure iterator.
-  ## The ``async`` macro supports a stmtList holding multiple async procedures.
-  if prc.kind notin {nnkProcDef, nnkLambda}:
-      error("Cannot transform this node kind into an async proc." &
-            " Proc definition or lambda node expected.")
-
-  hint("Processing " & prc[0].getName & " as an async proc.")
-
-  let returnType = prc[3][0]
-  var baseType: NimNode
-  # Verify that the return type is a Future[T]
-  if returnType.kind == nnkBracketExpr:
-    let fut = repr(returnType[0])
-    if fut != "Future":
-      error("Expected return type of 'Future' got '" & fut & "'")
-    baseType = returnType[1]
-  elif returnType.kind in nnkCallKinds and $returnType[0] == "[]":
-    let fut = repr(returnType[1])
-    if fut != "Future":
-      error("Expected return type of 'Future' got '" & fut & "'")
-    baseType = returnType[2]
-  elif returnType.kind == nnkEmpty:
-    baseType = returnType
-  else:
-    error("Expected return type of 'Future' got '" & repr(returnType) & "'")
-
-  let subtypeIsVoid = returnType.kind == nnkEmpty or
-        (baseType.kind == nnkIdent and returnType[1].ident == !"void")
-
-  var outerProcBody = newNimNode(nnkStmtList, prc[6])
-
-  # -> var retFuture = newFuture[T]()
-  var retFutureSym = genSym(nskVar, "retFuture")
-  var subRetType =
-    if returnType.kind == nnkEmpty: newIdentNode("void")
-    else: baseType
-  outerProcBody.add(
-    newVarStmt(retFutureSym,
-      newCall(
-        newNimNode(nnkBracketExpr, prc[6]).add(
-          newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
-          subRetType),
-      newLit(prc[0].getName)))) # Get type from return type of this proc
-
-  # -> iterator nameIter(): FutureBase {.closure.} =
-  # ->   {.push warning[resultshadowed]: off.}
-  # ->   var result: T
-  # ->   {.pop.}
-  # ->   <proc_body>
-  # ->   complete(retFuture, result)
-  var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
-  var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil)
-  if not subtypeIsVoid:
-    procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
-      newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
-        newIdentNode("warning"), newIdentNode("resultshadowed")),
-      newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
-
-    procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add(
-      newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
-
-    procBody.insert(2, newNimNode(nnkPragma).add(
-      newIdentNode("pop"))) # -> {.pop.})
-
-    procBody.add(
-      newCall(newIdentNode("complete"),
-        retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
-  else:
-    # -> complete(retFuture)
-    procBody.add(newCall(newIdentNode("complete"), retFutureSym))
-
-  var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
-                                procBody, nnkIteratorDef)
-  closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
-  outerProcBody.add(closureIterator)
-
-  # -> createCb(retFuture)
-  #var cbName = newIdentNode("cb")
-  var procCb = newCall(bindSym"createCb", retFutureSym, iteratorNameSym,
-                       newStrLitNode(prc[0].getName))
-  outerProcBody.add procCb
-
-  # -> return retFuture
-  outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym)
-
-  result = prc
-
-  # Remove the 'async' pragma.
-  for i in 0 .. <result[4].len:
-    if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
-      result[4].del(i)
-  result[4] = newEmptyNode()
-  if subtypeIsVoid:
-    # Add discardable pragma.
-    if returnType.kind == nnkEmpty:
-      # Add Future[void]
-      result[3][0] = parseExpr("Future[void]")
-
-  result[6] = outerProcBody
-
-  #echo(treeRepr(result))
-  #if prc[0].getName == "testInfix":
-  #  echo(toStrLit(result))
-
-macro async*(prc: stmt): stmt {.immediate.} =
-  ## Macro which processes async procedures into the appropriate
-  ## iterators and yield statements.
-  if prc.kind == nnkStmtList:
-    for oneProc in prc:
-      result = newStmtList()
-      result.add asyncSingleProc(oneProc)
-  else:
-    result = asyncSingleProc(prc)
+include asyncmacro
 
 proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
   ## Reads a line of data from ``socket``. Returned future will complete once