@@ -24,6 +24,65 @@ object ScalaConcurrentAsync extends AsyncBase {
24
24
override def asyncImpl [T : c.WeakTypeTag ](c : whitebox.Context )
25
25
(body : c.Expr [T ])
26
26
(execContext : c.Expr [futureSystem.ExecContext ]): c.Expr [Future [T ]] = {
27
- super .asyncImpl[T ](c)(body)(execContext)
27
+ import c .universe ._
28
+ if (! c.compilerSettings.contains(" -Xasync" )) {
29
+ super .asyncImpl[T ](c)(body)(execContext)
30
+ } else try {
31
+ val awaitSym = typeOf[Async .type ].decl(TermName (" await" ))
32
+ def mark (t : DefDef ): Tree = {
33
+ import language .reflectiveCalls
34
+ c.internal.asInstanceOf [{
35
+ def markForAsyncTransform (owner : Symbol , method : DefDef , awaitSymbol : Symbol , config : Map [String , AnyRef ]): DefDef
36
+ }].markForAsyncTransform(c.internal.enclosingOwner, t, awaitSym, Map .empty)
37
+ }
38
+ val name = TypeName (" stateMachine$async_" + body.tree.pos.line)
39
+ // TODO all but the `apply` method code could live in a abstract base class shipped as runtime support.
40
+
41
+ // TODO scala-async accidentally started catching NonFatal exceptions in:
42
+ // https://github.com/scala/scala-async/commit/e3ff0382ae4e015fc69da8335450718951714982#diff-136ab0b6ecaee5d240cd109e2b17ccb2R411
43
+ // This follows the new behaviour but should we fix the regression?
44
+
45
+ val tree = q """
46
+
47
+ final class $name extends _root_.scala.Function1[_root_.scala.util.Try[_root_.scala.AnyRef], _root_.scala.Unit] {
48
+ private val execContext $$ async = ${execContext.tree}
49
+ private val result $$ async: _root_.scala.concurrent.Promise[_root_.scala.AnyRef] = _root_.scala.concurrent.Promise[_root_.scala.AnyRef]();
50
+
51
+ // FSM translated method
52
+ ${mark(q """ override def apply(tr $$ async: _root_.scala.util.Try[_root_.scala.AnyRef]) = ${body.tree}""" )}
53
+
54
+ // Required methods
55
+ protected var state $$ async: _root_.scala.Int = 0
56
+
57
+ protected def completeFailure(t: _root_.scala.Throwable): _root_.scala.Unit = result $$ async.complete(_root_.scala.util.Failure(t))
58
+
59
+ protected def completeSuccess(value: _root_.scala.AnyRef): _root_.scala.Unit = result $$ async.complete(_root_.scala.util.Success(value))
60
+ protected def onComplete(f: _root_.scala.concurrent.Future[_root_.scala.AnyRef]): _root_.scala.Unit = f.onComplete(this)(execContext $$ async)
61
+ protected def getCompleted(f: _root_.scala.concurrent.Future[_root_.scala.AnyRef]): _root_.scala.util.Try[_root_.scala.AnyRef] = {
62
+ if (f.isCompleted) {
63
+ f.value.get
64
+ } else null
65
+ }
66
+ protected def tryGet(tr: _root_.scala.util.Try[_root_.scala.AnyRef]): _root_.scala.AnyRef = tr match {
67
+ case _root_.scala.util.Success(value) =>
68
+ value.asInstanceOf[_root_.scala.AnyRef]
69
+ case _root_.scala.util.Failure(throwable) =>
70
+ completeFailure(throwable)
71
+ this // sentinel value to indicate the dispatch loop should exit.
72
+ }
73
+ def start(): _root_.scala.concurrent.Future[_root_.scala.AnyRef] = {
74
+ // This cast is safe because we know that `def apply` does not consult its argument when `state == 0`.
75
+ _root_.scala.concurrent.Future.unit.asInstanceOf[_root_.scala.concurrent.Future[_root_.scala.AnyRef]].onComplete(this)(execContext $$ async)
76
+ result $$ async.future
77
+ }
78
+
79
+ }
80
+ new $name().start().asInstanceOf[ ${c.macroApplication.tpe}]
81
+ """
82
+ c.Expr [Future [T ]](tree)
83
+ } catch {
84
+ case e : ReflectiveOperationException =>
85
+ c.abort(c.macroApplication.pos, " -Xasync is provided as a Scala compiler option, but the async macro is unable to call c.internal.markForAsyncTransform. " + e.getClass.getName + " " + e.getMessage)
86
+ }
28
87
}
29
88
}
0 commit comments