Files
kotlin-fork/docs/backend/jvm/inline-scopes.md
T
2024-02-23 23:59:13 +01:00

97 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Introduction
The Kotlin debugger uses inline marker variables (`$i$f$...`, `$i$a$...`) that are produced by the Kotlin compiler
to handle stepping into/over/out of inline functions and lambdas and also to build inline stack traces. This scheme
does not work for dex code, where the same variable may be duplicated in different registers and where basic blocks can be reordered.
This leads to many debugging issues on Android from inconsistent locals in the variables view to bad stepping in inline functions.
## **New format description**
The new format affects 3 types of variables:
1. Inline function marker variables
`$i$f$name\[scope number]\[call site line number]`
2. Inline lambda marker variables
`$i$a$name\[scope number]\[call site line number]\[surrounding scope number]`
3. Local variables from inline functions or lambdas
`name\[scope number]`
Lets break it down on the following example:
```
fun main() {
val inMain = 0
g(0) {
val inLambdaG1 = 1 // breakpoint 1
h()
g(4) {
val inLambdaG2 = 2 // breakpoint 2
}
}
}
inline fun g(gParam: Int, block: () -> Unit) {
block()
}
inline fun h() {
val inH = 3
i()
}
inline fun i() {
val inI = 4
}
```
How LVT used to look:
```
LocalVariableTable:
Start Length Slot Name Signature
20 4 7 $i$f$i I
23 1 8 inI$iv$iv I
14 11 5 $i$f$h I
17 8 6 inH$iv I
34 4 7 $i$a$-g-SandboxKt$main$1$1 I
37 1 8 inLambdaG2 I
31 9 6 $i$f$g I
28 12 5 gParam$iv I
8 33 3 $i$a$-g-SandboxKt$main$1 I
11 30 4 inLambdaG1 I
6 37 2 $i$f$g I
4 39 1 gParam$iv I
2 42 0 inMain I
```
How LVT looks with the new format:
```
LocalVariableTable:
Start Length Slot Name Signature
20 4 7 $i$f$i\4\30 I
23 1 8 inI\4 I
14 11 5 $i$f$h\3\7 I
17 8 6 inH\3 I
34 4 7 $i$a$-g-SandboxKt$main$1$1\6\36\2 I
37 1 8 inLambdaG2\6 I
31 9 6 $i$f$g\5\8 I
28 12 5 gParam\5 I
8 33 3 $i$a$-g-SandboxKt$main$1\2\28\0 I
11 30 4 inLambdaG1\2 I
6 37 2 $i$f$g\1\5 I
4 39 1 gParam\1 I
2 42 0 inMain I
```
Previously the compiler added the `$iv` suffixes to distinguish locals that belong to different inline functions.
Now we assign scope numbers to marker and local variables. Locals with a scope number belong to the function
which is represented by a marker variable with the same scope number.
Lets see how it works in our example:
* `$i$f$g\1\5` has scope number 1, as well as `gParam\1`.
* `$i$a$-g-SandboxKt$main$1\2\28\0` has scope number 2, as well as `inLambdaG1\2`. It also has a surrounding scope number equal to 0.
The scope number 0 belongs to the top frame, and basically means that the `inMain` variable should be included in the variables view
when we stop at breakpoint 1.
* `$i$f$h\3\7` has scope number 3, as well as `inH\3`.
* `$i$f$i\4\30` has scope number 4, as well as `inI\4`.
* `$i$f$g\5\8` has scope number 5, as well as `gParam\5`.
* `$i$a$-g-SandboxKt$main$1$1\6\36\2` has scope number 6, as well as `inLambdaG2\6`, and surrounding scope number 2, which means that
we should include variables that are visible in scope 2 when we stop at breakpoint 2. In our example these variables are `inMain`
and `inLambdaG1\2`.