From cce4c3778b3f33885d340d53d1986289788b79b2 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 9 Nov 2012 15:40:23 +0100 Subject: Remove sleeps from the tests. --- src/test/scala/scala/async/run/await0/Await0Spec.scala | 5 ----- src/test/scala/scala/async/run/block0/AsyncSpec.scala | 1 - src/test/scala/scala/async/run/block1/block1.scala | 1 - src/test/scala/scala/async/run/ifelse0/IfElse0.scala | 1 - src/test/scala/scala/async/run/ifelse1/IfElse1.scala | 1 - src/test/scala/scala/async/run/ifelse2/ifelse2.scala | 1 - src/test/scala/scala/async/run/ifelse3/IfElse3.scala | 1 - src/test/scala/scala/async/run/match0/Match0.scala | 1 - 8 files changed, 12 deletions(-) (limited to 'src/test/scala') diff --git a/src/test/scala/scala/async/run/await0/Await0Spec.scala b/src/test/scala/scala/async/run/await0/Await0Spec.scala index e7740e0..50c8fa2 100644 --- a/src/test/scala/scala/async/run/await0/Await0Spec.scala +++ b/src/test/scala/scala/async/run/await0/Await0Spec.scala @@ -20,27 +20,22 @@ class Await0Class { import ExecutionContext.Implicits.global def m1(x: Double): Future[Double] = future { - Thread.sleep(200) x + 2.0 } def m2(x: Float): Future[Float] = future { - Thread.sleep(200) x + 2.0f } def m3(x: Char): Future[Char] = future { - Thread.sleep(200) (x.toInt + 2).toChar } def m4(x: Short): Future[Short] = future { - Thread.sleep(200) (x + 2).toShort } def m5(x: Byte): Future[Byte] = future { - Thread.sleep(200) (x + 2).toByte } diff --git a/src/test/scala/scala/async/run/block0/AsyncSpec.scala b/src/test/scala/scala/async/run/block0/AsyncSpec.scala index f56e394..5a7247c 100644 --- a/src/test/scala/scala/async/run/block0/AsyncSpec.scala +++ b/src/test/scala/scala/async/run/block0/AsyncSpec.scala @@ -20,7 +20,6 @@ class Test1Class { import ExecutionContext.Implicits.global def m1(x: Int): Future[Int] = future { - Thread.sleep(1000) x + 2 } diff --git a/src/test/scala/scala/async/run/block1/block1.scala b/src/test/scala/scala/async/run/block1/block1.scala index 8f21688..a449805 100644 --- a/src/test/scala/scala/async/run/block1/block1.scala +++ b/src/test/scala/scala/async/run/block1/block1.scala @@ -20,7 +20,6 @@ class Test1Class { import ExecutionContext.Implicits.global def m1(x: Int): Future[Int] = future { - Thread.sleep(1000) x + 2 } diff --git a/src/test/scala/scala/async/run/ifelse0/IfElse0.scala b/src/test/scala/scala/async/run/ifelse0/IfElse0.scala index eca3acd..0363a75 100644 --- a/src/test/scala/scala/async/run/ifelse0/IfElse0.scala +++ b/src/test/scala/scala/async/run/ifelse0/IfElse0.scala @@ -20,7 +20,6 @@ class TestIfElseClass { import ExecutionContext.Implicits.global def m1(x: Int): Future[Int] = future { - Thread.sleep(1000) x + 2 } diff --git a/src/test/scala/scala/async/run/ifelse1/IfElse1.scala b/src/test/scala/scala/async/run/ifelse1/IfElse1.scala index 128f02a..3ca3a94 100644 --- a/src/test/scala/scala/async/run/ifelse1/IfElse1.scala +++ b/src/test/scala/scala/async/run/ifelse1/IfElse1.scala @@ -20,7 +20,6 @@ class TestIfElse1Class { import ExecutionContext.Implicits.global def base(x: Int): Future[Int] = future { - Thread.sleep(1000) x + 2 } diff --git a/src/test/scala/scala/async/run/ifelse2/ifelse2.scala b/src/test/scala/scala/async/run/ifelse2/ifelse2.scala index f894923..84974b6 100644 --- a/src/test/scala/scala/async/run/ifelse2/ifelse2.scala +++ b/src/test/scala/scala/async/run/ifelse2/ifelse2.scala @@ -20,7 +20,6 @@ class TestIfElse2Class { import ExecutionContext.Implicits.global def base(x: Int): Future[Int] = future { - Thread.sleep(1000) x + 2 } diff --git a/src/test/scala/scala/async/run/ifelse3/IfElse3.scala b/src/test/scala/scala/async/run/ifelse3/IfElse3.scala index 0c0dbfe..d475a0c 100644 --- a/src/test/scala/scala/async/run/ifelse3/IfElse3.scala +++ b/src/test/scala/scala/async/run/ifelse3/IfElse3.scala @@ -20,7 +20,6 @@ class TestIfElse3Class { import ExecutionContext.Implicits.global def base(x: Int): Future[Int] = future { - Thread.sleep(1000) x + 2 } diff --git a/src/test/scala/scala/async/run/match0/Match0.scala b/src/test/scala/scala/async/run/match0/Match0.scala index 3c7e297..6a17e2b 100644 --- a/src/test/scala/scala/async/run/match0/Match0.scala +++ b/src/test/scala/scala/async/run/match0/Match0.scala @@ -20,7 +20,6 @@ class TestMatchClass { import ExecutionContext.Implicits.global def m1(x: Int): Future[Int] = future { - Thread.sleep(1000) x + 2 } -- cgit v1.2.3 From 6226c3583b742e0576e5c598fba4e64622c99165 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 9 Nov 2012 18:33:13 +0100 Subject: Avoid hygiene problems by suffixing result/result/state. Perhaps we should freshen them, but that will be a little awkward in our reify block. --- src/main/scala/scala/async/Async.scala | 14 ++++-- src/main/scala/scala/async/ExprBuilder.scala | 9 ++-- .../scala/scala/async/run/hygiene/Hygiene.scala | 52 ++++++++++++++++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 src/test/scala/scala/async/run/hygiene/Hygiene.scala (limited to 'src/test/scala') diff --git a/src/main/scala/scala/async/Async.scala b/src/main/scala/scala/async/Async.scala index e8f5263..0a3bbbf 100644 --- a/src/main/scala/scala/async/Async.scala +++ b/src/main/scala/scala/async/Async.scala @@ -129,19 +129,25 @@ abstract class AsyncBase extends AsyncUtils { val prom: Expr[futureSystem.Prom[T]] = reify { - val result = futureSystemOps.createProm[T].splice - var state = 0 + val result$async = futureSystemOps.createProm[T].splice + var state$async = 0 futureSystemOps.future[Unit] { c.Expr[Unit](Block( localVarTrees :+ resumeFunTree, Apply(Ident(name.resume), List()))) }(futureSystemOps.execContext).splice - result + result$async } val result = futureSystemOps.promiseToFuture(prom) - // println(s"${c.macroApplication} \nexpands to:\n ${result.tree}") +// println(s"${c.macroApplication} \nexpands to:\n ${result.tree}") +// val positions = result.tree.collect { +// case t => (t.toString.take(10).replaceAll("\n", "\\n"), t.pos) +// } +// println(positions.mkString("\n")) + result + case tree => c.abort(c.macroApplication.pos, s"expression not supported by async: ${tree}") } diff --git a/src/main/scala/scala/async/ExprBuilder.scala b/src/main/scala/scala/async/ExprBuilder.scala index 65e98e0..b563869 100644 --- a/src/main/scala/scala/async/ExprBuilder.scala +++ b/src/main/scala/scala/async/ExprBuilder.scala @@ -21,9 +21,10 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy object name { // TODO do we need to freshen any of these? - val resume = newTermName("resume") - val state = newTermName("state") - val result = newTermName("result") + def expanded(prefix: String) = newTermName(prefix + "$async") + val resume = expanded("resume") + val state = expanded("state") + val result = expanded("result") val tr = newTermName("tr") val any = newTermName("any") val x1 = newTermName("x$1") @@ -256,7 +257,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy * case any if any == num => * stats * awaitable.onComplete { - * (try: Try[A]) => + * (tr: Try[A]) => * resultName = tr.get * resume() * } diff --git a/src/test/scala/scala/async/run/hygiene/Hygiene.scala b/src/test/scala/scala/async/run/hygiene/Hygiene.scala new file mode 100644 index 0000000..0cc68a4 --- /dev/null +++ b/src/test/scala/scala/async/run/hygiene/Hygiene.scala @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2012 Typesafe Inc. + */ + +package scala.async +package run +package hygiene + +import language.{reflectiveCalls, postfixOps} +import concurrent._ +import scala.concurrent.{Future, ExecutionContext, future, Await} +import scala.concurrent.duration._ +import scala.async.Async.{async, await} +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + + +class HygieneClass { + + import ExecutionContext.Implicits.global + + def m1(x: Int): Future[Int] = future { + x + 2 + } + + def m2(y: Int) = { + val state = 23 + val result: Any = "result" + def resume(): Any = "resume" + async { + val f1 = m1(state) + val x = await(f1) + val y = await(future(result)) + val z = await(future(resume())) + (x, y, z) + } + } +} + +@RunWith(classOf[JUnit4]) +class HygieneSpec { + + @Test def `is hygenic`() { + val o = new HygieneClass + val fut = o.m2(10) + val res = Await.result(fut, 2 seconds) + res._1 mustBe (25) + res._2 mustBe ("result") + res._3 mustBe ("resume") + } +} -- cgit v1.2.3 From 23209e2f10a123aa7ccd512454cd18ace81a2476 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 9 Nov 2012 18:41:16 +0100 Subject: Test with less trivial types. Works like it says on the box. --- .../scala/async/run/toughtype/ToughType.scala | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/scala/scala/async/run/toughtype/ToughType.scala (limited to 'src/test/scala') diff --git a/src/test/scala/scala/async/run/toughtype/ToughType.scala b/src/test/scala/scala/async/run/toughtype/ToughType.scala new file mode 100644 index 0000000..f576ddc --- /dev/null +++ b/src/test/scala/scala/async/run/toughtype/ToughType.scala @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2012 Typesafe Inc. + */ + +package scala.async +package run +package toughtype + +import language.{reflectiveCalls, postfixOps} +import scala.concurrent._ +import scala.concurrent.duration._ +import scala.async.Async._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + + +object ToughTypeObject { + + import ExecutionContext.Implicits.global + + class Inner + + def m2 = async[(List[_], ToughTypeObject.Inner)] { + val y = await(future[List[_]](Nil)) + val z = await(future[Inner](new Inner)) + (y, z) + } +} + +@RunWith(classOf[JUnit4]) +class ToughTypeSpec { + + @Test def `propogates tough types`() { + val fut = ToughTypeObject.m2 + val res: (List[_], scala.async.run.toughtype.ToughTypeObject.Inner) = Await.result(fut, 2 seconds) + res._1 mustBe (Nil) + } +} -- cgit v1.2.3 From cf444641bff609964ee16c5aec19029c1ae04da1 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 11 Nov 2012 00:53:05 +0100 Subject: Synthesize `case 23 => rhs` rather than `case any @ (_: Int) if any == 25)` --- src/main/scala/scala/async/ExprBuilder.scala | 14 +------------- src/test/scala/scala/async/run/await0/Await0Spec.scala | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) (limited to 'src/test/scala') diff --git a/src/main/scala/scala/async/ExprBuilder.scala b/src/main/scala/scala/async/ExprBuilder.scala index 7d4bf35..e298a05 100644 --- a/src/main/scala/scala/async/ExprBuilder.scala +++ b/src/main/scala/scala/async/ExprBuilder.scala @@ -30,13 +30,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy // TODO do we need to freshen any of these? val resume = expandedTermName("resume") - val any = newTermName("any") val x1 = newTermName("x$1") - - val apply = newTermName("apply") - val isDefinedAt = newTermName("isDefinedAt") - - val asyncHander = expandedTypeName("Handler") } private val execContext = futureSystemOps.execContext @@ -71,13 +65,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy mkHandlerCase(num, Block(rhs: _*)) def mkHandlerCase(num: Int, rhs: c.Tree): CaseDef = - CaseDef( - // pattern - Bind(name.any, Typed(Ident(nme.WILDCARD), Ident(definitions.IntClass))), - // guard - mkAny_==(c.Expr(Ident(name.any)))(c.literal(num)).tree, - rhs - ) + CaseDef(c.literal(num).tree, EmptyTree, rhs) private def paramValDef(name: TermName, sym: Symbol) = ValDef(Modifiers(PARAM), name, Ident(sym), EmptyTree) diff --git a/src/test/scala/scala/async/run/await0/Await0Spec.scala b/src/test/scala/scala/async/run/await0/Await0Spec.scala index 50c8fa2..42d4ef2 100644 --- a/src/test/scala/scala/async/run/await0/Await0Spec.scala +++ b/src/test/scala/scala/async/run/await0/Await0Spec.scala @@ -70,4 +70,3 @@ class Await0Spec { res mustBe (26.0) } } - -- cgit v1.2.3 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/test/scala') 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 From 40da520a74fbb13b0ed1c5acb7ddf5c521fd8acc Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 11 Nov 2012 22:12:50 +0100 Subject: Add a test to interrogate the expanded tree. - checks that only one function is synthesized - checks the set of vars created (TODO minimize these) - use x$1 rather than x1 for the freshened names for lifted vars. - make execContext a val, not a var. --- src/main/scala/scala/async/Async.scala | 2 +- src/main/scala/scala/async/ExprBuilder.scala | 6 ++-- src/test/scala/scala/async/TestUtils.scala | 9 +++-- src/test/scala/scala/async/TreeInterrogation.scala | 38 ++++++++++++++++++++++ 4 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 src/test/scala/scala/async/TreeInterrogation.scala (limited to 'src/test/scala') diff --git a/src/main/scala/scala/async/Async.scala b/src/main/scala/scala/async/Async.scala index 9f43b0b..4f7fa01 100644 --- a/src/main/scala/scala/async/Async.scala +++ b/src/main/scala/scala/async/Async.scala @@ -139,7 +139,7 @@ abstract class AsyncBase { // Initialize the state var state$async = 0 // Resolve the execution context - var execContext$async = futureSystemOps.execContext.splice + val execContext$async = futureSystemOps.execContext.splice var onCompleteHandler$async: util.Try[Any] => Unit = null // Spawn a future to: diff --git a/src/main/scala/scala/async/ExprBuilder.scala b/src/main/scala/scala/async/ExprBuilder.scala index 09489d4..1a3f866 100644 --- a/src/main/scala/scala/async/ExprBuilder.scala +++ b/src/main/scala/scala/async/ExprBuilder.scala @@ -34,6 +34,8 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy val x1 = newTermName("x$1") val tr = newTermName("tr") val onCompleteHandler = suffixedName("onCompleteHandler") + + def fresh(name: TermName) = newTermName(c.fresh("" + name + "$")) } private val execContext = futureSystemOps.execContext @@ -295,7 +297,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy for (stat <- stats) stat match { // the val name = await(..) pattern case ValDef(mods, name, tpt, Apply(fun, args)) if fun.symbol == Async_await => - val newName = c.fresh(name) + val newName = builder.name.fresh(name) toRename += (stat.symbol -> newName) asyncStates += stateBuilder.complete(args.head, newName, tpt, toRename).result // complete with await @@ -309,7 +311,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](val c: C, val futureSy case ValDef(mods, name, tpt, rhs) => checkForUnsupportedAwait(rhs) - val newName = c.fresh(name) + val newName = builder.name.fresh(name) toRename += (stat.symbol -> newName) // when adding assignment need to take `toRename` into account stateBuilder.addVarDef(mods, newName, tpt, rhs, toRename) diff --git a/src/test/scala/scala/async/TestUtils.scala b/src/test/scala/scala/async/TestUtils.scala index 0920659..bac22a3 100644 --- a/src/test/scala/scala/async/TestUtils.scala +++ b/src/test/scala/scala/async/TestUtils.scala @@ -40,11 +40,14 @@ trait TestUtils { } def eval(code: String, compileOptions: String = ""): Any = { + val tb = mkToolbox(compileOptions) + tb.eval(tb.parse(code)) + } + + def mkToolbox(compileOptions: String = "") = { val m = scala.reflect.runtime.currentMirror import scala.tools.reflect.ToolBox - val tb = m.mkToolBox(options = compileOptions) - val result = tb.eval(tb.parse(code)) - result + m.mkToolBox(options = compileOptions) } def expectError(errorSnippet: String, compileOptions: String = "")(code: String) { diff --git a/src/test/scala/scala/async/TreeInterrogation.scala b/src/test/scala/scala/async/TreeInterrogation.scala new file mode 100644 index 0000000..7c277b1 --- /dev/null +++ b/src/test/scala/scala/async/TreeInterrogation.scala @@ -0,0 +1,38 @@ +package scala.async + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import AsyncId._ + +@RunWith(classOf[JUnit4]) +class TreeInterrogation { + @Test + def `a minimal set of vals are lifted to vars`() { + val cm = reflect.runtime.currentMirror + val tb = mkToolbox() + val tree = mkToolbox().parse( + """| import _root_.scala.async.AsyncId._ + | async { + | val x = await(1) + | val y = x * 2 + | val z = await(x * 3) + | z + | }""".stripMargin) + val tree1 = tb.typeCheck(tree) + + // println(cm.universe.showRaw(tree1)) + + import tb.mirror.universe._ + val functions = tree1.collect { + case f: Function => f + } + functions.size mustBe 1 + + val varDefs = tree1.collect { + case ValDef(mods, name, _, _) if mods.hasFlag(Flag.MUTABLE) => name + } + // TODO no need to lift `y` as it is only accessed from a single state. + varDefs.map(_.decoded).toSet mustBe(Set("state$async", "onCompleteHandler$async", "x$1", "z$1", "y$1")) + } +} -- cgit v1.2.3