我为运行时编译 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

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

感谢关注。

请您参考如下方法:

解决方案:

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

结果动态编译器:

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 .


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!