1

Via reflection, I need to distinguish between a method with a context and a method without a context (Kotlin 1.8.0).

Let's say we have following dummy context and a class with two functions - one with the context, second without.

  class Context(val a: Int)

  class Foo {
    context (Context)
    fun bar() = a
    
    fun baz() = 1
  }

In runtime, we don't know which method is which, so let's try to distinguish them via reflection.

  val foo = Foo()

  val barMethod = foo::class.memberFunctions.first { it.name == "bar" }
  println("barMethod has ${barMethod.parameters.size} parameter(s)")

  val bazMethod = foo::class.memberFunctions.first { it.name == "baz" }
  println("bazMethod has ${bazMethod.parameters.size} parameter(s)")

The problem is, both return they have 1 parameter (and they are identical in all other public properties). But obviously, when called, bar will fail if provided only one parameter. If called as expected, it works.

  // println(barMethod.call(foo))        -- fails
  println(bazMethod.call(foo))

  println(barMethod.call(foo, Context(6))

When debugging, I found, there actually is an internal property descriptor on barMethod (KCallableImpl) which contains contextReceiverParameters property correctly showing 1 for barMethod and 0 for bazMethod. But it's not accessible.

Any idea, if there's any supported way of retrieving information about context receivers via reflection, or it simply hasn't been implemented yet?

Erlik
  • 658
  • 8
  • 24

1 Answers1

0

First, define the annotation class:

@Target(AnnotationTarget.FUNCTION)
annotation class WithContext

Then, annotate the functions that have a context receiver with this annotation:

class Context(val a: Int)

class Foo {
    context (Context)
    
    @WithContext
    fun bar() = a
    
    fun baz() = 1
}

Now you can use the following code to check if a function has a context receiver:

val foo = Foo()

val barMethod = foo::class.memberFunctions.first { it.name == "bar" }
val hasBarContext = barMethod.annotations.any { it is WithContext }
println("barMethod has context: $hasBarContext")

val bazMethod = foo::class.memberFunctions.first { it.name == "baz" }
val hasBazContext = bazMethod.annotations.any { it is WithContext }
println("bazMethod has context: $hasBazContext")

This will output:

barMethod has context: true
bazMethod has context: false

Keep in mind that this approach relies on manually adding the @WithContext annotation to the functions with context receivers. There might be a more direct way to access the context receiver parameters in future Kotlin versions when the feature becomes stable and the reflection

Vijayanand Premnath
  • 3,415
  • 4
  • 25
  • 42