diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2014-12-11 21:30:35 +1000 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2014-12-15 12:55:30 +1000 |
commit | 1333b3837d405c31baaa44d1db89aab0f7d09349 (patch) | |
tree | bcceb74cff609645543c07c7114ef24a468360d5 /src/main/scala/scala/async/internal/AsyncTransform.scala | |
parent | 61b4c183fa2392522051305ec12ab5f433cc09a7 (diff) | |
download | scala-async-1333b3837d405c31baaa44d1db89aab0f7d09349.tar.gz scala-async-1333b3837d405c31baaa44d1db89aab0f7d09349.tar.bz2 scala-async-1333b3837d405c31baaa44d1db89aab0f7d09349.zip |
Avoid unbounded stack consumption for synchronous control flow
Previously, as sequence of state transitions that did not pass through
an asynchrous boundary incurred stack frames. The trivial loop in
the enclosed test case would then overflow the stack.
This commit merges the `resume` and `apply(tr: Try[Any])` methods into
a `apply`. It changes the body of this method to be an infinite loop
with returns at the terminal points in the state machine (or at a
terminal failure.)
To allow merging of these previously separate matches, states that
contain an await are now allocated two state ids: one for the setup
code that calls `onComplete`, and one for the code in the continuation
that records the result and advances the state machine.
Fixes #93
Diffstat (limited to 'src/main/scala/scala/async/internal/AsyncTransform.scala')
-rw-r--r-- | src/main/scala/scala/async/internal/AsyncTransform.scala | 24 |
1 files changed, 7 insertions, 17 deletions
diff --git a/src/main/scala/scala/async/internal/AsyncTransform.scala b/src/main/scala/scala/async/internal/AsyncTransform.scala index 7d56043..c7a0c65 100644 --- a/src/main/scala/scala/async/internal/AsyncTransform.scala +++ b/src/main/scala/scala/async/internal/AsyncTransform.scala @@ -24,31 +24,29 @@ trait AsyncTransform { val anfTree = futureSystemOps.postAnfTransform(anfTree0) - val resumeFunTreeDummyBody = DefDef(Modifiers(), name.resume, Nil, List(Nil), Ident(definitions.UnitClass), Literal(Constant(()))) - val applyDefDefDummyBody: DefDef = { val applyVParamss = List(List(ValDef(Modifiers(Flag.PARAM), name.tr, TypeTree(futureSystemOps.tryType[Any]), EmptyTree))) - DefDef(NoMods, name.apply, Nil, applyVParamss, TypeTree(definitions.UnitTpe), Literal(Constant(()))) + DefDef(NoMods, name.apply, Nil, applyVParamss, TypeTree(definitions.UnitTpe), literalUnit) } // Create `ClassDef` of state machine with empty method bodies for `resume` and `apply`. val stateMachine: ClassDef = { val body: List[Tree] = { - val stateVar = ValDef(Modifiers(Flag.MUTABLE | Flag.PRIVATE | Flag.LOCAL), name.state, TypeTree(definitions.IntTpe), Literal(Constant(0))) + val stateVar = ValDef(Modifiers(Flag.MUTABLE | Flag.PRIVATE | Flag.LOCAL), name.state, TypeTree(definitions.IntTpe), Literal(Constant(StateAssigner.Initial))) val result = ValDef(NoMods, name.result, TypeTree(futureSystemOps.promType[T](uncheckedBoundsResultTag)), futureSystemOps.createProm[T](uncheckedBoundsResultTag).tree) val execContextValDef = ValDef(NoMods, name.execContext, TypeTree(), execContext) val apply0DefDef: DefDef = { // We extend () => Unit so we can pass this class as the by-name argument to `Future.apply`. // See SI-1247 for the the optimization that avoids creatio - DefDef(NoMods, name.apply, Nil, Nil, TypeTree(definitions.UnitTpe), Apply(Ident(name.resume), Nil)) + DefDef(NoMods, name.apply, Nil, Nil, TypeTree(definitions.UnitTpe), Apply(Ident(name.apply), literalNull :: Nil)) } val extraValDef: ValDef = { // We extend () => Unit so we can pass this class as the by-name argument to `Future.apply`. // See SI-1247 for the the optimization that avoids creatio - ValDef(NoMods, newTermName("extra"), TypeTree(definitions.UnitTpe), Literal(Constant(()))) + ValDef(NoMods, newTermName("extra"), TypeTree(definitions.UnitTpe), literalUnit) } - List(emptyConstructor, stateVar, result, execContextValDef) ++ List(resumeFunTreeDummyBody, applyDefDefDummyBody, apply0DefDef, extraValDef) + List(emptyConstructor, stateVar, result, execContextValDef) ++ List(applyDefDefDummyBody, apply0DefDef, extraValDef) } val tryToUnit = appliedType(definitions.FunctionClass(1), futureSystemOps.tryType[Any], typeOf[Unit]) @@ -90,8 +88,7 @@ trait AsyncTransform { val stateMachineSpliced: Tree = spliceMethodBodies( liftedFields, stateMachine, - atMacroPos(asyncBlock.onCompleteHandler[T]), - atMacroPos(asyncBlock.resumeFunTree[T].rhs) + atMacroPos(asyncBlock.onCompleteHandler[T]) ) def selectStateMachine(selection: TermName) = Select(Ident(name.stateMachine), selection) @@ -131,10 +128,9 @@ trait AsyncTransform { * @param liftables trees of definitions that are lifted to fields of the state machine class * @param tree `ClassDef` tree of the state machine class * @param applyBody tree of onComplete handler (`apply` method) - * @param resumeBody RHS of definition tree of `resume` method * @return transformed `ClassDef` tree of the state machine class */ - def spliceMethodBodies(liftables: List[Tree], tree: ClassDef, applyBody: Tree, resumeBody: Tree): Tree = { + def spliceMethodBodies(liftables: List[Tree], tree: ClassDef, applyBody: Tree): Tree = { val liftedSyms = liftables.map(_.symbol).toSet val stateMachineClass = tree.symbol liftedSyms.foreach { @@ -211,12 +207,6 @@ trait AsyncTransform { (ctx: analyzer.Context) => val typedTree = fixup(dd, changeOwner(applyBody, callSiteTyper.context.owner, dd.symbol), ctx) typedTree - - case dd@DefDef(_, name.resume, _, _, _, _) if dd.symbol.owner == stateMachineClass => - (ctx: analyzer.Context) => - val changed = changeOwner(resumeBody, callSiteTyper.context.owner, dd.symbol) - val res = fixup(dd, changed, ctx) - res } result } |