From c0d823fa09d0c17415bf65baab08c18aa6a1f003 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Fri, 24 May 2024 13:15:03 +0530 Subject: [PATCH] Empty stack error compiling project with broken classpath (#2491) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2485 --- .../compiler/codegen/BranchLabel.java | 38 ++++++++++++++++--- .../SwitchExpressionsYieldTest.java | 29 ++++++++++++++ 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/codegen/BranchLabel.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/codegen/BranchLabel.java index f019cd1d5f5..1c247adb1a1 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/codegen/BranchLabel.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/codegen/BranchLabel.java @@ -28,7 +28,7 @@ public class BranchLabel extends Label { protected int targetStackDepth = -1; public final static int WIDE = 1; public final static int USED = 2; - private OperandStack switchSaveTypeBindings; + private OperandStack operandStack; public BranchLabel() { @@ -85,6 +85,29 @@ public void becomeDelegateFor(BranchLabel otherLabel) { // other label is delegating to receiver from now on otherLabel.delegate = this; + if (this.targetStackDepth == -1) { + if (this.codeStream.stackDepth < 0) { + this.codeStream.classFile.referenceBinding.scope.problemReporter() + .operandStackSizeInappropriate(this.codeStream.classFile.referenceBinding.scope.referenceContext); + this.codeStream.stackDepth = 0; // FWIW + this.codeStream.operandStack.clear(); + } + this.targetStackDepth = this.codeStream.stackDepth; + this.operandStack = this.codeStream.operandStack.copy(); + // TODO: check that operand stack contents slot count matches targetStackDepth + } else { + // Stack depth known at label having encountered a previous branch and/or having fallen through to label + if (this.targetStackDepth != this.codeStream.stackDepth) { + this.codeStream.classFile.referenceBinding.scope.problemReporter().operandStackSizeInappropriate( + this.codeStream.classFile.referenceBinding.scope.referenceContext); + if (this.targetStackDepth < this.codeStream.stackDepth) { + this.targetStackDepth = this.codeStream.stackDepth; // FWIW, pick the higher water mark. + this.operandStack = this.codeStream.operandStack.copy(); + } + } + // TODO: check that operand stacks match. + } + // all existing forward refs to other label are inlined into current label final int otherCount = otherLabel.forwardReferenceCount; if (otherCount == 0) return; @@ -136,7 +159,7 @@ protected void trackStackDepth(boolean branch) { this.codeStream.operandStack.clear(); } this.targetStackDepth = this.codeStream.stackDepth; - this.switchSaveTypeBindings = this.codeStream.operandStack.copy(); + this.operandStack = this.codeStream.operandStack.copy(); // TODO: check that contents slot count matches targetStackDepth } // else: previous instruction completes abruptly via goto/return/throw: Wait for a backward branch to be emitted. } else { @@ -145,13 +168,16 @@ protected void trackStackDepth(boolean branch) { if (this.targetStackDepth != this.codeStream.stackDepth) { this.codeStream.classFile.referenceBinding.scope.problemReporter().operandStackSizeInappropriate( this.codeStream.classFile.referenceBinding.scope.referenceContext); - if (this.targetStackDepth < this.codeStream.stackDepth) + if (this.targetStackDepth < this.codeStream.stackDepth) { this.targetStackDepth = this.codeStream.stackDepth; // FWIW, pick the higher water mark. + this.operandStack = this.codeStream.operandStack.copy(); + } } - // TODO: check that contents slot count matches targetStackDepth + // TODO: check that operand stacks match. + } else { + this.codeStream.stackDepth = this.targetStackDepth; + this.codeStream.operandStack = this.operandStack.copy(); } - this.codeStream.stackDepth = this.targetStackDepth; - this.codeStream.operandStack = this.switchSaveTypeBindings.copy(); } } /* diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchExpressionsYieldTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchExpressionsYieldTest.java index 817136a6eb2..a4b1bdd0fdb 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchExpressionsYieldTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchExpressionsYieldTest.java @@ -8001,4 +8001,33 @@ public static void main(String[] args) { }, "String = Hello int = 42"); } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2485 + // Empty stack error compiling project with broken classpath + public void testIssue2485() { + if (this.complianceLevel < ClassFileConstants.JDK14) + return; + this.runConformTest( + new String[] { + "X.java", + """ + public class X { + public static int convertOpcode(int from, int to) { + return switch (from) { + case 42 -> + switch (to) { + case 42 -> 42; + default -> throw new UnsupportedOperationException(); + }; + default -> throw new UnsupportedOperationException(); + }; + } + public static void main(String [] args) { + System.out.println("With 42 & 42 = " + convertOpcode(42, 42)); + } + } + """ + }, + "With 42 & 42 = 42"); + } + }