Java 如何在内存中编译kotlin文件并检查结果?

Java 如何在内存中编译kotlin文件并检查结果?,java,android,jvm,kotlin,kotlinc,Java,Android,Jvm,Kotlin,Kotlinc,到目前为止我有 import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler MyProjectCompiler.initialize("SampleKtFileOutput") .packageName("com.test.sample") .compile(File(someFile.path)) .result { ktSource: String -> K2JVMCompiler() .exec(Sy

到目前为止我有

import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler

  MyProjectCompiler.initialize("SampleKtFileOutput")
    .packageName("com.test.sample")
    .compile(File(someFile.path))
    .result { ktSource: String -> K2JVMCompiler()
       .exec(System.out, /** arguments here?*/) }
这将手动启动编译器,但我希望编译内存中第一个编译器(
MyProjectCompiler
,它生成kotlin源代码)的结果字符串,并检查结果,而不写入文件


如果可能的话,我想包括当前类路径上的所有内容。

阅读
K2JVMCompiler
类的源代码,编译器似乎只支持文件编译。深入挖掘,伪造
org.jetbrains.kotlin.codegen.KotlinCodegenFacade
静态方法
compileCorrectFiles
的条目似乎过于复杂


您最好使用文件系统来实现这一点。临时RAM磁盘可能适合您的需要。(这是内置的macOS)

我发现最简单的方法是使用类似于原始问题中的代码,然后使用
java.io.tmpdir
。下面是一个可重用的解决方案:

将kotlin编译器添加为测试依赖项:

testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler', version: "$kotlin_version"
编译器的包装器:

object JvmCompile {

  fun exe(input: File, output: File): Boolean = K2JVMCompiler().run {
    val args = K2JVMCompilerArguments().apply {
      freeArgs = listOf(input.absolutePath)
      loadBuiltInsFromDependencies = true
      destination = output.absolutePath
      classpath = System.getProperty("java.class.path")
          .split(System.getProperty("path.separator"))
          .filter {
            it.asFile().exists() && it.asFile().canRead()
          }.joinToString(":")
      noStdlib = true
      noReflect = true
      skipRuntimeVersionCheck = true
      reportPerf = true
    }
    output.deleteOnExit()
    execImpl(
        PrintingMessageCollector(
            System.out,
            MessageRenderer.WITHOUT_PATHS, true),
        Services.EMPTY,
        args)
  }.code == 0

}
用于从已编译类创建对象的类加载器:

class Initializer(private val root: File) {

  val loader = URLClassLoader(
      listOf(root.toURI().toURL()).toTypedArray(),
      this::class.java.classLoader)

  @Suppress("UNCHECKED_CAST") 
  inline fun <reified T> loadCompiledObject(clazzName: String): T? 
      = loader.loadClass(clazzName).kotlin.objectInstance as T

  @Suppress("UNCHECKED_CAST") 
  inline fun <reified T> createInstance(clazzName: String): T? 
      = loader.loadClass(clazzName).kotlin.createInstance() as T

}
将类作为接口实例加载

Initializer(compileOutputDir)
      .createInstance<Consumer<String>>("com.test.Example")
      ?.accept("Hello, world!")
初始值设定项(compileOutputDir)
.createInstance(“com.test.Example”)
?接受(“你好,世界!”)

输出将如预期的那样:
found:'Hello,world!'

有没有办法使用JSR223 API来实现这一点?我对itJSR 223不太熟悉,它只允许您在应用程序中添加脚本功能,可以更轻松地调整应用程序,但它不会帮助您使用RAM驱动器或在RAM中编译。这就是REPL的运行方式,不是吗?可以用它来定义我一个接一个编译的每个类型,然后用这种方式进行测试吗?我已经浏览了kotlin的源代码,但我真的不知道如何通过编程方式运行REPL。imho:REPL是命令行实用程序。另一方面,JSR233允许您在应用程序中运行预先编写的脚本,而无需REPL。我已经用它在应用程序中运行了Beanshell和Jython脚本,例如,这有助于创建用户定义的行为。不管怎样,我认为找出REPL可能比手动编译和类加载容易得多。我至少会用字符串进行测试,但这并不难,因为我在IR中仍然有类。虽然在任何地方都找不到关于它的任何信息,因此可能需要一些工作。我尝试了代码,但缺少三件事:
MockClasswriter
codegeoutputfile
compileOutputDir
assertTrue(JvmCompile.exe(codegenOutputFile, compileOutputDir))
Initializer(compileOutputDir)
      .createInstance<Consumer<String>>("com.test.Example")
      ?.accept("Hello, world!")