mardi 19 janvier 2021

How to define a class at runtime that includes a call to an method of an object without a companion class?

The following Scala (2.12.12) code runs with the expected behavior. The ClassDef tree is defined using the toolbox, and a ClassSymbol is returned.

class MyObj

object MyObj {
  def foo(x: Int): Int = x
}

object Test {

  import ru._

  def main(args: Array[String]): Unit = {

    val objSymbol = ru.typeOf[MyObj].typeSymbol.companion  // This is the most important line!

    val tree = ClassDef(
      Modifiers(),
      TypeName("Program"),
      List(),
      Template(
        List(Ident(TypeName("AnyRef"))),
        noSelfType,
        List(
          DefDef(
            Modifiers(),
            termNames.CONSTRUCTOR,
            List(),
            List(List()),
            TypeTree(),
            Block(
              List(
                Apply(Select(Super(This(typeNames.EMPTY), typeNames.EMPTY), termNames.CONSTRUCTOR), List())
              ),
              Literal(Constant(()))
            )
          ),
          DefDef(
            Modifiers(),
            TermName("fn"),
            List(),
            List(),
            TypeTree(),
            Apply(Select(Ident(objSymbol), TermName("foo")), List(Literal(Constant(10))))
          )
        )
      )
    )

    val clsSymbol = Reflection.toolbox.define(tree).asInstanceOf[ClassSymbol]
  }

}

However, the class MyObj is only serving the purpose of allowing us to get the symbol of the object via ru.typeOf[MyObj].typeSymbol.companion. I have some situations where an object does not have a companion class, and I would like to modify this code so that it works without the need for class MyObj.

Here is what I have tried so far:


Attempt 1.

val objSymbol = ru.typeOf[MyObj.type].typeSymbol

Produces the following error in the compiler. I don't know how to understand it, and can't find many mentions of this kind of error outside of the Scala language developer discussions.

Exception in thread "main" scala.tools.reflect.ToolBoxError: reflective compilation has failed:


  Unexpected tree in genLoad: erp12.scala_cbgp.lang.MyObj.type/class scala.reflect.internal.Trees$TypeTree at: NoPosition
     while compiling: <no file>
        during phase: jvm
     library version: version 2.12.12
    compiler version: version 2.12.12
  reconstructed args: 

  last tree to typer: TypeTree(class Int)
       tree position: <unknown>
            tree tpe: Int
              symbol: (final abstract) class Int in package scala
   symbol definition: final abstract class Int extends  (a ClassSymbol with SynchronizedClassSymbol)
      symbol package: scala
       symbol owners: class Int
           call site: constructor Program in class Program in package __wrapper$1$78b0f9d262f74777a2e9b5209fcd5413

Attempt 2.

currentMirror.staticModule(MyObj.getClass.getCanonicalName)

Also tried with variations:

  • runtimeMirror(getClass.getClassLoader) vs currentMirror
  • MyObj.getClass.getTypeName vs MyObj.getClass.getCanonicalName (same thing in this case)

This throw a ToolBoxError with value foo is not a member of object erp12.scala_cbgp.lang.MyObj$ which leads me to believe it is returning a different symbol from the one I need.


Attempt 3.

currentMirror.staticClass(MyObj.getClass.getCanonicalName)

Yields the same result as attempt 1.


Any guidance on what I am doing wrong would be much appreciated.





Aucun commentaire:

Enregistrer un commentaire