JDK-8314275 : Incorrect stepping in switch
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 17,18,19,20,21,22
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2023-08-15
  • Updated: 2024-02-15
  • Resolved: 2024-02-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 22 JDK 23
22.0.1Fixed 23 b10Fixed
Related Reports
Relates :  
Description
Consider debugging the code with jdk 17:

public class DebuggingSwitch {

    public static void main(String[] args) throws Exception {
        String returnValue;
        try {

            System.out.println("Enter the try catch block");
            throw new Exception("Error");
        } catch(Exception e) {
            returnValue = switch(e.getMessage()) { // breakpoint here and start stepping over
                case "Error" -> {
                    System.out.println("Error exception"); // 1 stop - ok
                    yield "Error"; // 2 stop - ok
                }
                case "Warning" -> {
                    System.out.println("Warning exception");
                    yield "Warning";
                }
                default -> {
                    System.out.println("Default exception");
                    System.out.println("Another default exception");
                    throw e; // 2 stop - NOT OK
                }
            };
        }
    }
}

Set a breakpoint at the switch line inside the catch block, then step over several times. On the third stop it will stop at the line with  "throw e;" inside the default block which is definitely not executed, which is really misleading.

Originally the problem was reported here: https://youtrack.jetbrains.com/issue/IDEA-316450
Comments
Fix Request 22u This patch addresses an issue during debugging of pattern matching. The user will face an inconsistency between the actual flow and what a debugger shows. Patch applies cleanly and is very minimal.
09-02-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk17u/pull/387 Date: 2024-02-09 10:11:11 +0000
09-02-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk22u/pull/47 Date: 2024-02-09 10:08:45 +0000
09-02-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk21u/pull/437 Date: 2024-02-09 10:06:43 +0000
09-02-2024

Changeset: e3dc6a7a Author: Aggelos Biboudis <abimpoudis@openjdk.org> Date: 2024-02-09 08:52:28 +0000 URL: https://git.openjdk.org/jdk/commit/e3dc6a7a28c4f049eb234c5487fca6c54298aa31
09-02-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/17772 Date: 2024-02-08 14:29:50 +0000
08-02-2024

The linked issue is related, but at a different level. Similar testing used as in that one.
08-02-2024

This is a javac LineNumberTable bug. This simpler example also demonstrates the problem (note I'm using a build from the current JDK22 source for this): import java.time.DayOfWeek; public class SwitchDebug { public static void main(String[] args) { System.out.println(multiply(DayOfWeek.MONDAY)); } private static double multiply(DayOfWeek day) { double cr = 15; cr = switch (day) { case MONDAY -> cr * 1; case FRIDAY -> cr * 2; default -> cr * 4; }; return cr; } } private static double multiply(java.time.DayOfWeek); Code: 0: ldc2_w #31 // double 15.0d 3: dstore_1 4: getstatic #33 // Field SwitchDebug$1.$SwitchMap$java$time$DayOfWeek:[I 7: aload_0 8: invokevirtual #39 // Method java/time/DayOfWeek.ordinal:()I 11: iaload 12: lookupswitch { // 2 1: 40 2: 46 default: 54 } 40: dload_1 41: dconst_1 42: dmul 43: goto 59 46: dload_1 47: ldc2_w #43 // double 2.0d 50: dmul 51: goto 59 54: dload_1 55: ldc2_w #45 // double 4.0d 58: dmul 59: dstore_1 60: dload_1 61: dreturn LineNumberTable: line 9: 0 line 10: 4 line 11: 40 line 12: 46 line 13: 54 line 15: 60 All 3 case statements leave the resulting value on the stack and then do a goto 59. 59 is responsible for storing the value into a local. Looking at the LineNumberTable, you can see that bytecode 54 maps to line 13 and bytecode 60 maps to line 15, so the debugger is going to map bytecode 59 to line 13. Line 13 is: 13 default -> cr * 4; So no matter which case statement you step through, the debugger eventually will show execution flowing through line 13, even though correct behavior is to show it flowing through line 14. I think the fix is to make the LineNumberTable be the following: LineNumberTable: line 9: 0 line 10: 4 line 11: 40 line 12: 46 line 13: 54 line 14: 59 <-- add this entry line 15: 60
15-08-2023

tried to compile and run with jdk 21 - same thing
15-08-2023

In all likelihood this is a javac bug due to incorrect LineNumberTable attributes. Has anyone tried with a more recent version of the JDK to see it still reproduces?
15-08-2023

Another case is here: https://youtrack.jetbrains.com/issue/IDEA-327945 (even without the catch block)
15-08-2023