Support for Dynamic KLibs?

It would be really useful to have a way to dynamically load KLibs. An example use could be a plugin system:

// In plugin
object SquarePlugin : Plugin {
    override val name: String get() = "Square Plugin"
    override fun operate(x: Double): Double = x * x
}

// In main project
interface Plugin {
    val name: String
    fun operate(x: Double): Double
}

fun loadPlugin(path: String): Plugin {
    val klib = loadKLib(path)
    // Somehow resolve `SquarePlugin`
    return plugin
}

Using dlopen, it is possible to use the plugin library as you would when calling it from C. However, this requires scanning the header file to find which function is where, and it is impossible to convert the returned result from a CPointer<*> to a Plugin. Is it possible to do this, and if not is support planned?

5 Likes

Unfortunately, loading using dlopen appears to create two seperate “kotlin instances”.

// In plugin project, exported to "libplugin.so"
fun pluginRun() {
    Foo
}

// In main project
object Foo {
    init {
        println("Foo init")
    }
}

fun main() {
    Foo

    val handle = dlopen("./libproject.so", RTLD_LAZY)!!
    val getSymbolsFunction = dlsym(handle, "lib${name}_symbols")!! as CPointer<CFunction<() -> CPointer<CPointerVarOf<*>>>>
    val symbolsPtr = getSymbolsFunction()
    val pluginFunction = symbolsPtr[12].value!! // number depends on header file generated
    (pluginsFunction as CPointer<CFunction<() -> Unit>>)()
}

This produces the output of Foo init twice, which implies that the plugin and the main project do not share data.

This means in order to produce a plugin system, either a .klib must be loaded dynamically, a protocol must be established (either via socket or C functions), or the main project must statically “load” all plugins on compilation.

3 Likes