From 23e24fc738e2d4ad967090c375cd356cf45745ce Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 11 Nov 2012 17:22:17 +0100 Subject: Fixes #7, allow async blocks without await or with a single expression. --- src/main/scala/scala/async/Async.scala | 158 ++++++++++----------- src/main/scala/scala/async/ExprBuilder.scala | 14 +- .../scala/async/run/noawait/NoAwaitSpec.scala | 34 +++++ 3 files changed, 121 insertions(+), 85 deletions(-) create mode 100644 src/test/scala/scala/async/run/noawait/NoAwaitSpec.scala (limited to 'src') 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) + } +} -- cgit v1.2.3