Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 544943

Summary: [13][codegen] Exception while codegen for multi-nested switch expression with try
Product: [Eclipse Project] JDT Reporter: Manoj N Palat <manoj.palat>
Component: CoreAssignee: Olivier Thomann <Olivier_Thomann>
Status: RESOLVED FIXED QA Contact: Olivier Thomann <Olivier_Thomann>
Severity: normal    
Priority: P3 CC: daniel_megert, jarthana, loskutov, manoj.palat, stephan.herrmann
Version: 4.11   
Target Milestone: 4.14 M1   
Hardware: PC   
OS: Windows 7   
See Also: https://git.eclipse.org/r/149843
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=d629b1096b8cb254d581d898bc8e532a43fadd9d
https://git.eclipse.org/r/149876
https://git.eclipse.org/r/149878
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=42e8e7c7d3c0a2353712025cd77254613c5d31d7
https://git.eclipse.org/r/149881
https://git.eclipse.org/r/149896
https://git.eclipse.org/r/150001
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=d698c9585b5705d5d5c4944bb938bdba566fefdc
https://git.eclipse.org/r/150017
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=4cce8d539640bf79f986bb9a432b2351d6a08608
https://bugs.eclipse.org/bugs/show_bug.cgi?id=553315
https://bugs.eclipse.org/bugs/show_bug.cgi?id=553386
Whiteboard:
Bug Depends on: 551368    
Bug Blocks:    
Attachments:
Description Flags
test case
none
Debugging Notes none

Description Manoj N Palat CLA 2019-03-01 05:46:31 EST
Created attachment 277728 [details]
test case

Run the attached testcase to get :
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	at X.foo(X.java:3)
	at X.main(X.java:20)
Comment 1 Manoj N Palat CLA 2019-03-01 05:49:45 EST
ST for debugging : break here:
MethodDeclaration(AbstractMethodDeclaration).generateCode(ClassFile) line: 357	
MethodDeclaration(AbstractMethodDeclaration).generateCode(ClassScope, ClassFile) line: 281	
TypeDeclaration.generateCode(ClassFile) line: 578	
TypeDeclaration.generateCode(CompilationUnitScope) line: 648	
CompilationUnitDeclaration.generateCode() line: 410	
CompilationUnitProblemFinder(Compiler).resolve(CompilationUnitDeclaration, ICompilationUnit, boolean, boolean, boolean) line: 1061	
CompilationUnitProblemFinder(Compiler).resolve(ICompilationUnit, boolean, boolean, boolean) line: 1100	
CompilationUnitProblemFinder.process(CompilationUnit, SourceElementParser, WorkingCopyOwner, HashMap, boolean, int, IProgressMonitor) line: 280	
CompilationUnitProblemFinder.process(CompilationUnit, WorkingCopyOwner, HashMap, boolean, int, IProgressMonitor) line: 346	
ReconcileWorkingCopyOperation.makeConsistent(CompilationUnit) line: 193	
ReconcileWorkingCopyOperation.executeOperation() line: 94	
ReconcileWorkingCopyOperation(JavaModelOperation).run(IProgressMonitor) line: 736	
ReconcileWorkingCopyOperation(JavaModelOperation).runOperation(IProgressMonitor) line: 802	
CompilationUnit.reconcile(int, int, WorkingCopyOwner, IProgressMonitor) line: 1322	
JavaReconcilingStrategy.reconcile(ICompilationUnit, boolean) line: 131	

NegativeArraySizeException is thrown.
Comment 2 Olivier Thomann CLA 2019-03-01 08:02:09 EST
Reproduced. Working on it.
Comment 3 Jay Arthanareeswaran CLA 2019-03-20 02:24:54 EDT
I don't know if this is same, but the following code throws an AIOBE when compiling. And making small changes to the code throws a verify error.

public class X {
    @SuppressWarnings("preview")
	public static void main(String[] args) {
    (new X()).bar(switch (0) {
           default -> {
               try {
                   break 1;
               }
               catch (Exception ex) {
                   break 2;
               }
           }
        }); 
        foo(Day.SUNDAY); // Commenting out this code make the compiler work but throws a VerifyError on running
    }
    public void bar(int i) {} // if bar() is static, we can both compiler and run the code
    public static void foo(Day d) {} 
} 
enum Day {  
	SUNDAY, MONDAY;
}
Comment 4 Manoj N Palat CLA 2019-06-04 00:56:06 EDT
Retargeting to Java13 with the latest spec changes.
Comment 5 Jay Arthanareeswaran CLA 2019-08-14 00:52:57 EDT
(In reply to Manoj Palat from comment #0)
> Created attachment 277728 [details]
> test case

Test case further simplified without the custom exception:

public class X {
  @SuppressWarnings("preview")
  public static void foo(int i) throws Exception {
    int v = switch (i) {
        case 0 -> switch (i) {
              case 0 -> 0;
              default -> throw new Exception();
            };
        default -> 0;
    };
  }

  public static void main(String argv[]) throws Exception {
    X.foo(0);
  }
}
Comment 6 Manoj N Palat CLA 2019-08-21 23:26:04 EDT
Created attachment 279655 [details]
Debugging Notes

A short description of the analysis so far given below - For the complete detailed information, please refer to the attachment:

For the following code snippet,

  public static int foo(int i) throws Exception {
    int v = switch (i) {
        case 0 -> switch (i) {
              case 0 -> 0;
              default -> throw new Exception();
            };
        default -> 0;
    };
    return v;
  }

The code generated is shown below:
// Method descriptor #15 (I)I
  // Stack: 3, Locals: 1
  public static int foo(int arg0) throws java.lang.Exception;
     0  new java.lang.Error [23]
     3  dup
     4  ldc <String "Unresolved compilation problem: \n"> [25]
     6  invokespecial java.lang.Error(java.lang.String) [27]
     9  athrow
      Line numbers:
        [pc: 0, line: 3]

The message at runtime is of course given by the line 4  ldc <String "Unresolved compilation problem: \n"> which is inserted into the code stream by the following st (debug breakpoint):
ClassFile.addProblemMethod(AbstractMethodDeclaration, MethodBinding, CategorizedProblem[]) line: 914	
ClassFile.addProblemMethod(AbstractMethodDeclaration, MethodBinding, CategorizedProblem[], int) line: 952	
MethodDeclaration(AbstractMethodDeclaration).generateCode(ClassScope, ClassFile) line: 317	
TypeDeclaration.generateCode(ClassFile) line: 583	
TypeDeclaration.generateCode(CompilationUnitScope) line: 653	
CompilationUnitDeclaration.generateCode() line: 410	
CompilationUnitResolver.resolve(CompilationUnitDeclaration, ICompilationUnit, NodeSearcher, boolean, boolean, boolean) line: 1249	
CompilationUnitResolver.resolve(ICompilationUnit, IJavaProject, List, NodeSearcher, Map, WorkingCopyOwner, int, IProgressMonitor) line: 714	
ASTParser.internalCreateAST(IProgressMonitor) line: 1221	
ASTParser.createAST(IProgressMonitor) line: 836	
CoreASTProvider$1.run() line: 271	

at the st:
StackMapFrame.duplicate() line: 129	
ClassFile.traverse(MethodBinding, int, byte[], int, int, Map, boolean) line: 5955	
ClassFile.generateStackMapTableAttribute(MethodBinding, int, int, int, boolean) line: 4804	
ClassFile.completeCodeAttribute(int) line: 1513	
MethodDeclaration(AbstractMethodDeclaration).generateCode(ClassFile) line: 357	
MethodDeclaration(AbstractMethodDeclaration).generateCode(ClassScope, ClassFile) line: 281	
TypeDeclaration.generateCode(ClassFile) line: 583	
TypeDeclaration.generateCode(CompilationUnitScope) line: 653	
CompilationUnitDeclaration.generateCode() line: 410	

	length = this.numberOfStackItems;
	if (length != 0) {
the length is -1 is here

and an allocation of an array of negative size, causes NegativeArraySizeException which causes the abort pushing all the way up to provide the ldc string of compilation error.
Comment 7 Manoj N Palat CLA 2019-08-22 05:25:13 EDT
@Olivier: Adding further debugging notes:
the length mentioned in the above comment 6 is the stack items ie frame. numberOfStackItems
Starting from the expected code to be generated [the following generated by commenting out //					frame.numberOfStackItems--; under tableSwitch case in traverse [for debug purpose only]:
  // Method descriptor #15 (I)I
  // Stack: 2, Locals: 2
  public static int foo(int i) throws java.lang.Exception;
     0  iload_0 [i]
     1  tableswitch default: 52
          case 0: 20
    20  iload_0 [i]
    21  tableswitch default: 44
          case 0: 40
    40  iconst_0
    41  goto 53
    44  new java.lang.Exception [17]
    47  dup
    48  invokespecial java.lang.Exception() [19]
    51  athrow
    52  iconst_0
    53  istore_1 [v]
    54  iload_1 [v]
    55  ireturn
      Line numbers:
        [pc: 0, line: 4]
        [pc: 20, line: 5]
        [pc: 40, line: 6]
        [pc: 44, line: 7]
        [pc: 52, line: 9]
        [pc: 53, line: 4]
        [pc: 54, line: 11]
      Local variable table:
        [pc: 0, pc: 56] local: i index: 0 type: int
        [pc: 54, pc: 56] local: v index: 1 type: int

The stack frame snapshots are each of the opcodes are listed below:

0	iload_0 [i]
  [pc : -1 locals: 0 stack items: 1
locals: [I,top]
stack: [I]]


     1  tableswitch default: 52
          case 0: 20
[pc : -1 locals: 1 stack items: 0
locals: [I,top]
stack: []
]
    20  iload_0 [i]
[pc : -1 locals: 1 stack items: 1
locals: [I,top]
stack: [I]
]
    21  tableswitch default: 44
          case 0: 40
[pc : -1 locals: 1 stack items: 0
locals: [I,top]
stack: []
]
    40  iconst_0
[pc : -1 locals: 1 stack items: 1
locals: [I,top]
stack: [I]
]
    41  goto 53
[pc : -1 locals: 1 stack items: 1
locals: [I,top]
stack: [I]
]
    44  new java.lang.Exception [17]
[pc : -1 locals: 1 stack items: 1
locals: [I,top]
stack: [uninitialized(java/lang/Exception)]
]
    47  dup
[pc : -1 locals: 1 stack items: 2
locals: [I,top]
stack: [uninitialized(java/lang/Exception),uninitialized(java/lang/Exception)]
]
    48  invokespecial java.lang.Exception() [19]
[pc : -1 locals: 1 stack items: 1
locals: [I,top]
stack: [java/lang/Exception]
]
    51  athrow
[pc : -1 locals: 1 stack items: 0
locals: [I,top]
stack: []
]

And a frame.duplicate() throws NegativeArrayException here 
when it tries to decrement stack items which is already 0.
    52  iconst_0
    53  istore_1 [v]
    54  iload_1 [v]
    55  ireturn

]
This points to the decrement of athrow, and for debugging purposes, commenting out the //					frame.numberOfStackItems--;
Under	case Opcodes.OPC_athrow:
Of ClassFile.traverse; compilation goes through and the code generated is as follows:
// Method descriptor #15 (I)I
  // Stack: 2, Locals: 2
  public static int foo(int i) throws java.lang.Exception;
     0  iload_0 [i]
     1  tableswitch default: 52
          case 0: 20
    20  iload_0 [i]
    21  tableswitch default: 44
          case 0: 40
    40  iconst_0
    41  goto 53
    44  new java.lang.Exception [17]
    47  dup
    48  invokespecial java.lang.Exception() [19]
    51  athrow
    52  iconst_0
    53  istore_1 [v]
    54  iload_1 [v]
    55  ireturn
      Line numbers:
        [pc: 0, line: 4]
        [pc: 20, line: 5]
        [pc: 40, line: 6]
        [pc: 44, line: 7]
        [pc: 52, line: 9]
        [pc: 53, line: 4]
        [pc: 54, line: 11]
      Local variable table:
        [pc: 0, pc: 56] local: i index: 0 type: int
        [pc: 54, pc: 56] local: v index: 1 type: int
      Stack map table: number of frames 5
        [pc: 20, same]
        [pc: 40, same]
        [pc: 44, same]
        [pc: 52, same]
        [pc: 53, same_locals_1_stack_item, stack: {int}]
Looking at the Stack map table (jdt disassembler output – pc explicitly displayed),
 this is in complete agreement with the javac generated code as well, the appropriate part extracted below (javap output that of the above identical now)
      StackMapTable: number_of_entries = 5
        frame_type = 20 /* same */
        frame_type = 19 /* same */
        frame_type = 3 /* same */
        frame_type = 7 /* same */
        frame_type = 64 /* same_locals_1_stack_item */
          stack = [ int ]

The program runs and prints zero correctly as well.
Random notes/thinking aloud: Though a blanket commenting of decrement is of course wrong, is this pointing to any omission in terms of are we missing any conditional check before decrementing the stack items at athrow in a switch labeled expression rhs? Or any special processing at the flow-analysis/code-gen stage of ThrowStatement within a switch labelled expression enclosed within a switch expression?
Comment 8 Manoj N Palat CLA 2019-08-23 10:08:02 EDT
Further Notes:
public class X {
  @SuppressWarnings({ "preview" })
  public static int foo(int i) throws Exception {
    int v = switch (i) {
        case 0 -> switch (i) {
        	case 0 -> 0;
            default-> throw new Exception();
        	case 3 -> 3;
        	case 2 -> throw new Exception();
        };
        default -> 0;
    };
    return v;
  }
  public static void main(String argv[]) throws Exception {
    System.out.println(X.foo(1));
  }
The problem happens only if throw new Exception() is the last case in the switch expression – it can be either a case or the default, just the order , ie just the last one matters – Is the YieldStatement.adjustStackSize() a suspect?
Comment 9 Olivier Thomann CLA 2019-08-29 15:45:41 EDT
Investigating the issue again. Could not get a fix yet. Continuing to debug the case. I think this is a consequence of optimized goto.
Comment 10 Eclipse Genie CLA 2019-09-19 09:46:39 EDT
New Gerrit change created: https://git.eclipse.org/r/149843
Comment 11 Manoj N Palat CLA 2019-09-19 09:50:22 EDT
(In reply to Eclipse Genie from comment #10)
> New Gerrit change created: https://git.eclipse.org/r/149843

Merge of Olivier's new_stack_map to master via : https://git.eclipse.org/r/#/c/149842
Comment 13 Manoj N Palat CLA 2019-09-20 01:39:14 EDT
(In reply to Manoj Palat from comment #11)

> Merge of Olivier's new_stack_map to master via :
> https://git.eclipse.org/r/#/c/149842

Done via commit: https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=9407f9ff6ccc3c7dccbee9bda158370adbe122c7

Thanks Olivier!
Comment 14 Eclipse Genie CLA 2019-09-20 03:01:17 EDT
New Gerrit change created: https://git.eclipse.org/r/149876
Comment 15 Eclipse Genie CLA 2019-09-20 03:05:42 EDT
New Gerrit change created: https://git.eclipse.org/r/149878
Comment 17 Eclipse Genie CLA 2019-09-20 03:09:18 EDT
New Gerrit change created: https://git.eclipse.org/r/149881
Comment 18 Eclipse Genie CLA 2019-09-20 06:10:48 EDT
New Gerrit change created: https://git.eclipse.org/r/149896
Comment 19 Manoj N Palat CLA 2019-09-20 09:35:35 EDT
A combined stashed change of Olivier's patch alongwith test case changes and dom ast ConverterJCL additions/corrections at https://git.eclipse.org/r/#/c/149902/.

Tests are green.

This was pushed via commit:https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=ae3d720efce20459d1d746646153478f03bf3002

Thanks Olivier.

Resolving
Comment 20 Andrey Loskutov CLA 2019-09-23 09:27:54 EDT
(In reply to Manoj Palat from comment #19)
> A combined stashed change of Olivier's patch alongwith test case changes and
> dom ast ConverterJCL additions/corrections at
> https://git.eclipse.org/r/#/c/149902/.
> 
> Tests are green.
> 
> This was pushed via
> commit:https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/
> ?id=ae3d720efce20459d1d746646153478f03bf3002
> 
> Thanks Olivier.
> 
> Resolving

This one most likely caused a major regression, see bug 551368.
Comment 21 Eclipse Genie CLA 2019-09-23 10:02:15 EDT
New Gerrit change created: https://git.eclipse.org/r/150001
Comment 22 Andrey Loskutov CLA 2019-09-23 10:28:39 EDT
(In reply to Eclipse Genie from comment #21)
> New Gerrit change created: https://git.eclipse.org/r/150001

This is a revert for commit ae3d720efce20459d1d746646153478f03bf3002.
Comment 24 Eclipse Genie CLA 2019-09-23 15:56:10 EDT
New Gerrit change created: https://git.eclipse.org/r/150017
Comment 25 Manoj N Palat CLA 2019-09-23 23:58:28 EDT
(In reply to Eclipse Genie from comment #24)
> New Gerrit change created: https://git.eclipse.org/r/150017


New Gerrit change created: https://git.eclipse.org/r/150033


Created a combined gerrit that includes the revert of revert of bug 544943 and then applied the patch of bug 551368 - essentially https://git.eclipse.org/r/#/c/150018/.
Request people willing to test this out by spawning Eclipse and doing some sanity test.
Comment 27 Jay Arthanareeswaran CLA 2019-09-24 04:35:07 EDT
Fixed via bug 551368.
Comment 28 Olivier Thomann CLA 2019-09-25 14:04:14 EDT
The fix for this issue is a complete rewrite of the stack map frame computation. Instead of trying to keep stack map markers during the code generation, the generated bytecodes are traversed to compute the needed stack map frames. I added the notion of merging existing stack map frames with the current frame at branching locations. This required to have a scope in order to be able to compute type bindings based on the type names stored in the class files. This is required to be able to find the common type between two subtypes. Once the stack map frames are computed, the corresponding entries are added into the .class files by comparing the frames with their previous frame.
Comment 29 Jay Arthanareeswaran CLA 2019-10-10 01:52:41 EDT
Verified for 4.14 M1 using build I20191009-1800
Comment 30 Stephan Herrmann CLA 2019-10-16 14:33:50 EDT
This bug contains changes in org.eclipse.jdt.core/.settings/org.eclipse.pde.api.tools.prefs which effectively *turn off* API tools in the IDE.

Why??

While this bug has gone through many reverts and revert-reverts, the initial change to these prefs was done via http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=134cb1fd6dd0e0b14cfeaa0ff3d1bb323bc79a00

Wasn't Olivier one of the original authors of API tools? You should know the value of these checks :)
Comment 31 Jay Arthanareeswaran CLA 2019-10-17 04:58:48 EDT
(In reply to Stephan Herrmann from comment #30)
> This bug contains changes in
> org.eclipse.jdt.core/.settings/org.eclipse.pde.api.tools.prefs which
> effectively *turn off* API tools in the IDE.
> 
> Why??

I can only think of this to be overlooked.

Once Olivier confirm that there were no intended changes for this file, I will revert this particular file.

Stephan, out of curiosity, how did you catch this?
Comment 32 Stephan Herrmann CLA 2019-10-17 09:36:50 EDT
(In reply to Jay Arthanareeswaran from comment #31)
> Stephan, out of curiosity, how did you catch this?

Saw it flying by while merging JDT/Core changes into the Object Teams fork.
Comment 33 Eclipse Genie CLA 2019-11-05 01:34:13 EST
New Gerrit change created: https://git.eclipse.org/r/151998
Comment 34 Dani Megert CLA 2019-11-11 11:53:50 EST
Can you please track and fix this in a new bug. Thanks.
Comment 35 Stephan Herrmann CLA 2019-11-21 10:24:20 EST
(In reply to Dani Megert from comment #34)
> Can you please track and fix this in a new bug. Thanks.

Over to bug 553315
Comment 36 Olivier Thomann CLA 2020-01-03 12:07:31 EST
(In reply to Jay Arthanareeswaran from comment #31)
> Once Olivier confirm that there were no intended changes for this file, I
> will revert this particular file.
Sorry, overlook this