aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2012-11-11 17:22:17 +0100
committerJason Zaugg <jzaugg@gmail.com>2012-11-11 17:22:17 +0100
commit23e24fc738e2d4ad967090c375cd356cf45745ce (patch)
treec2fff138f04d76ec990654e12ef99c04d46a06ab
parent412682afb20def5e50533c300439828078d5e657 (diff)
downloadscala-async-23e24fc738e2d4ad967090c375cd356cf45745ce.tar.gz
scala-async-23e24fc738e2d4ad967090c375cd356cf45745ce.tar.bz2
scala-async-23e24fc738e2d4ad967090c375cd356cf45745ce.zip
Fixes #7, allow async blocks without await or with a single expression.
-rw-r--r--src/main/scala/scala/async/Async.scala158
-rw-r--r--src/main/scala/scala/async/ExprBuilder.scala14
-rw-r--r--src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala34
3 files changed, 121 insertions, 85 deletions
diff --git a/src/main/scala/scala/async/Async.scala b/src/main/scala/scala/async/Async.scala
index 16bca6b..9f43b0b 100644
--- a/src/main/scala/scala/async/Async.scala
+++ b/src/main/scala/scala/async/Async.scala
@@ -75,95 +75,93 @@ abstract class AsyncBase {
import builder.defn._
import builder.name
import builder.futureSystemOps
+ val (stats, expr) = body.tree match {
+ case Block(stats, expr) => (stats, expr)
+ case tree => (Nil, tree)
+ }
- body.tree match {
- case Block(stats, expr) =>
- val asyncBlockBuilder = new builder.AsyncBlockBuilder(stats, expr, 0, 1000, 1000, Map())
+ val asyncBlockBuilder = new builder.AsyncBlockBuilder(stats, expr, 0, 1000, 1000, Map())
- asyncBlockBuilder.asyncStates foreach (s => vprintln(s))
+ asyncBlockBuilder.asyncStates foreach (s => vprintln(s))
- val handlerCases: List[CaseDef] = asyncBlockBuilder.mkCombinedHandlerCases[T]()
+ val handlerCases: List[CaseDef] = asyncBlockBuilder.mkCombinedHandlerCases[T]()
- val initStates = asyncBlockBuilder.asyncStates.init
- val localVarTrees = initStates.flatMap(_.allVarDefs).toList
+ val initStates = asyncBlockBuilder.asyncStates.init
+ val localVarTrees = initStates.flatMap(_.allVarDefs).toList
- /*
- lazy val onCompleteHandler = (tr: Try[Any]) => state match {
- case 0 => {
- x11 = tr.get.asInstanceOf[Double];
- state = 1;
- resume()
- }
- ...
- */
- val onCompleteHandler = {
- val onCompleteHandlers = initStates.flatMap(_.mkOnCompleteHandler).toList
- Function(
- List(ValDef(Modifiers(PARAM), name.tr, TypeTree(TryAnyType), EmptyTree)),
- Match(Ident(name.state), onCompleteHandlers))
+ /*
+ lazy val onCompleteHandler = (tr: Try[Any]) => state match {
+ case 0 => {
+ x11 = tr.get.asInstanceOf[Double];
+ state = 1;
+ resume()
}
+ ...
+ */
+ val onCompleteHandler = {
+ val onCompleteHandlers = initStates.flatMap(_.mkOnCompleteHandler).toList
+ Function(
+ List(ValDef(Modifiers(PARAM), name.tr, TypeTree(TryAnyType), EmptyTree)),
+ Match(Ident(name.state), onCompleteHandlers))
+ }
- /*
- def resume(): Unit = {
- try {
- state match {
- case 0 => {
- f11 = exprReturningFuture
- f11.onComplete(onCompleteHandler)(context)
- }
- ...
- }
- } catch {
- case NonFatal(t) => result.failure(t)
- }
- }
- */
- val resumeFunTree: c.Tree = DefDef(Modifiers(), name.resume, Nil, List(Nil), Ident(definitions.UnitClass),
- Try(
- Match(Ident(name.state), handlerCases),
- List(
- CaseDef(
- Apply(Ident(NonFatalClass), 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 {
- // Create the empty promise
- val result$async = futureSystemOps.createProm[T].splice
- // Initialize the state
- var state$async = 0
- // Resolve the execution context
- var execContext$async = futureSystemOps.execContext.splice
- var onCompleteHandler$async: util.Try[Any] => Unit = null
-
- // Spawn a future to:
- futureSystemOps.future[Unit] {
- c.Expr[Unit](Block(
- // define vars for all intermediate results
- localVarTrees :+
- // define the resume() method
- resumeFunTree :+
- // assign onComplete function. (The var breaks the circular dependency with resume)`
- Assign(Ident(name.onCompleteHandler), onCompleteHandler),
- // and get things started by calling resume()
- Apply(Ident(name.resume), Nil)))
- }(c.Expr[futureSystem.ExecContext](Ident(name.execContext))).splice
- // Return the promise from this reify block...
- result$async
+ /*
+ def resume(): Unit = {
+ try {
+ state match {
+ case 0 => {
+ f11 = exprReturningFuture
+ f11.onComplete(onCompleteHandler)(context)
+ }
+ ...
+ }
+ } catch {
+ case NonFatal(t) => result.failure(t)
}
- // ... and return its Future from the macro.
- val result = futureSystemOps.promiseToFuture(prom)
-
- vprintln(s"${c.macroApplication} \nexpands to:\n ${result.tree}")
+ }
+ */
+ val resumeFunTree: c.Tree = DefDef(Modifiers(), name.resume, Nil, List(Nil), Ident(definitions.UnitClass),
+ Try(
+ Match(Ident(name.state), handlerCases),
+ List(
+ CaseDef(
+ Apply(Ident(NonFatalClass), 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 {
+ // Create the empty promise
+ val result$async = futureSystemOps.createProm[T].splice
+ // Initialize the state
+ var state$async = 0
+ // Resolve the execution context
+ var execContext$async = futureSystemOps.execContext.splice
+ var onCompleteHandler$async: util.Try[Any] => Unit = null
+
+ // Spawn a future to:
+ futureSystemOps.future[Unit] {
+ c.Expr[Unit](Block(
+ // define vars for all intermediate results
+ localVarTrees :+
+ // define the resume() method
+ resumeFunTree :+
+ // assign onComplete function. (The var breaks the circular dependency with resume)`
+ Assign(Ident(name.onCompleteHandler), onCompleteHandler),
+ // and get things started by calling resume()
+ Apply(Ident(name.resume), Nil)))
+ }(c.Expr[futureSystem.ExecContext](Ident(name.execContext))).splice
+ // Return the promise from this reify block...
+ result$async
+ }
+ // ... and return its Future from the macro.
+ val result = futureSystemOps.promiseToFuture(prom)
- result
+ vprintln(s"${c.macroApplication} \nexpands to:\n ${result.tree}")
- case tree =>
- c.abort(c.macroApplication.pos, s"expression not supported by async: ${tree}")
- }
+ result
}
}
diff --git a/src/main/scala/scala/async/ExprBuilder.scala b/src/main/scala/scala/async/ExprBuilder.scala
index 22f3738..09489d4 100644
--- a/src/main/scala/scala/async/ExprBuilder.scala
+++ b/src/main/scala/scala/async/ExprBuilder.scala
@@ -22,6 +22,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy
object name {
def suffix(string: String) = string + "$async"
+
def suffixedName(prefix: String) = newTermName(suffix(prefix))
val state = suffixedName("state")
@@ -375,17 +376,19 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy
asyncStates += lastState
def mkCombinedHandlerCases[T](): List[CaseDef] = {
- assert(asyncStates.size > 1)
- val initCases = for (state <- asyncStates.toList.init) yield state.mkHandlerCaseForState()
-
val caseForLastState: CaseDef = {
val lastState = asyncStates.last
val lastStateBody = c.Expr[T](lastState.body)
val rhs = futureSystemOps.completeProm(c.Expr[futureSystem.Prom[T]](Ident(name.result)), reify(scala.util.Success(lastStateBody.splice)))
mkHandlerCase(lastState.state, rhs.tree)
}
-
- initCases :+ caseForLastState
+ asyncStates.toList match {
+ case s :: Nil =>
+ List(caseForLastState)
+ case _ =>
+ val initCases = for (state <- asyncStates.toList.init) yield state.mkHandlerCaseForState()
+ initCases :+ caseForLastState
+ }
}
}
@@ -432,4 +435,5 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy
tpe.member(c.universe.newTermName("await"))
}
}
+
}
diff --git a/src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala b/src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala
new file mode 100644
index 0000000..90be946
--- /dev/null
+++ b/src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala
@@ -0,0 +1,34 @@
+package scala.async
+package run
+package noawait
+
+import AsyncId._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(classOf[JUnit4])
+class NoAwaitSpec {
+ @Test
+ def `async block without await`() {
+ def foo = 1
+ async {
+ foo
+ foo
+ } mustBe (foo)
+ }
+
+ @Test
+ def `async block without await 2`() {
+ async {
+ def x = 0
+ if (x > 0) 0 else 1
+ } mustBe (1)
+ }
+
+ @Test
+ def `async expr without await`() {
+ def foo = 1
+ async(foo) mustBe (foo)
+ }
+}