Android Studio 4.2.2 - Why debbuger doesn't evaluate parameter in a inner funcion?

Viewed 274

Android Studio 4.2.2 evaluates a local and global variable, but doesn't evaluate parameter funcion when is inside a inner function.

Until the previous version this worked perfectly.

fun a(p:param) {
fun b(){
var v = p+1 // Here
}
}

Suppose that one try to evaluate the parameter p in the line with comment // Here with Alt F8

The message in evaluate window is

Cannot find the local variable 'p' with type

This hurts a lot because it forces you to replicate the parameter as a local variable in each routine to be visible in the debugger.

var p = p

Has anyone noticed this? Is there any workaround?

Notice that Variables windows display a parameter with $ prefix, but it also doesn't work in evaluate window.

I've posted this issue in JetBrains.

enter image description here

1 Answers

First things first:

Is there any workaround?

Yes, bind the parameter p to a local variable inside b:

fun a(p: Param) {
  fun b() {
    val p = p
    var v = p + 1
  }
}

and it will all work as expected.


The root cause is slightly more convoluted.

Kotlin grew up on a language tool chain tied very closely to the IntelliJ language plug-in for Kotlin. The original JVM compiler was a code generator that consumed the data structures of semantic information used by the IntelliJ language plug-in for diagnostics, quick fixes, and so on.

This architecture was very good for providing language support to an IDE, but less so for building a fast batch compiler.

With Kotlin 1.5, the "IR backend" was enabled as the default code generator for JVM. It uses a more traditional compilation approach of gradually translating an abstract syntax tree (AST) to progressively simpler intermediate languages before outputting JVM byte code.

With this change, a number of new compilation strategies were implemented. In particular, in the old strategy, local functions were treated as lambdas bound to a local variable. Their free variables were recorded in the state of the allocated Function object at the declaration site. When the local function is called, it translates to a call to invoke on that function object. End of story.

In the new approach, local functions are lifted into private static functions on the same class as the outer function. Free variables in the local function are closed by parameters to that lifted function, and instead of recording them at the declaration site in a lambda object, they are passed at the call site as arguments.

In your example. the p is free in the inner function b, so an additional parameter $p is added to b.

While this works for "release builds", the surrounding tooling has not caught up until recently. The "Evaluate Expression..." mechanism has been hit particularly hard, as it's quite sensitive to the layout and shape of the resulting JVM byte code.

In this specific case, it was a matter of adjusting the mechanism in the debugger that maps free variables of the fragment to local variables at the breakpoint. With this change aimed at 2022.3, you should hopefully stop noticing this specific bug, and a host of other improvements as a new and revised version of the evaluation mechanism ships.

Related