@@ -300,6 +300,27 @@ class DeterminismTest {
300
300
test(List (code))
301
301
}
302
302
303
+ @ Test def testAsync (): Unit = {
304
+ def code = List [SourceFile ](
305
+ source(" a.scala" ,
306
+ """
307
+ | object A {
308
+ | import scala.tools.nsc.OptionAwait.{optionally, value}
309
+ | def test = optionally {
310
+ | if (value(Some(true))) {
311
+ | var x = ""
312
+ | if (value(Some(false))) {
313
+ | value(Some(x)) + value(Some(2))
314
+ | }
315
+ | }
316
+ | }
317
+ | }
318
+ |
319
+ """ .stripMargin)
320
+ )
321
+ test(List (code))
322
+ }
323
+
303
324
def source (name : String , code : String ): SourceFile = new BatchSourceFile (name, code)
304
325
private def test (groups : List [List [SourceFile ]]): Unit = {
305
326
val referenceOutput = Files .createTempDirectory(" reference" )
@@ -309,6 +330,7 @@ class DeterminismTest {
309
330
g.settings.usejavacp.value = true
310
331
g.settings.classpath.value = output.toAbsolutePath.toString
311
332
g.settings.outputDirs.setSingleOutput(output.toString)
333
+ g.settings.async.value = true
312
334
val storeReporter = new StoreReporter
313
335
g.reporter = storeReporter
314
336
import g ._
@@ -362,3 +384,79 @@ class DeterminismTest {
362
384
def permutationsWithSubsets [A ](as : List [A ]): List [List [A ]] =
363
385
as.permutations.toList.flatMap(_.inits.filter(_.nonEmpty)).distinct
364
386
}
387
+
388
+
389
+
390
+ import scala .annotation .compileTimeOnly
391
+ import scala .language .experimental .macros
392
+ import scala .reflect .macros .blackbox
393
+
394
+ object OptionAwait {
395
+ def optionally [T ](body : T ): Option [T ] = macro impl
396
+ @ compileTimeOnly(" [async] `value` must be enclosed in `optionally`" )
397
+ def value [T ](option : Option [T ]): T = ???
398
+ def impl (c : blackbox.Context )(body : c.Tree ): c.Tree = {
399
+ import c .universe ._
400
+ val awaitSym = typeOf[OptionAwait .type ].decl(TermName (" value" ))
401
+ def mark (t : DefDef ): Tree = c.internal.markForAsyncTransform(c.internal.enclosingOwner, t, awaitSym, Map .empty)
402
+ val name = TypeName (" stateMachine$async" )
403
+ q """
404
+ final class $name extends _root_.scala.tools.nsc.OptionStateMachine {
405
+ ${mark(q """ override def apply(tr $$ async: _root_.scala.Option[_root_.scala.AnyRef]) = ${body}""" )}
406
+ }
407
+ new $name().start().asInstanceOf[ ${c.macroApplication.tpe}]
408
+ """
409
+ }
410
+ }
411
+
412
+ trait AsyncStateMachine [F , R ] {
413
+ /** Assign `i` to the state variable */
414
+ protected def state_= (i : Int ): Unit
415
+ /** Retrieve the current value of the state variable */
416
+ protected def state : Int
417
+ /** Complete the state machine with the given failure. */
418
+ protected def completeFailure (t : Throwable ): Unit
419
+ /** Complete the state machine with the given value. */
420
+ protected def completeSuccess (value : AnyRef ): Unit
421
+ /** Register the state machine as a completion callback of the given future. */
422
+ protected def onComplete (f : F ): Unit
423
+ /** Extract the result of the given future if it is complete, or `null` if it is incomplete. */
424
+ protected def getCompleted (f : F ): R
425
+ /**
426
+ * Extract the success value of the given future. If the state machine detects a failure it may
427
+ * complete the async block and return `this` as a sentinel value to indicate that the caller
428
+ * (the state machine dispatch loop) should immediately exit.
429
+ */
430
+ protected def tryGet (tr : R ): AnyRef
431
+ }
432
+
433
+
434
+ abstract class OptionStateMachine extends AsyncStateMachine [Option [AnyRef ], Option [AnyRef ]] {
435
+ var result$async : Option [AnyRef ] = _
436
+
437
+ // FSM translated method
438
+ def apply (tr$async : Option [AnyRef ]): Unit
439
+
440
+ // Required methods
441
+ private [this ] var state$async : Int = 0
442
+ protected def state : Int = state$async
443
+ protected def state_= (s : Int ): Unit = state$async = s
444
+ protected def completeFailure (t : Throwable ): Unit = throw t
445
+ protected def completeSuccess (value : AnyRef ): Unit = result$async = Some (value)
446
+ protected def onComplete (f : Option [AnyRef ]): Unit = ???
447
+ protected def getCompleted (f : Option [AnyRef ]): Option [AnyRef ] = {
448
+ f
449
+ }
450
+ protected def tryGet (tr : Option [AnyRef ]): AnyRef = tr match {
451
+ case Some (value) =>
452
+ value.asInstanceOf [AnyRef ]
453
+ case None =>
454
+ result$async = None
455
+ this // sentinel value to indicate the dispatch loop should exit.
456
+ }
457
+ def start (): Option [AnyRef ] = {
458
+ apply(None )
459
+ result$async
460
+ }
461
+ }
462
+
0 commit comments