在运行时动态编译多个Scala类 [英] Dynamic compilation of multiple Scala classes at runtime

查看:154
本文介绍了在运行时动态编译多个Scala类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道我可以编译单个的代码段"在Scala中使用工具箱,如下所示:

I know I can compile individual "snippets" in Scala using the Toolbox like this:

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!")"""))
  }
}

除了片段"(即相互引用的类)以外,还有什么方法可以编译?像这样:

Is there any way I can compile more than just "snippets", i.e., classes that refer to each other? Like this:

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)
  }
}

很显然,我的代码段无效,因为我必须告诉工具箱如何解析"A".不知何故:

Obviously, my snippet doesn't work as I have to tell the toolbox how to resolve "A" somehow:

线程"main"中的异常;scala.tools.reflect.ToolBoxError:反射编译失败:

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

未找到:输入A

推荐答案

尝试

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

在比工具箱无法处理的情况复杂的情况下,您始终可以手动运行编译器

In cases more complex than those the toolbox can handle, you can always run the compiler manually

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)
}

在flink映射中动态解析json

Scala反射中的Tensorflow

如何评估该代码使用InterfaceStability批注(以涉及类InterfaceStability的非法循环引用失败")?

这篇关于在运行时动态编译多个Scala类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆