/* * Copyright (C) 2012 Typesafe Inc. */ package scala.async import scala.language.experimental.macros import scala.reflect.macros.Context /* * @author Philipp Haller */ object Async extends AsyncBase { import scala.concurrent.Future 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 bug SI-6650 in the macro system) * * The default implementation, [[scala.async.Async]], binds the macro to `scala.concurrent._`. */ abstract class AsyncBase { 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 current thread, rather it is a delimiter * used by the enclosing `async` macro. Code following the `await` * call is executed asynchronously, when the argument of `await` has been completed. * * @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._ val builder = ExprBuilder[c.type, futureSystem.type](c, self.futureSystem) val anaylzer = AsyncAnalysis[c.type](c) val utils = TransformUtils[c.type](c) import utils.{name, defn} import builder.futureSystemOps anaylzer.reportUnsupportedAwaits(body.tree) // Transform to A-normal form: // - no await calls in qualifiers or arguments, // - if/match only used in statement position. val anfTree: Block = { val anf = AnfTransform[c.type](c) val stats1 :+ expr1 = anf(body.tree) val block = Block(stats1, expr1) c.typeCheck(block).asInstanceOf[Block] } // Analyze the block to find locals that will be accessed from multiple // states of our generated state machine, e.g. a value assigned before // an `await` and read afterwards. val renameMap: Map[Symbol, TermName] = { anaylzer.valDefsUsedInSubsequentStates(anfTree).map { vd => (vd.symbol, name.fresh(vd.name)) }.toMap } val asyncBlock: builder.AsyncBlock = builder.build(anfTree, renameMap) import asyncBlock.asyncStates logDiagnostics(c)(anfTree, asyncStates.map(_.toString)) val localVarTrees = anfTree.collect { case vd@ValDef(_, _, tpt, _) if renameMap contains vd.symbol => utils.mkVarDefTree(tpt.tpe, renameMap(vd.symbol)) } val onCompleteHandler = asyncBlock.onCompleteHandler val resumeFunTree = asyncBlock.resumeFunTree[T] 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 val 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 that are accessed from multiple states 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) AsyncUtils.vprintln(s"async state machine transform expands to:\n ${result.tree}") result } def logDiagnostics(c: Context)(anfTree: c.Tree, states: Seq[String]) { def location = try { c.macroApplication.pos.source.path } catch { case _: UnsupportedOperationException => c.macroApplication.pos.toString } AsyncUtils.vprintln(s"In file '$location':") AsyncUtils.vprintln(s"${c.macroApplication}") AsyncUtils.vprintln(s"ANF transform expands to:\n $anfTree") states foreach (s => AsyncUtils.vprintln(s)) } }