-
Notifications
You must be signed in to change notification settings - Fork 237
Open
Description
Steps to reproduce
- Follow the instructions from Youtrack. In short:
- Setup an "Enhanced Class Redefinition" launch configuration, i.e. set VM options to
-XX:+AllowEnhancedClassRedefinition -XX:HotswapAgent=fatjar
- Add the
hotswap-agent-2.0.2.jar
file from here into thelib/hotswap
directory in the JBR, and rename it intohotswap-agent.jar
. - You can use the
-core
hotswap agent, or no hotswap agent at all, and it will crash all the same.
- Run this code in debug mode in Intellij IDEA:
val lambdas = mutableListOf<Runnable>()
fun main() {
val x = 10
subcallAdd(x)
while (true) {
Thread.sleep(100)
subcallRun()
}
}
private fun subcallRun() {
lambdas.forEach { it.run() }
}
private fun subcallAdd(x: Int) {
val lambda = Runnable {
println(x)
println("Halo1")
}
lambdas.add(lambda)
}
- Comment out the line
println(x);
By doing this you have made the lambda no longer capture x
, and the function signature of the lambda changes from (int)
to ()
4. Reload changed classes, using a hotkey, or the new integrated Idea popup button.
5. Observe crash:
Exception in thread "main" java.lang.NoSuchMethodError: 'void MainKt.subcallAdd$lambda$1(int)'
at MainKt.subcallRun(Main.kt:20)
at MainKt.main(Main.kt:15)
at MainKt.main(Main.kt)
- You can write it in Java, or add instead of removing capture, and it will crash all the same.
Expected Behavior
The code reloads successfully; The code continues executing without running println(x);
i.e. without printing x
.
Actual Behavior
Program crashes with NoSuchMethodError
on reload with a lambda capture change.
Environment
Using Windows 11, latest JBR Release 21.0.6b631.42. Using Gradle if the matters.
Notes
- Related to DCEVM reloading fails with NoSuchMethodError when changing lambda captures in active stack frame #534 but has a different cause
- If you use an anonymous class instead of a lambda it works:
private fun subcallAdd(x: Int) {
// val lambda = Runnable {
// println(x)
// println("Halo1")
// }
val lambda = object : Runnable {
override fun run() {
println(x)
println("Halo1")
}
}
lambdas.add(lambda)
}
- Same goes with Java - lambda doesn't work; anonymous class does work.
Investigation
While the anonymous class creates a new class and adds an instance of it to the list:
public final class io/github/natanfudge/fn/MainKt$subcallAdd$lambda$1 implements java/lang/Runnable
...
public run()V
...
... In `subcallAdd`:
NEW io/github/natanfudge/fn/MainKt$subcallAdd$lambda$1
The lambda generates a method and adds it to the list using invokedynamic:
private final static subcallAdd$lambda$1(I)V
...
... in `subcallAdd`:
INVOKEDYNAMIC run(I)Ljava/lang/Runnable; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
io/github/natanfudge/fn/MainKt.subcallAdd$lambda$1(I)V,
()V
]
Metadata
Metadata
Assignees
Labels
No labels