Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin dynamically compile a class from source code at runtime

Tags:

kotlin

Is it possible to compile and instantiate Kotlin class at runtime? I'm talking about something like that but using Kotlin API: How do I programmatically compile and instantiate a Java class?

As example: I'm getting full class definition as String:

val example = "package example\n" +
        "\n" +
        "fun main(args: Array<String>) {\n" +
        "    println(\"Hello World\")\n" +
        "}\n"

And then inserting it into some class.kt and running it so I'm getting "Hello World" printed in console at runtime.

like image 513
ohwelppp Avatar asked Mar 21 '18 20:03

ohwelppp


2 Answers

Please check this solution for dependencies, jar resources, etc. Code below isn't enough for successful execution.

However, to compile dynamic class you can do the following:

val classLoader = Thread.currentThread().contextClassLoader

val engineManager = ScriptEngineManager(classLoader)

setIdeaIoUseFallback() // hack to have ability to do this from IntelliJ Idea context

val ktsEngine: ScriptEngine = engineManager.getEngineByExtension("kts")

ktsEngine.eval("object MyClass { val number = 123 } ")

println(ktsEngine.eval("MyClass.number"))

Please note: there is code injection possible here. Please be careful and use dedicated process or dedicated ClassLoader for this.

like image 121
Manushin Igor Avatar answered Sep 21 '22 18:09

Manushin Igor


You might want to look at Kotlin Scripting, see https://github.com/andrewoma/kotlin-script

Alternatively, you'll need to write your own eval(kotlin-code-string-here) method which will dump the text inside blah.kt file for example, compile it using an external Kotlin compiler into blah.class then dynamically load those classes into the runtime using the Java Classloader doing something like this:

MainClass.class.classLoader.loadClass("com.mypackage.MyClass")

This might be very slow and unreliable.

Another no so great option is to make use of Rhino and run JavaScript inside your Kotlin code. So once again, you'll have an eval(kotlin-code-string-here) method which will dump the content to a blah.kt file, then you would use a Kotlin2JS compiler to compile it to JavaScript and directly execute the JavaScript inside Kotlin using Rhino which is not great either.

Another option is to make use of Kotlin Scripting or an external Kotlin compiler (in both cases, the Kotlin compiler will have to start up) and doing something like this will also allow you to execute dynamically, albeit, only on Unix systems.

Runtime.getRuntime().exec("""  "kotlin code here" > blah.kts | sh""")

I'm not aware of a clean solution for this, Kotlin was not designed to be run like like PHP / JavaScript / Python which just interprets text dynamically, it has to compile to bytecode first before it can do anything on the JVM; so in each scenario, you will need to compile that code first in one way or another, whether to bytecode or to javascript and in both cases load it into you application using the Java Classloader or Rhino.

like image 28
Jan Vladimir Mostert Avatar answered Sep 21 '22 18:09

Jan Vladimir Mostert