【问题标题】:Dynamic compilation of multiple Scala classes at runtime在运行时动态编译多个 Scala 类
【发布时间】:2019-11-17 06:14:14
【问题描述】:

我知道我可以使用这样的工具箱在 Scala 中编译单个“sn-ps”:

import scala.reflect.runtime.universe
import scala.tools.reflect.ToolBox

object Compiler {
  val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()

  def main(args: Array[String]): Unit = {
    tb.eval(tb.parse("""println("hello!")"""))
  }
}

除了“sn-ps”(即相互引用的类)之外,还有什么方法可以编译?像这样:

import scala.reflect.runtime.universe
import scala.tools.reflect.ToolBox

object Compiler {
  private val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()

  val a: String =
    """
      |package pkg {
      |
      |class A {
      |def compute(): Int = 42
      |}}
    """.stripMargin

  val b: String =
    """
      |import pkg._
      |
      |class B {
      |def fun(): Unit = {
      |    new A().compute()
      |}
      |}
    """.stripMargin

  def main(args: Array[String]): Unit = {
    val compiledA = tb.parse(a)
    val compiledB = tb.parse(b)
    tb.eval(compiledB)
  }
}

显然,我的 sn-p 不起作用,因为我必须告诉工具箱如何以某种方式解析“A”:

线程“主”scala.tools.reflect.ToolBoxError 中的异常:反射编译失败:

未找到:类型 A

【问题讨论】:

    标签: java scala reflection runtime scala-reflect


    【解决方案1】:

    试试

    import scala.reflect.runtime.universe._
    import scala.reflect.runtime.universe
    import scala.tools.reflect.ToolBox
    
    val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
    
    val a = q"""
              class A {
                def compute(): Int = 42
              }"""
    
    val symbA = tb.define(a)
    
    val b = q"""
              class B {
                def fun(): Unit = {
                  new $symbA().compute()
                }
              }"""
    
    tb.eval(b)
    

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


    如果情况比工具箱可以处理的复杂,您总是可以手动运行编译器

    import scala.reflect.internal.util.{AbstractFileClassLoader, BatchSourceFile}
    import scala.reflect.io.{AbstractFile, VirtualDirectory}
    import scala.tools.nsc.{Global, Settings}
    import scala.reflect.runtime
    import scala.reflect.runtime.universe
    import scala.reflect.runtime.universe._
    
    val a: String =
      """
        |package pkg {
        |
        |class A {
        |  def compute(): Int = 42
        |}}
    """.stripMargin
    
    val b: String =
      """
        |import pkg._
        |
        |class B {
        |  def fun(): Unit = {
        |    println(new A().compute())
        |  }
        |}
    """.stripMargin
    
    val directory = new VirtualDirectory("(memory)", None)
    compileCode(List(a, b), List(), directory)
    val runtimeMirror = createRuntimeMirror(directory, runtime.currentMirror)
    val bInstance = instantiateClass("B", runtimeMirror)
    runClassMethod("B", runtimeMirror, "fun", bInstance) // 42
    
    def compileCode(sources: List[String], classpathDirectories: List[AbstractFile], outputDirectory: AbstractFile): Unit = {
      val settings = new Settings
      classpathDirectories.foreach(dir => settings.classpath.prepend(dir.toString))
      settings.outputDirs.setSingleOutput(outputDirectory)
      settings.usejavacp.value = true
      val global = new Global(settings)
      val files = sources.zipWithIndex.map { case (code, i) => new BatchSourceFile(s"(inline-$i)", code) }
      (new global.Run).compileSources(files)
    }
    
    def instantiateClass(className: String, runtimeMirror: Mirror, arguments: Any*): Any = {
      val classSymbol = runtimeMirror.staticClass(className)
      val classType = classSymbol.typeSignature
      val constructorSymbol = classType.decl(termNames.CONSTRUCTOR).asMethod
      val classMirror = runtimeMirror.reflectClass(classSymbol)
      val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
      constructorMirror(arguments: _*)
    }
    
    def runClassMethod(className: String, runtimeMirror: Mirror, methodName: String, classInstance: Any, arguments: Any*): Any = {
      val classSymbol = runtimeMirror.staticClass(className)
      val classType = classSymbol.typeSignature
      val methodSymbol = classType.decl(TermName(methodName)).asMethod
      val instanceMirror = runtimeMirror.reflect(classInstance)
      val methodMirror = instanceMirror.reflectMethod(methodSymbol)
      methodMirror(arguments: _*)
    }
    
    //def runObjectMethod(objectName: String, runtimeMirror: Mirror, methodName: String, arguments: Any*): Any = {
    //  val objectSymbol = runtimeMirror.staticModule(objectName)
    //  val objectModuleMirror = runtimeMirror.reflectModule(objectSymbol)
    //  val objectInstance = objectModuleMirror.instance
    //  val objectType = objectSymbol.typeSignature
    //  val methodSymbol = objectType.decl(TermName(methodName)).asMethod
    //  val objectInstanceMirror = runtimeMirror.reflect(objectInstance)
    //  val methodMirror = objectInstanceMirror.reflectMethod(methodSymbol)
    //  methodMirror(arguments: _*)
    //}
    
    def createRuntimeMirror(directory: AbstractFile, parentMirror: Mirror): Mirror = {
      val classLoader = new AbstractFileClassLoader(directory, parentMirror.classLoader)
      universe.runtimeMirror(classLoader)
    }
    

    dynamically parse json in flink map

    Tensorflow in Scala reflection

    How to eval code that uses InterfaceStability annotation (that fails with "illegal cyclic reference involving class InterfaceStability")?

    【讨论】:

    • 这行得通,谢谢。但是如果我想在a 中有导入语句怎么办?这会导致类型不匹配,因为a 不再是Compiler.tb.u.ImplDef,而是成为常规reflect.runtime.universe.Tree
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多