Reflect.toolbox initialization issue


#1

I ran into a problem using reflect.toolbox. It seemed either eval() or untypecheck() had some side effects. See my code and error log at the end. I am not sure if this was due to my improper usage, or it was really a Scala issue.

  1. This happened to both 2.11.12 and 2.12.8 that I tried.

  2. If I move mkToolBox() inside c, the thing would work. (see commented lines inside the function.)

  3. If I exchanged the two calls to annotatedMethods() (get “requires” before “publishable”, see commented lines at the bottom of the FlowContext class), the thing worked as well.

Questions:

  1. How do I know when I need a brand new toolbox? It seemed that I do not need to re-initialize it every time I want to use it, but when I don’t, sometimes it could fail.
  2. It makes no sense to me why exchanging the two annotatedMethods() calls would make the error go away.

Thanks.

C.J.

========== Code ==========

import scala.reflect.runtime.{universe => ru}
import ru._
import scala.reflect.ClassTag

type StepMethod = (Seq[_]*) => Seq[_]

class Requires(val steps: StepMethod*) extends scala.annotation.StaticAnnotation
class Publishable(val str: String) extends scala.annotation.StaticAnnotation

trait Flow

class FlowContext[F <: Flow :TypeTag :ClassTag](val flow: F) {

    import scala.tools.reflect.ToolBox

    val fields = typeOf[F].decls.filter(_.isMethod)

    val mirror = runtimeMirror(getClass.getClassLoader)

    val tb = mirror.mkToolBox()

    private def annotatedMethods[A <: annotation.StaticAnnotation : TypeTag] = {

        // Move toolbox creating here would work
        //val tb = mirror.mkToolBox()

        def fromAnnotation(a: Annotation) = {
            tb.eval(tb.untypecheck(a.tree)).asInstanceOf[A]
        }

        fields
            .flatMap(f => {
                f.annotations
                    .find(_.tree.tpe =:= typeOf[A])
                    .map(a => {
                        println(s"field=${f.name.toString}")
                        println(a.tree.children.mkString("\n"))
                        val x = f.name.toString -> fromAnnotation(a)
                        println("done")
                        x
                    })
            })
            .toMap
    }

    val publishable = annotatedMethods[Publishable]
    val requires = annotatedMethods[Requires]

    // Exchange requires and publishable would work too
//    val requires = annotatedMethods[Requires]
//    val publishable = annotatedMethods[Publishable]

}

object FlowContextTest{

    object TestFlowF extends Flow{

        @Publishable("XXX")
        def x(args: Seq[_]*)= {
            println("inX")
           Seq(1,2,3,4)
        }

        @Requires(x)
        def y(args: Seq[_]*)= args.head.asInstanceOf[Seq[Int]].map(_ + 2)

        @Publishable("ZZZ")
        @Requires(y,x)
        def z(args: Seq[_]*)= {
            val zz = args.map(_.asInstanceOf[Seq[Int]])
            zz(0) zip zz(1)
        }

        def w()=123
    }

}

val x = new FlowContext(FlowContextTest.TestFlowF)

========= Error ==========
field=x
new Publishable
“XXX”
done
field=z
new Publishable
“ZZZ”
done
field=y
new Requires
{
((args: Seq[Seq[]]) => TestFlowF.this.x((args: *)))
}
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
type mismatch;
found : collection.Seq[Int]
required: (in ).scala.collection.Seq[(in ).scala.collection.Seq[
]] => (in ).scala.collection.Seq[
]
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.throwIfErrors(ToolBoxFactory.scala:331)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.wrapInPackageAndCompile(ToolBoxFactory.scala:213)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:267)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.$anonfun$compile$13(ToolBoxFactory.scala:444)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.apply(ToolBoxFactory.scala:370)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:437)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:459)
at FlowContext.fromAnnotation$1(:35)
at FlowContext.$anonfun$annotatedMethods$3(:45)
at scala.Option.map(Option.scala:163)
at FlowContext.$anonfun$annotatedMethods$1(:42)
at scala.collection.TraversableLike.$anonfun$flatMap$1(TraversableLike.scala:244)
at scala.reflect.internal.Scopes$Scope.foreach(Scopes.scala:415)
at scala.collection.TraversableLike.flatMap(TraversableLike.scala:244)
at scala.collection.TraversableLike.flatMap$(TraversableLike.scala:241)
at scala.reflect.internal.Scopes$Scope.flatMap(Scopes.scala:67)
at FlowContext.annotatedMethods(:39)
… 41 elided