JVM exceptions are weird: a decompiler perspective

(purplesyringa.moe)

65 points | by vrnvu 6 days ago ago

11 comments

  • marginalia_nu 2 hours ago ago

    On the subject

      void foo() {
        for (;;) {
          try { return; } 
          finally { continue; }
        }
      }
    
    is my favorite cursed Java exceptions construct.
    • ptx 42 minutes ago ago

      Python has the same construct but is removing it, starting with a warning in version 3.14: https://peps.python.org/pep-0765/

    • cerved 2 hours ago ago

      To anyone wondering, I believe it's cursed because the finally continue blocks hijacks the try return, so the for loop never returns

    • ziml77 33 minutes ago ago

      Just tested that in C# and it seems they made the smart decision to not allow shenanigans like that in a finally block:

      CS0157 Control cannot leave the body of a finally clause

    • kfuse an hour ago ago

      That's not just Java and there is nothing really cursed about it: throwing in a finally block is the most common example. Jump statements are no different, you can't just ignore them when they override the return or throw statements.

    • bear8642 an hour ago ago

      This is exceedingly nasty. Well Done!

  • pron 3 hours ago ago

    Nice post!

    A minor point:

    > monitors are incompatible with coroutines

    If by coroutines the author meant virtual threads, then monitors have always been compatible with virtual threads (which have always needed to adhere to the Thread specification). Monitors could, for a short while, degrade the scalability of virtual threads (and in some situations even lead to deadlocks), but that has since been resolved in JDK 24 (https://openjdk.org/jeps/491).

    • PhilipRoman an hour ago ago

      I think it's coroutines as in other JVM languages like Kotlin, where yielding may be implemented internally as return (due to lack of native coroutine support in JVM).

      Holding a lock/monitor across a yield is a bad idea for other reasons, so it shouldn't be a big deal in practice.

  • Joker_vD an hour ago ago

    Doesn't JRE has some limited form of decompilation in its JIT, as a pre-pass? IIRC, it reconstructs the basic blocks and CFG from the bytecode and does some minor optimizations before going on to regalloc and codegen.

    • monocasa 41 minutes ago ago

      It's hard to call it decompilation as opposed to just regular compilation though.

  • immibis an hour ago ago

    Older versions of Java did try to have only one copy of the finally block code. To implement this, there were "jsr" and "ret" instructions, which allowed a method (a subroutine) to contain subroutines inside it. This even curseder implementation of finally is prohibited starting from version 51 class files (Java 7).