1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
/**
* Copyright (C) 2012 Typesafe Inc. <http://www.typesafe.com>
*/
package scala.async
import language.experimental.macros
import scala.reflect.macros.Context
import scala.collection.mutable.ListBuffer
import scala.concurrent.{Future, Promise, ExecutionContext, future}
import ExecutionContext.Implicits.global
import scala.util.control.NonFatal
import scala.util.continuations.{shift, reset, cpsParam}
/*
* @author Philipp Haller
*/
object Async extends AsyncBase {
lazy val futureSystem = ScalaConcurrentFutureSystem
type FS = ScalaConcurrentFutureSystem.type
def async[T](body: T) = macro asyncImpl[T]
override def asyncImpl[T: c.WeakTypeTag](c: Context)(body: c.Expr[T]): c.Expr[Future[T]] = super.asyncImpl[T](c)(body)
}
object AsyncId extends AsyncBase {
lazy val futureSystem = IdentityFutureSystem
type FS = IdentityFutureSystem.type
def async[T](body: T) = macro asyncImpl[T]
override def asyncImpl[T: c.WeakTypeTag](c: Context)(body: c.Expr[T]): c.Expr[T] = super.asyncImpl[T](c)(body)
}
/**
* A base class for the `async` macro. Subclasses must provide:
*
* - Concrete types for a given future system
* - Tree manipulations to create and complete the equivalent of Future and Promise
* in that system.
* - The `async` macro declaration itself, and a forwarder for the macro implementation.
* (The latter is temporarily needed to workaround a bug in the macro system)
*
* The default implementation, [[scala.async.Async]], binds the macro to `scala.concurrent._`.
*/
abstract class AsyncBase extends AsyncUtils {
self =>
type FS <: FutureSystem
val futureSystem: FS
/**
* A call to `await` must be nested in an enclosing `async` block.
*
* A call to await does not block the thread, rather it is a delimiter
* used by the enclosing `async` macro. Code following the `await`
* call.
*
* @param awaitable The future from which a value is awaited
* @tparam T The type of that value
* @return The value
*/
// TODO Replace with `@compileTimeOnly when this is implemented SI-6539
@deprecated("`await` must be enclosed in an `async` block", "0.1")
def await[T](awaitable: futureSystem.Fut[T]): T = ???
def asyncImpl[T: c.WeakTypeTag](c: Context)(body: c.Expr[T]): c.Expr[futureSystem.Fut[T]] = {
import c.universe._
import Flag._
val builder = new ExprBuilder[c.type, futureSystem.type](c, self.futureSystem)
import builder.defn._
import builder.name
import builder.futureSystemOps
val awaitMethod = awaitSym(c)
body.tree match {
case Block(stats, expr) =>
val asyncBlockBuilder = new builder.AsyncBlockBuilder(stats, expr, 0, 1000, 1000, Map())
asyncBlockBuilder.asyncStates foreach vprintln
val handlerCases: List[CaseDef] = asyncBlockBuilder.mkCombinedHandlerCases[T]()
val combinedHander = c.Expr[Int => Unit](builder.mkFunction(handlerCases))
val localVarTrees = asyncBlockBuilder.asyncStates.init.flatMap(_.allVarDefs).toList
/*
def resume(): Unit = {
try {
combinedHander(state)
} catch {
case NonFatal(t) => result.failure(t)
}
}
*/
val nonFatalModule = builder.defn.NonFatalClass
val resumeFunTree: c.Tree = DefDef(Modifiers(), name.resume, Nil, List(Nil), Ident(definitions.UnitClass),
Try(
reify {
combinedHander.splice.apply(c.Expr[Int](Ident(name.state)).splice)
}.tree
,
List(
CaseDef(
Apply(Ident(nonFatalModule), List(Bind(name.tr, Ident(nme.WILDCARD)))),
EmptyTree,
Block(List({
val t = c.Expr[Throwable](Ident(name.tr))
futureSystemOps.completeProm[T](c.Expr[futureSystem.Prom[T]](Ident(name.result)), reify(scala.util.Failure(t.splice))).tree
}), c.literalUnit.tree))), EmptyTree))
val prom: Expr[futureSystem.Prom[T]] = reify {
val result$async = futureSystemOps.createProm[T].splice
var state$async = 0
futureSystemOps.future[Unit] {
c.Expr[Unit](Block(
localVarTrees :+ resumeFunTree,
Apply(Ident(name.resume), Nil)))
}(futureSystemOps.execContext).splice
result$async
}
val result = futureSystemOps.promiseToFuture(prom)
vprintln(s"${c.macroApplication} \nexpands to:\n ${result.tree}")
result
case tree =>
c.abort(c.macroApplication.pos, s"expression not supported by async: ${tree}")
}
}
}
|