Search code examples
scalareflectionscala-reflectscala-compiler

How to compile and run Scala code programmatically


I have the following code and I would like to compile it on the fly and run it.

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, world!")
  }
}

So far I have tried something like below:

import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox

object MainScala {
  def main(args: Array[String]): Unit = {
    val toolbox = currentMirror.mkToolBox()
    val source =
      """
        |object HelloWorld {
        |  def main(args: Array[String]): Unit = {
        |    println("Hello, world!")
        |  }
        |}
        |""".stripMargin
    val tree = toolbox.parse(source)
    val binary = toolbox.compile(tree)
    var c = binary.getClass
    val m = c.getMethod("main", classOf[Array[String]])
    val params = Array("Apple", "Banana", "Orange")
    m.invoke(null, null)
  }
}

After toolbox.compile(tree) I am not able to get the Class object of the compiled code.


Solution

  • If you look at the type of binary you'll see it's () => Any. So when you ask .getClass you actually get a subclass of Function1, which doesn't have .main.

    Toolbox isn't supposed to be used like that.

    https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/reflect/ToolBox.scala#L120-L129

    https://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#tree-creation-via-parse-on-toolboxes

    Try

    val source =
      """
        |object HelloWorld {
        |  def main(args: Array[String]): Unit = {
        |    println("Hello, world!")
        |  }
        |}
        |
        |HelloWorld.main(Array())
        |""".stripMargin
    val tree = toolbox.parse(source)
    val binary = toolbox.compile(tree)
    binary() // Hello, world!
    

    or

    val params = """Array("Apple", "Banana", "Orange")"""
    val source =
      s"""
        |object HelloWorld {
        |  def main(args: Array[String]): Unit = {
        |    println(args.toList)
        |  }
        |}
        |
        |HelloWorld.main($params)
        |""".stripMargin
    val tree = toolbox.parse(source)
    val binary = toolbox.compile(tree)
    binary() // List(Apple, Banana, Orange)
    

    or

    import scala.reflect.runtime.universe._
    val params = q"""Array("Apple", "Banana", "Orange")"""
    val tree =
      q"""
        object HelloWorld {
          def main(args: Array[String]): Unit = {
            println(args.toList)
          }
        }
    
        HelloWorld.main($params)
        """
    val binary = toolbox.compile(tree)
    binary() // List(Apple, Banana, Orange)
    

    or

    val params = """Array("Apple", "Banana", "Orange")"""
    val source =
      """
        |object HelloWorld {
        |  def main(args: Array[String]): Unit = {
        |    println(args.toList)
        |  }
        |}
        |""".stripMargin
    val tree = toolbox.parse(source)
    val symbol = toolbox.define(tree.asInstanceOf[ImplDef])
    val params1 = toolbox.parse(params)
    val tree1 = q"$symbol.main($params1)"
    val binary = toolbox.compile(tree1)
    binary() // List(Apple, Banana, Orange)