Search code examples
classloaderkotlin

Kotlin class reloading with companion objects/functions


I'm experimenting with Kotlin class reloading, but I recently got stuck with something close to this:

package com.aurieh.reloading
fun doSomething(): String { // a function that does not belong to the class,
    // so it gets compiled as FileName$doSomething$...
}

class FileName {
    // do things with doSomething
}

And if I reload this class (with ImplClassLoader.defineClass and ByteArray), and try do invoke methods that call doSomething internally, I get an error similar to:

java.lang.IllegalAccessError: tried to access method com.aurieh.reloading.FileName.doSomething$default()Ljava/lang/String; from class com.aurieh.ares.reloading.FileName`

I would interpret this as if the reloaded class does not have doSomething attached.. So my question would be, how would I resolve this error? By somehow attaching doSomething to reloading classloader?

For reference, my class reloading code:

class Reloader : ClassLoader() {
    fun load(name: String, bytes: ByteArray, offset: Int, len: Int): Class<*> {
        return defineClass("com.aurieh.reloading.$name", bytes, offset, len)
    }
}

And for loading:

val bytes = File("../classes/path/to/class/FileName.class").readBytes()
Reloader().load("FileName", bytes, 0, bytes.size).newInstance()

Solution

  • Basically, top-level functions are not compiled into any of the classes defined in the file. Instead, a separate class is created for the top-level members: FileNameKt (if the file is named FileName.kt).

    So, to make your class load correctly (i.e. without any unsatisfied links), you have to load the FileNameKt class first:

    val bytes1 = File("../classes/path/to/class/FileNameKt.class").readBytes()
    val bytes2 = File("../classes/path/to/class/FileName.class").readBytes()
    val reloader = Reloader()
    reloader.load("FileNameKt", bytes1, 0, bytes1.size)
    reloader.load("FileName", bytes2, 0, bytes2.size).newInstance()