Skip to content

Commit 4925970

Browse files
committed
adopted to 2.12
1 parent c7776f3 commit 4925970

File tree

11 files changed

+143
-42
lines changed

11 files changed

+143
-42
lines changed

build.sbt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@ name:="scala-gopher"
33

44
organization:="com.github.rssh"
55

6-
scalaVersion := "2.11.8"
6+
//scalaVersion := "2.12.0"
7+
scalaVersion := "2.12.1-SNAPSHOT"
78

89
resolvers += Resolver.sonatypeRepo("snapshots")
910

1011
resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
1112

12-
scalacOptions ++= Seq("-unchecked","-deprecation", "-feature" /* , "-Ymacro-debug-lite" , "-Ydebug" , "-Ylog:lambdalift" */ )
13+
scalacOptions ++= Seq("-unchecked","-deprecation", "-feature"
14+
/* , "-Ymacro-debug-lite" */
15+
/* , "-Ydebug" , "-Ylog:lambdalift" */
16+
)
1317

1418
libraryDependencies <+= scalaVersion( "org.scala-lang" % "scala-reflect" % _ )
1519

16-
libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.6-RC2"
20+
libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.7-SNAPSHOT-p1"
1721
//libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.6-SNAPSHOT"
1822

19-
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"
23+
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.0" % "test"
2024

21-
libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.4.8"
25+
libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.4.12"
2226

2327
//TODO: enable after 1.0
2428
//libraryDependencies += "com.typesafe.akka" %% "akka-stream-experimental" % "0.9"

notes/techreport.pdf

0 Bytes
Binary file not shown.

notes/techreport.tex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
}
88
\date{\today}
99

10+
% arxiv url: https://arxiv.org/abs/1611.00602
1011

1112

1213
\documentclass[12pt]{article}

src/main/scala/gopher/channels/ActorBackedChannel.scala

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import akka.actor._
55
import akka.pattern._
66
import scala.concurrent._
77
import scala.concurrent.duration._
8-
import gopher._
8+
import scala.util._
99
import scala.language.experimental.macros
1010
import scala.language.postfixOps
1111
import scala.reflect.macros.blackbox.Context
1212
import scala.reflect.api._
13+
import gopher._
1314

1415
class ActorBackedChannel[A](futureChannelRef: Future[ActorRef], override val api: GopherAPI) extends Channel[A]
1516
{
@@ -26,18 +27,21 @@ class ActorBackedChannel[A](futureChannelRef: Future[ActorRef], override val api
2627
}
2728
}
2829
}
30+
implicit val ec = api.executionContext
2931
if (closed) {
3032
if (closedEmpty) {
3133
applyClosed();
3234
} else {
3335
// TODO: ask timeput on closed channel set in config.
3436
futureChannelRef.foreach{ ref => val f = ref.ask(ClosedChannelRead(cont))(5 seconds)
35-
f.onFailure{
36-
case e: AskTimeoutException => applyClosed()
37-
}
38-
f.onSuccess{
39-
case ChannelCloseProcessed(0) =>
40-
closedEmpty = true
37+
f.onComplete{
38+
case Failure(e) =>
39+
if (e.isInstanceOf[AskTimeoutException]) {
40+
applyClosed()
41+
}
42+
case Success(ChannelCloseProcessed(0)) =>
43+
closedEmpty = true
44+
case _ => // do nothing
4145
}
4246
}
4347
}
@@ -47,33 +51,33 @@ class ActorBackedChannel[A](futureChannelRef: Future[ActorRef], override val api
4751
}
4852

4953
private def contRead[B](x:ContRead[A,B]): Unit =
50-
futureChannelRef.foreach( _ ! x )
54+
futureChannelRef.foreach( _ ! x )(api.executionContext)
5155

5256
def cbwrite[B](f: ContWrite[A,B] => Option[(A,Future[Continuated[B]])], flwt: FlowTermination[B] ): Unit =
5357
if (closed) {
5458
flwt.doThrow(new ChannelClosedException())
5559
} else {
56-
futureChannelRef.foreach( _ ! ContWrite(f,this, flwt) )
60+
futureChannelRef.foreach( _ ! ContWrite(f,this, flwt) )(api.executionContext)
5761
}
5862

5963
private def contWrite[B](x:ContWrite[A,B]): Unit =
60-
futureChannelRef.foreach( _ ! x )
64+
futureChannelRef.foreach( _ ! x )(api.executionContext)
6165

62-
private[this] implicit val ec = api.executionContext
66+
//private[this] implicit val ec = api.executionContext
6367

6468
def isClosed: Boolean = closed
6569

6670
def close(): Unit =
6771
{
68-
futureChannelRef.foreach( _ ! ChannelClose )
72+
futureChannelRef.foreach( _ ! ChannelClose )(api.executionContext)
6973
closed=true
7074
}
7175

7276

7377
override protected def finalize(): Unit =
7478
{
7579
// allow channel actor be grabage collected
76-
futureChannelRef.foreach( _ ! ChannelRefDecrement )
80+
futureChannelRef.foreach( _ ! ChannelRefDecrement )(api.executionContext)
7781
}
7882

7983
private var closed = false

src/main/scala/gopher/channels/FoldSelectorBuilder.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,9 +540,13 @@ class FoldSelectorBuilderImpl(override val c:Context) extends SelectorBuilderImp
540540
c.abort(tp.pos, "match pattern in select without guard must be in form x:channel.write or x:channel.read");
541541
} else {
542542
parseGuardInSelectorCaseDef(termName, caseDef.guard) match {
543-
case q"scala.async.Async.await[${t}](${readed}.aread):${t1}" =>
543+
case q"scala.async.Async.await[${t}](${readed}.aread):${t1}" =>
544544
onRead(readed)
545-
case q"scala.async.Async.await[${t}](${ch}.awrite($expression)):${t1}" =>
545+
case q"gopher.goasync.AsyncWrapper.await[${t}](${readed}.aread):${t1}" =>
546+
onRead(readed)
547+
case q"scala.async.Async.await[${t}](${ch}.awrite($expression)):${t1}" =>
548+
onWrite(ch)
549+
case q"gopher.goasync.AsyncWrapper.await[${t}](${ch}.awrite($expression)):${t1}" =>
546550
onWrite(ch)
547551
case x@_ =>
548552
c.abort(tp.pos, "can't parse match guard: "+x);

src/main/scala/gopher/channels/SelectorBuilder.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,10 @@ class SelectorBuilderImpl(val c: Context) extends ASTUtilImpl
101101
c.abort(f.tree.pos, "match expected in gopher select loop, have: ${MacroUtil.shortString(f.tree)}");
102102
}
103103
}
104-
c.Expr[T](c.untypecheck(q"scala.async.Async.await(${builder}.go)"))
104+
System.err.println(s"builder=${builder}")
105+
//val tc = c.typecheck(builder)
106+
//System.err.println(s"tc=${tc}")
107+
c.Expr[T](MacroUtil.cleanUntypecheck(c)(q"gopher.goasync.AsyncWrapper.await(${builder}.go)"))
105108
}
106109

107110
def foreachBuildMatch(cases:List[c.universe.CaseDef]):c.Tree =
@@ -330,7 +333,7 @@ class SelectorBuilderImpl(val c: Context) extends ASTUtilImpl
330333
c.abort(f.tree.pos, "match expected in gopher select map, have: ${MacroUtil.shortString(f.tree)}");
331334

332335
}
333-
c.Expr[Input[T]](c.untypecheck(q"${builder}.started"))
336+
c.Expr[Input[T]](MacroUtil.cleanUntypecheck(c)(q"${builder}.started"))
334337
}
335338

336339
def builder[T](f:c.Expr[PartialFunction[Any,T]]):c.Tree =
@@ -364,7 +367,7 @@ class SelectorBuilderImpl(val c: Context) extends ASTUtilImpl
364367
mapBuildMatch[T](cases)
365368
case _ => c.abort(f.tree.pos,"expected partial function with syntax case ... =>, have ${MacroUtil.shortString(f.tree)}");
366369
}
367-
c.Expr[Input[T]](c.untypecheck(q"${builder}.started"))
370+
c.Expr[Input[T]](MacroUtil.cleanUntypecheck(c)(q"${builder}.started"))
368371
}
369372

370373
}
@@ -455,16 +458,16 @@ object SelectorBuilder
455458
val ftParam = ValDef(Modifiers(Flag.PARAM),ft,tq"gopher.FlowTermination[${weakTypeOf[T]}]",EmptyTree)
456459
val ecParam = ValDef(Modifiers(Flag.PARAM),ec,tq"scala.concurrent.ExecutionContext",EmptyTree)
457460
val nvaldefs = ecParam::ftParam::valdefs
458-
val asyncBody = GoAsync.transformAsyncBody[T](c)(body)
461+
val asyncBody = GoAsync.transformAsyncBody[T](c)(body)
459462
val nbody = q"""{
460463
implicit val ${ft1} = ${ft}
461464
implicit val ${ec1} = ${ec}
462-
scala.async.Async.async(${transformDelayedMacroses[T](c)(asyncBody)})(${ec})
465+
gopher.goasync.AsyncWrapper.async(${transformDelayedMacroses[T](c)(asyncBody)})(${ec})
463466
}
464467
"""
465468
val newTree = lastFun(nvaldefs,nbody)
466469
// untypecheck is necessory: otherwise exception in async internals
467-
c.Expr[S](c.untypecheck(newTree))
470+
c.Expr[S](MacroUtil.cleanUntypecheck(c)(newTree))
468471
}
469472

470473
def idleImpl[T:c.WeakTypeTag,S](c:Context)(body:c.Expr[T]):c.Expr[S] =

src/main/scala/gopher/goasync/GoAsync.scala

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -198,27 +198,42 @@ object GoAsync
198198
{
199199
import c.universe._
200200
val btype = body.tpe
201-
// untypechack is necessory, because async-transform later will corrupt
202-
// symbols owners inside body
203-
// [scala-2.11.8]
204-
val nb = c.untypecheck(body)
201+
val nb = body
205202
val anb = atPos(body.pos){
206203
val nnb = transformAsyncBody(c)(nb)
207204
val ec = c.inferImplicitValue(c.weakTypeOf[ExecutionContext])
208-
q"(${param})=>scala.async.Async.async[$btype](${nnb})($ec)"
205+
// untypechack is necessory, because async-transform corrupt
206+
// symbols owners inside body and we change scope of param
207+
// [scala-2.11.8]
208+
MacroUtil.cleanUntypecheck(c)(
209+
q"(${param})=>scala.async.Async.async[$btype](${nnb})($ec)"
210+
)
209211
}
210212
val ar = atPos(fun.pos) {
211213
val uar = if (implicitParams.isEmpty) {
212214
q"gopher.asyncApply1(${fun})(${anb})"
213215
} else {
214216
q"gopher.goasync.AsyncApply.apply1i(${fun})(${anb},${implicitParams})"
215217
}
216-
// typecheck is necessory
217-
// 1. to prevent runnint analysis of async over internal awaits in anb as on
218-
// enclosing async instead those applied from asyncApply
219-
// 2. to expand macroses here, to prevent error during expanding macroses
220-
// in next typecheck
221-
c.typecheck(uar)
218+
if (true) {
219+
// typecheck is necessory
220+
// 1. to prevent runnint analysis of async over internal awaits in anb as on
221+
// enclosing async instead those applied from asyncApply
222+
// 2. to expand macroses here, to prevent error during expanding macroses
223+
// in next typecheck
224+
try {
225+
c.typecheck(uar)
226+
}catch{
227+
case ex: Exception =>
228+
System.err.println(s"failed to typecheck uar: ${ex}")
229+
System.err.println(s"uar=${uar}")
230+
System.err.println(s"nb=${nb}")
231+
//System.err.println(s"raw=${showRaw(uar)}")
232+
throw ex
233+
}
234+
} else {
235+
uar
236+
}
222237
}
223238
//typecheck with macros disabled is needed for compiler,
224239
//to set symbol 'await', because async macro discovered

src/main/scala/gopher/util/MacroUtil.scala

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package gopher.util
22

3+
import scala.annotation._
34
import scala.reflect.macros.blackbox.Context
45
import scala.reflect.api._
56
import scala.language.reflectiveCalls
@@ -53,6 +54,63 @@ object MacroUtil
5354
findAwait.found
5455
}
5556

57+
/**
58+
* bug in async/scala-2.12.x
59+
* async/types generate in state-machine next chunk of code:
60+
*```
61+
* val result: scala.concurrent.Promise[Int] = Promise.apply[Int]();
62+
* <stable> <accessor> def result: scala.concurrent.Promise[Int] = stateMachine$macro$1041.this.result;
63+
* val execContext: scala.concurrent.ExecutionContext = ..
64+
* <stable> <accessor> def execContext: scala.concurrent.Promise[Int] = stateMachine$macro$1041.this.execContext;
65+
*```
66+
* when we attempt untype/type code again, it is not compiled.
67+
*So, we need to remove result and execContext DefDefs
68+
**/
69+
def removeAsyncStateMachineResultDefDef(c:Context)(tree: c.Tree):c.Tree =
70+
{
71+
import c.universe._
72+
73+
val outsideStm = new Transformer {
74+
75+
override def transform(tree:Tree):Tree =
76+
tree match {
77+
case ClassDef(mods,name,tparams,impl)
78+
if (name.toString.startsWith("stateMachine$")) =>
79+
impl match {
80+
case Template(parents,self,body) =>
81+
ClassDef(mods,name,tparams,
82+
Template(parents,self,removeResultDefDef(body,Nil)))
83+
//case _ => // impossible, throw
84+
}
85+
case _ => super.transform(tree)
86+
}
87+
88+
@tailrec
89+
def removeResultDefDef(body:List[Tree],acc:List[Tree]):List[Tree] =
90+
{
91+
body match {
92+
case Nil => acc.reverse
93+
case head::tail =>
94+
val (rest,nacc) = head match {
95+
case DefDef(mods,name,tparams,vparamss,tpt,rsh)
96+
if (name.toString == "result" ||
97+
name.toString == "execContext" ) => (tail,acc)
98+
case _ => (tail, transform(head)::acc)
99+
}
100+
removeResultDefDef(rest,nacc)
101+
}
102+
}
103+
104+
}
105+
106+
val retval = outsideStm.transform(tree)
107+
retval
108+
}
109+
110+
def cleanUntypecheck(c:Context)(tree:c.Tree):c.Tree =
111+
{
112+
removeAsyncStateMachineResultDefDef(c)(c.untypecheck(tree))
113+
}
56114

57115

58116
final val SHORT_LEN = 80

src/main/scala/gopher/util/ReflectUtil.scala

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,21 @@ object ReflectUtil
99

1010
def retrieveValSymbols[T:u.TypeTag](u:Universe)(ownerType:u.Type): List[u.TermSymbol] =
1111
{
12-
ownerType.members.filter(_.isTerm).map(_.asTerm).filter( x =>
13-
x.isVal && x.typeSignature <:< u.typeOf[T]
14-
).toList
12+
val r1 = ownerType.members.filter(_.isTerm).map(_.asTerm).filter(x => x.isVal)
13+
val signatures = r1.map(_.typeSignature)
14+
val ut = u.weakTypeOf[T]
15+
val checkResults = signatures.map( _ <:< u.typeOf[T])
16+
val retval = ownerType.members.filter(_.isTerm).map(_.asTerm).filter{ x =>
17+
if (x.isVal) {
18+
// in scala 2.12 getter method type, scala 2.11 - type
19+
val r = x.typeSignature match {
20+
case u.NullaryMethodType(rt) => rt <:< u.typeOf[T] // for scala-2.12
21+
case _ => (x.typeSignature <:< u.typeOf[T]) // for scala-2.11
22+
}
23+
r
24+
} else false
25+
}.toList
26+
retval
1527
}
1628

1729

src/test/scala/example/SieveSuite.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ object Sieve
7777
class SieveSuite extends FunSuite
7878
{
7979

80-
test("last prime before 1000", Now) {
80+
test("last prime before 1000") {
8181

8282
val quit = Promise[Boolean]()
8383
val quitInput = futureInput(quit.future)

0 commit comments

Comments
 (0)