1

I was trying to use Toolbox and quasiquote together to do code generation tasks, and faced with StackOverflowError while using AST of object returned from reify(x).tree, my code is as follows:

abstract class A[T] {def i: T}
class B(val i: Int) extends A[Int]
object A {

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

    val javaSeparator = "$"
    val curId = new java.util.concurrent.atomic.AtomicInteger()

    protected def freshName(prefix: String): TermName = {
        newTermName(s"$prefix$javaSeparator${curId.getAndIncrement}")
 }

 def main(args: Array[String]) {
    val b = new B(2)
    calculate(b)
 }

 def calculate(a: A[_]): Unit = {

    val toolBox = runtimeMirror(getClass.getClassLoader).mkToolBox()
    val i = freshName("i")
    val aTree = reify(a).tree

    val tree = q"""
       val $i = $aTree.i
      println($i)
   """

   toolBox.eval(tree)
  }
}

when I remove type parameter of class A or use def calculate(a: B), the toolBox.eval success and work as expected, i.e. print 2 in console.

I don't quite understand why this happens, can someone explain why class definition with type parameters fails the evaluation?

yjshen
  • 6,583
  • 3
  • 31
  • 40
  • Looks like a bug. Though first I'd like to ask what you wanted to achieve by doing `reify(a).tree`. What is the result that you're expecting? – Eugene Burmako Aug 31 '14 at 11:43
  • @EugeneBurmako, the code above was just to show the failure I faced with. In my real project, since A has many subclasses with different Array type, I want to manipulate them all in calculate, at the same time, avoid the boxing and unboxing overhead due to polynomial. reify(a).tree was just to use parameter in AST I can consider. Are there better ways to use argument directly in AST? Thank you. – yjshen Sep 01 '14 at 01:35
  • So, when you write `reify(a).tree`, you want to get `Ident(TermName("a"))`, which retains a link to the runtime value of the argument `a` passed to the current invocation of the method, right? – Eugene Burmako Sep 01 '14 at 08:57
  • @EugeneBurmako, yes, "link to the runtime value of the argument a" is what I want, you mean manual tree construction with Ident(TermName("a")) is just ok and a preferred method? Is it hygiene by doing so? – yjshen Sep 01 '14 at 09:11
  • @EugeneBurmako, I tried substitute `val aTree = reify(a).tree ` by `val aTree = Ident(newTermName("a"))`, but a "not found: value a" error – yjshen Sep 01 '14 at 09:18
  • @EugeneBurmako, by the way, is there a way to check the bytecode generated by compiler during `toolBox.eval`? Currently, I can only debug the `compile` method in `ToolBoxImpl`, using the className there and getBytes using ClassLoader. – yjshen Sep 01 '14 at 12:24
  • No, just Ident(TermName("a")) won't be enough, because manual tree construction isn't hygienic. Some magic sauce is needed, which is provided by reify :) – Eugene Burmako Sep 01 '14 at 21:05
  • 1
    To see the bytecodes, try this: https://github.com/xeno-by/scala/blob/2.11.x/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala#L195 – Eugene Burmako Sep 01 '14 at 21:06
  • Anyway, it looks like you're blocked by a bug, and I'll try to take a look at in the near future. – Eugene Burmako Sep 01 '14 at 21:07
  • Okay, found a workaround. Please check the answer to the question. – Eugene Burmako Sep 02 '14 at 07:07

1 Answers1

1

This is https://issues.scala-lang.org/browse/SI-8833. Until the bug is fixed, you can use a workaround outlined in the comments in JIRA. I've only provided a workaround for 2.11.x, and if you need one for 2.10.x, please leave a comment.

Eugene Burmako
  • 13,028
  • 1
  • 46
  • 59
  • I will, in 1-2 hours. Need to go now. – Eugene Burmako Sep 02 '14 at 07:46
  • Done. Updated the comment in JIRA. – Eugene Burmako Sep 02 '14 at 09:06
  • Thx for the updates. By the way, it seems .class files dumped by `Ydump-classes` does not include `__wrapper$xxxx` that are generated by toolbox compiler? I can find batch of `treecreator`s only – yjshen Sep 02 '14 at 09:27
  • It does work for me: https://gist.github.com/xeno-by/d632787c4626b1b7db23. What version of Scala are you using? – Eugene Burmako Sep 02 '14 at 09:44
  • I put `-Ydump-classes` option into build.sbt scalacOptions, no wonder I cannot find the generated classes' bytecode :( . I follow your gist above and it works, thank you very much. – yjshen Sep 02 '14 at 11:17