Dynamically invoke Function<R>

I’m building a utility to translate lambdas into callable references inside a different language context. Essentially, making Kotlin lambdas callable from a V8 runtime living in the JVM. I have a method that takes a Function and I would like to invoke it. From what I’m understanding, it looks like I have to do something like this in order to support any lambda:

fun <R> Function<R>.call(vararg args: Any?): R = when (args.size) {
    0 -> (this as Function0<R>).invoke()
    1 -> (this as Function1<Any?, R>).invoke(args[0])
    2 -> (this as Function2<Any?, Any?, R>).invoke(args[0], args[1])
    3 -> (this as Function3<Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2])
    4 -> (this as Function4<Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3])
    5 -> (this as Function5<Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4])
    6 -> (this as Function6<Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5])
    7 -> (this as Function7<Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
    8 -> (this as Function8<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])
    9 -> (this as Function9<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])
    10 -> (this as Function10<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9])
    11 -> (this as Function11<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10])
    12 -> (this as Function12<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11])
    13 -> (this as Function13<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12])
    14 -> (this as Function14<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13])
    15 -> (this as Function15<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14])
    16 -> (this as Function16<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15])
    17 -> (this as Function17<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16])
    18 -> (this as Function18<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17])
    19 -> (this as Function19<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18])
    20 -> (this as Function20<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19])
    21 -> (this as Function21<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19], args[20])
    22 -> (this as Function22<Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, Any?, R>).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19], args[20], args[21])
    else -> (this as FunctionN<R>).invoke(*args)
}

Originally, I found these specifications regarding invoking functions and it looked super promising, but it seems those docs are outdated and no longer represent the current state. I tried looking for the equivalent in the current docs, but to no avail.

Additionally, I was also considering checking arity against the length of the arguments, but arity is contained in the FunctionBase interface and since that is in kotlin.jvm.internal, it seemed inappropriate to use.

I would appreciate any confirmation on my approach or suggestions on how to proceed otherwise. Thanks!

There is a function in the stdlib that does exactly this:

1 Like

Thanks for the quick reply! I am aware of that method, because I’m using it for member functions on a class. I wasn’t sure how to get a KCallable reference from a Lambda type.

Trying to use reflection on the Lambda types result in the following error:

This class is an internal synthetic class generated by the Kotlin compiler, such as an anonymous class for a lambda, a SAM wrapper, a callable reference, etc. It’s not a Kotlin class or interface, so the reflection library has no idea what declarations does it have. Please use Java reflection to inspect this class: class com.example$testExample$1$function$1

EDIT:

Just found this:

value.reflect().call("arg1", "arg2", ...)

But there is still an error:

Introspecting local functions, lambdas, anonymous functions and local variables is not yet fully supported in Kotlin reflection

EDIT 2:

Which makes sense given the docs I just found on it.

You can also use JVM reflection to call methods.

Gotcha. Just for clarities sake, this seems to work fine:

lambdaFunction::class.java.methods.find { it.name == "invoke" }.invoke(value, "arg1", "arg2", ...)

Though, I would love to just handle this as a KCallable, but it seems that I’ll have to wait for that. I appreciate the help and direction here. Thanks!

1 Like

What should you pass as the value? I am trying to do the exact same thing, however my lambda function is not defined within any class.

While debugging I can see that the declaring class required for my Lambda function invocation is of type KFunctionImpl, but it is not clear where this class is coming from and how to pass it into the invocation