【问题标题】:How to pass current ClassLoader to KotlinToJVMBytecodeCompiler for dynamic (runtime) compilation kotlin code programmatically?如何以编程方式将当前 ClassLoader 传递给 KotlinToJVMBytecodeCompiler 以进行动态(运行时)编译 kotlin 代码?
【发布时间】:2020-03-14 05:10:55
【问题描述】:

我为运行时编译 kotlin 代码创建了简单的实用程序:

package com.example

import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.com.intellij.openapi.Disposable
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
import java.io.File
import kotlin.script.experimental.jvm.util.KotlinJars

class KotlinDynamicCompiler {
    fun compileScript(moduleName: String,
                      sourcePath: String,
                      saveClassesDir: File
    ): GenerationState {
        val stubDisposable = StubDisposable();
        val configuration = CompilerConfiguration()
        configuration.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
        configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, PrintingMessageCollector(System.out, MessageRenderer.PLAIN_FULL_PATHS, true))
        configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, saveClassesDir)
        configuration.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
        configuration.addKotlinSourceRoot(sourcePath)
        configuration.addJvmClasspathRoots(listOf(KotlinJars.stdlib))
        val env = KotlinCoreEnvironment.createForProduction(stubDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
        return KotlinToJVMBytecodeCompiler.analyzeAndGenerate(env)!!;
    }

    inner class StubDisposable : Disposable {
        @Volatile
        var isDisposed: Boolean = false
            private set

        override fun dispose() {
            isDisposed = true
        }

    };

}

它适用于代码

package com.example.kt

class SimpleClass(val str:String){
    fun test(){
    }
}
class UsedSimpleClass(val simpleClass: SimpleClass, val file: java.io.File) {
}

但如果我想使用无基包类作为:

package com.example.kt

import com.example.pojo.TestPojo //class have in project that call runtime compilation 

class SimpleClass(val str:TestPojo){

}

或:

package com.example.kt

import com.fasterxml.jackson.databind.ObjectMapper //class have in project classpath where called runtime compilation 

class SimpleClass(val str:ObjectMapper){

}

如何将当前 ClassLoader 传递给 KotlinToJVMBytecodeCompiler 以编程方式进行动态(运行时)编译 kotlin 代码?


更多细节:

github 上的测试项目崩溃测试:https://github.com/nekkiy/dynamic-kotlin

原因: 我们需要使用代码生成并想测试生成的代码。但我不明白如何通过当前的课程环境。

感谢关注。

【问题讨论】:

    标签: kotlin dynamic-compilation


    【解决方案1】:

    解决方案:

    我使用了来自kotlin.script.experimental.jvm.util.jvmClasspathUtil.kt 的方法fun classpathFromClassloader(currentClassLoader: ClassLoader, unpackJarCollections: Boolean = false): List<File>?,它有效。

    结果动态编译器:

    package com.example
    
    import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
    import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots
    import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
    import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
    import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
    import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
    import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
    import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
    import org.jetbrains.kotlin.codegen.state.GenerationState
    import org.jetbrains.kotlin.com.intellij.openapi.Disposable
    import org.jetbrains.kotlin.config.CommonConfigurationKeys
    import org.jetbrains.kotlin.config.CompilerConfiguration
    import org.jetbrains.kotlin.config.JVMConfigurationKeys
    import org.jetbrains.kotlin.config.JvmTarget
    import java.io.ByteArrayOutputStream
    import java.io.File
    import java.io.PrintStream
    import kotlin.script.experimental.jvm.util.KotlinJars
    import kotlin.script.experimental.jvm.util.classpathFromClassloader
    
    class KotlinDynamicCompiler {
        fun compileModule(moduleName: String,
                          sourcePath: List<String>,
                          saveClassesDir: File,
                          classLoader: ClassLoader? = null,
                          forcedAddKotlinStd: Boolean = true
    
        ): GenerationState {
            val stubDisposable = StubDisposable();
            val configuration = CompilerConfiguration()
            configuration.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
            val baos = ByteArrayOutputStream()
            val ps: PrintStream = PrintStream(baos)
            configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, PrintingMessageCollector(ps, MessageRenderer.PLAIN_FULL_PATHS, true))
            configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, saveClassesDir)
    //        configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
            configuration.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
            val classPath = mutableSetOf<File>()
            if (classLoader != null) {
                classPath.addAll(classpathFromClassloader(classLoader)!!);
            }
            if (forcedAddKotlinStd) {
                classPath.add(KotlinJars.stdlib)
            }
            configuration.addJvmClasspathRoots(classPath.toList())
            configuration.addKotlinSourceRoots(sourcePath)
            val env = KotlinCoreEnvironment.createForProduction(stubDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
            val result = KotlinToJVMBytecodeCompiler.analyzeAndGenerate(env);
            ps.flush();
            if (result != null) {
                return result
            } else {
                throw IllegalStateException("Compilation error. Details:\n$baos")
            }
    
        }
    
        inner class StubDisposable : Disposable {
            @Volatile
            var isDisposed: Boolean = false
                private set
    
            override fun dispose() {
                isDisposed = true
            }
    
        };
    
    }
    

    注意:此功能包含在experimental包中。

    附:我还更新了github-project

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-06
      • 1970-01-01
      • 1970-01-01
      • 2012-05-23
      相关资源
      最近更新 更多