如何在Scala3中编译并在运行时执行scala代码?

2024-04-19

我想使用 Scala3 编译并执行在运行时以字符串形式给出的 Scala 代码。例如在 Scala 2 中我会使用 Reflection

import scala.reflect.runtime.universe as ru
import scala.tools.reflect.ToolBox
val scalaCode = q"""println("Hello world!")"""
val evalMirror = ru.runtimeMirror(this.getClass.getClassLoader)
val toolBox = evalMirror.mkToolBox()
toolBox.eval(scalaCode) //Hello world!

如果我尝试在 Scala3 中运行这段代码,我会得到

Scala 2 macro cannot be used in Dotty. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html
To turn this error into a warning, pass -Xignore-scala2-macros to the compiler

我如何在 Scala3 中翻译这段代码?


这个答案的 Scala 2 版本在这里:如何在脚本运行时运行生成的代码? https://stackoverflow.com/questions/73911801/how-can-i-run-generated-code-during-script-runtime

在 Scala 3 中:

  • 例如你可以使用Li Haoyi https://stackoverflow.com/users/871202/li-haoyi's Ammonite http://ammonite.io/
ammonite.Main(verboseOutput = false).runCode("""println("Hello, World!")""")
// Hello, World!

构建.sbt

scalaVersion := "3.1.3"
libraryDependencies += "com.lihaoyi" % "ammonite" % "2.5.4-22-4a9e6989" cross CrossVersion.full
excludeDependencies ++= Seq(
  ExclusionRule("com.lihaoyi", "sourcecode_2.13"),
  ExclusionRule("com.lihaoyi", "fansi_2.13"),
)
  • 或者你可以尝试尤金·横田 https://stackoverflow.com/users/3827/eugene-yokota's Eval https://eed3si9n.com/eval/
com.eed3si9n.eval.Eval()
  .evalInfer("""println("Hello, World!")""")
  .getValue(this.getClass.getClassLoader)
// Hello, World!

构建.sbt

scalaVersion := "3.2.0"
libraryDependencies += "com.eed3si9n.eval" % "eval" % "0.1.0" cross CrossVersion.full
  • 或者你可以试试我的执行 https://github.com/DmytroMitin/dotty-patched
com.github.dmytromitin.eval.Eval[Unit]("""println("Hello, World!")""")
// Hello, World!
scalaVersion := "3.2.1"
libraryDependencies += "com.github.dmytromitin" %% "eval" % "0.1"
  • 您也可以使用标准 Scala 3 REPL口译员 https://javadoc.io/static/org.scala-lang/scala3-compiler_3/3.2.0/dotty/tools/repl/ScriptEngine.html
dotty.tools.repl.ScriptEngine().eval("""println("Hello, World!")""")
// Hello, World!

构建.sbt

scalaVersion := "3.1.3"
libraryDependencies += scalaOrganization.value %% "scala3-compiler" % scalaVersion.value
  • 如果你有一个scala.quoted.Expr '{...}(抽象语法树上的静态类型包装器scala.quoted.Quotes#Tree)而不是纯字符串那么你可以使用运行时多阶段 https://docs.scala-lang.org/scala3/reference/metaprogramming/staging.html
import scala.quoted.*
given staging.Compiler = staging.Compiler.make(getClass.getClassLoader)
staging.run('{ println("Hello, World!") })
// Hello, World!

构建.sbt

scalaVersion := "3.1.3"
libraryDependencies += scalaOrganization.value %% "scala3-staging" % scalaVersion.value
  • 以上都是在Scala 3中运行Scala 3代码。如果我们想在Scala 3中运行Scala 2代码那么我们仍然可以使用Scala 2反射Toolbox https://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#tree-creation-via-parse-on-toolboxes。 Scala 2 宏不起作用,所以我们不能这样做runtime.currentMirror or q"..."但可以做universe.runtimeMirror or tb.parse
import scala.tools.reflect.ToolBox // implicit 

val tb = scala.reflect.runtime.universe
  .runtimeMirror(getClass.getClassLoader)
  .mkToolBox()
tb.eval(tb.parse("""println("Hello, World!")"""))
// Hello, World!

构建.sbt

scalaVersion := "3.1.3"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % "2.13.8"
  • 另外,要在 Scala 3 中运行 Scala 2 代码,您可以使用标准 Scala 2 REPL口译员 https://www.scala-lang.org/api/2.13.9/scala-compiler/scala/tools/nsc/interpreter/shell/Scripted.html
scala.tools.nsc.interpreter.shell.Scripted()
  .eval("""System.out.println("Hello, World!")""")
// Hello, World!

构建.sbt

scalaVersion := "3.1.3"
libraryDependencies ++= scalaOrganization.value % "scala-compiler" % "2.13.8"
  • 您也可以使用JSR223 https://jcp.org/aboutJava/communityprocess/final/jsr223/index.html 脚本编写 https://github.com/lampepfl/dotty/blob/master/tests/run-with-compiler/scripting.scala。取决于你是否有scala3-compiler or scala-compiler在类路径中,您将运行 Scala 3 或 Scala 2(上述两个脚本引擎之一:Scala 3dotty.tools.repl.ScriptEngine或斯卡拉2scala.tools.nsc.interpreter.shell.Scripted)。如果您首先添加了两个依赖项,则获胜。
new javax.script.ScriptEngineManager(getClass.getClassLoader)
  .getEngineByName("scala")
  .eval("""println("Hello, World!")""")
// Hello, World!

如果您想更好地控制使用什么依赖项(无需重新导入项目),您可以使用 Coursier 并指定类加载器

import coursier.* // libraryDependencies += "io.get-coursier" %% "coursier" % "2.1.0-M6-53-gb4f448130" cross CrossVersion.for3Use2_13
val files = Fetch()
  .addDependencies(
    Dependency(Module(Organization("org.scala-lang"), ModuleName("scala3-compiler_3")), "3.2.0"),
    // Dependency(Module(Organization("org.scala-lang"), ModuleName("scala-compiler")), "2.13.9")
  )
  .run()

val classLoader = new java.net.URLClassLoader(
  files.map(_.toURI.toURL).toArray,
  /*getClass.getClassLoader*/null // ignoring current classpath
)
new javax.script.ScriptEngineManager(classLoader)
  .getEngineByName("scala")
  .eval("""
    type T = [A] =>> [B] =>> (A, B) // Scala 3
    //type T = List[Option[A]] forSome {type A} // Scala 2
    System.out.println("Hello, World!")
  """)
// Hello, World!
  • 您可以使用实际的编译器自己在 Scala 3 中实现 Eval
import dotty.tools.io.AbstractFile
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.Driver
import dotty.tools.dotc.util.SourceFile
import dotty.tools.io.{VirtualDirectory, VirtualFile}
import java.net.URLClassLoader
import java.nio.charset.StandardCharsets
import dotty.tools.repl.AbstractFileClassLoader
import scala.io.Codec
import coursier.{Dependency, Module, Organization, ModuleName, Fetch}

  // we apply usejavacp=true instead
//  val files = Fetch()
//    .addDependencies(
//       Dependency(Module(Organization("org.scala-lang"), ModuleName("scala3-compiler_3")), "3.1.3"),
//    )
//    .run()
//
//  val depClassLoader = new URLClassLoader(
//    files.map(_.toURI.toURL).toArray,
//    /*getClass.getClassLoader*/ null // ignoring current classpath
//  )

val code =
  s"""
     |package mypackage
     |
     |object Main {
     |  def main(args: Array[String]): Unit = {
     |    println("Hello, World!")
     |  }
     |}""".stripMargin

val outputDirectory = VirtualDirectory("(memory)")
compileCode(code, List()/*files.map(f => AbstractFile.getFile(f.toURI.toURL.getPath)).toList*/, outputDirectory)
val classLoader = AbstractFileClassLoader(outputDirectory, this.getClass.getClassLoader/*depClassLoader*/)
runObjectMethod("mypackage.Main", classLoader, "main", Seq(classOf[Array[String]]), Array.empty[String])
// Hello, World!

def compileCode(
                 code: String,
                 classpathDirectories: List[AbstractFile],
                 outputDirectory: AbstractFile
               ): Unit = {
  class DriverImpl extends Driver {
    private val compileCtx0 = initCtx.fresh
    given Context = compileCtx0.fresh
      .setSetting(
        compileCtx0.settings.classpath,
        classpathDirectories.map(_.path).mkString(":")
      ).setSetting(
        compileCtx0.settings.usejavacp,
        true
      ).setSetting(
        compileCtx0.settings.outputDir,
        outputDirectory
      )
    val compiler = newCompiler
  }

  val driver = new DriverImpl
  import driver.given Context

  val sourceFile = SourceFile(VirtualFile("(inline)", code.getBytes(StandardCharsets.UTF_8)), Codec.UTF8)
  val run = driver.compiler.newRun
  run.compileSources(List(sourceFile))
  // val unit = run.units.head
  // println("untyped tree=" + unit.untpdTree)
  // println("typed tree=" + unit.tpdTree)
}

def runObjectMethod(
                     objectName: String,
                     classLoader: ClassLoader,
                     methodName: String,
                     paramClasses: Seq[Class[?]],
                     arguments: Any*
                   ): Any = {
  val clazz = Class.forName(s"$objectName$$", true, classLoader)
  val module = clazz.getField("MODULE$").get(null)
  val method = module.getClass.getMethod(methodName, paramClasses*)
  method.invoke(module, arguments*)
}

(以前的版本 https://gist.github.com/DmytroMitin/2e02055bc061e5b7c9421ba76e384013)

构建.sbt

scalaVersion := "3.1.3"
libraryDependencies += scalaOrganization.value %% "scala3-compiler" % scalaVersion.value

也可以看看:从 scala 3 宏中的类获取注释 https://stackoverflow.com/questions/71390113/get-annotations-from-class-in-scala-3-macros(在 Scala 3 中破解多阶段编程并实现我们自己的eval而不是 Scala 2context.eval or staging.runScala 3 宏中禁止)。

  • See also

Scala 演示编译器简介 https://www.chris-kipp.io/blog/an-intro-to-the-scala-presentation-compiler

在运行时将 scala 3 代码从字符串解析为 Scala 3 AST https://stackoverflow.com/questions/68194436/parsing-scala-3-code-from-a-string-into-scala-3-ast-at-runtime

Scala 3 反射 https://www.reddit.com/r/scala/comments/siatmj/scala_3_reflection/

在运行时帮助 dotty 编译器和类加载 https://github.com/lampepfl/dotty/discussions/16080

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在Scala3中编译并在运行时执行scala代码? 的相关文章

  • 如何使用 Java 和 Selenium WebDriver 在 C 目录中创建文件夹并需要将屏幕截图保存在该目录中?

    目前正在与硒网络驱动程序和代码Java 我有一种情况 我需要在 C 目录中创建一个文件夹 并在该文件夹中创建我通过 selenium Web 驱动程序代码拍摄的屏幕截图 它需要存储在带有时间戳的文件夹中 如果我每天按计划运行脚本 所有屏幕截
  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • JAXb、Hibernate 和 beans

    目前我正在开发一个使用 Spring Web 服务 hibernate 和 JAXb 的项目 1 我已经使用IDE hibernate代码生成 生成了hibernate bean 2 另外 我已经使用maven编译器生成了jaxb bean
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 我可以使用 HSQLDB 进行 junit 测试克隆 mySQL 数据库吗

    我正在开发一个 spring webflow 项目 我想我可以使用 HSQLDB 而不是 mysql 进行 junit 测试吗 如何将我的 mysql 数据库克隆到 HSQLDB 如果您使用 spring 3 1 或更高版本 您可以使用 s
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • 禁止的软件包名称:java

    我尝试从数据库名称为 jaane 用户名 Hello 和密码 hello 获取数据 错误 java lang SecurityException Prohibited package name java at java lang Class
  • 从 127.0.0.1 到 2130706433,然后再返回

    使用标准 Java 库 从 IPV4 地址的点分字符串表示形式获取的最快方法是什么 127 0 0 1 到等效的整数表示 2130706433 相应地 反转所述操作的最快方法是什么 从整数开始2130706433到字符串表示形式 127 0
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • 在 Scala 中,使用“_”和使用命名标识符有什么区别?

    为什么当我尝试使用时会出现错误 而不是使用命名标识符 scala gt res0 res25 List Int List 1 2 3 4 5 scala gt res0 map gt item toString
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • Java列表的线程安全

    我有一个列表 它将在线程安全上下文或非线程安全上下文中使用 究竟会是哪一个 无法提前确定 在这种特殊情况下 每当列表进入非线程安全上下文时 我都会使用它来包装它 Collections synchronizedList 但如果不进入非线程安
  • 静态变量的线程安全

    class ABC implements Runnable private static int a private static int b public void run 我有一个如上所述的 Java 类 我有这个类的多个线程 在里面r

随机推荐