Skip to content

Commit

Permalink
[Switch Expression] Internal compiler error: java.lang.ClassCastExcep…
Browse files Browse the repository at this point in the history
…tion: class org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding cannot be cast to class org.eclipse.jdt.internal.compiler.lookup.ArrayBinding (eclipse-jdt#2344)

* Fixes eclipse-jdt#2335
  • Loading branch information
srikanth-sankaran authored Apr 17, 2024
1 parent b2ac1e4 commit a524b63
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;

import org.eclipse.jdt.core.compiler.CharOperation;
Expand Down Expand Up @@ -263,46 +262,38 @@ private LocalVariableBinding addTypeStackVariable(CodeStream codeStream, TypeBin
lvb.declaration = new LocalDeclaration(name, 0, 0);
return lvb;
}
private int getNextOffset(LocalVariableBinding local) {
int delta = ((TypeBinding.equalsEquals(local.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(local.type, TypeBinding.DOUBLE))) ?
2 : 1;
return local.resolvedPosition + delta;
}
private void processTypesBindingsOnStack(CodeStream codeStream) {
int count = 0;
private void spillOperandStack(CodeStream codeStream) {
int nextResolvedPosition = this.scope.offset;
if (!codeStream.switchSaveTypeBindings.empty()) {
this.typesOnStack = new ArrayList<>();
int index = 0;
Stack<TypeBinding> typeStack = new Stack<>();
int sz = codeStream.switchSaveTypeBindings.size();
for (int i = codeStream.lastSwitchCumulativeSyntheticVars; i < sz; ++i) {
typeStack.add(codeStream.switchSaveTypeBindings.get(i));
}
while (!typeStack.empty()) {
TypeBinding type = typeStack.pop();
LocalVariableBinding lvb = addTypeStackVariable(codeStream, type, TypeIds.T_undefined, index++, nextResolvedPosition);
nextResolvedPosition = getNextOffset(lvb);
this.typesOnStack.add(lvb);
codeStream.store(lvb, false);
codeStream.addVariable(lvb);
++count;
}
this.typesOnStack = new ArrayList<>();
int index = 0;
while (codeStream.switchSaveTypeBindings.size() > 0) {
TypeBinding type = codeStream.switchSaveTypeBindings.peek();
LocalVariableBinding lvb = addTypeStackVariable(codeStream, type, TypeIds.T_undefined, index++, nextResolvedPosition);
nextResolvedPosition += switch (lvb.type.id) {
case TypeIds.T_long, TypeIds.T_double -> 2;
default -> 1;
};
this.typesOnStack.add(lvb);
codeStream.store(lvb, false);
codeStream.addVariable(lvb);
}
if (codeStream.stackDepth != 0 || codeStream.switchSaveTypeBindings.size() != 0) {
codeStream.classFile.referenceBinding.scope.problemReporter().operandStackSizeInappropriate(codeStream.classFile.referenceBinding.scope.referenceContext);
}
// now keep a position reserved for yield result value
this.yieldResolvedPosition = nextResolvedPosition;
nextResolvedPosition += ((TypeBinding.equalsEquals(this.resolvedType, TypeBinding.LONG)) ||
(TypeBinding.equalsEquals(this.resolvedType, TypeBinding.DOUBLE))) ?
2 : 1;

codeStream.lastSwitchCumulativeSyntheticVars += count + 1; // 1 for yield var
int delta = nextResolvedPosition - this.scope.offset;
this.scope.adjustLocalVariablePositions(delta, false);
}
public void loadStoredTypesAndKeep(CodeStream codeStream) {
public void refillOperandStack(CodeStream codeStream) {
List<LocalVariableBinding> tos = this.typesOnStack;
int sz = tos != null ? tos.size() : 0;
codeStream.clearTypeBindingStack();
codeStream.stackDepth = 0;
int index = sz - 1;
while (index >= 0) {
LocalVariableBinding lvb = tos.get(index--);
Expand All @@ -322,15 +313,12 @@ private void removeStoredTypes(CodeStream codeStream) {
}
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
int tmp = 0;
if (this.containsTry) {
tmp = codeStream.lastSwitchCumulativeSyntheticVars;
processTypesBindingsOnStack(codeStream);
spillOperandStack(codeStream);
}
super.generateCode(currentScope, codeStream);
if (this.containsTry) {
removeStoredTypes(codeStream);
codeStream.lastSwitchCumulativeSyntheticVars = tmp;
}
if (!valueRequired) {
// switch expression is saved to a variable that is not used. We need to pop the generated value from the stack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) {
}
}
if (generateExpressionResultCodeExpanded) {
this.switchExpression.loadStoredTypesAndKeep(codeStream);
this.switchExpression.refillOperandStack(codeStream);
codeStream.load(this.secretYieldResultValue);
codeStream.removeVariable(this.secretYieldResultValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
package org.eclipse.jdt.internal.compiler.codegen;

import java.util.Arrays;
import java.util.Stack;

import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;

public class BranchLabel extends Label {

Expand All @@ -29,6 +31,8 @@ public class BranchLabel extends Label {
protected int targetStackDepth = -1;
public final static int WIDE = 1;
public final static int USED = 2;
private Stack<TypeBinding> switchSaveTypeBindings;


public BranchLabel() {
// for creating labels ahead of code generation
Expand Down Expand Up @@ -118,6 +122,7 @@ public void becomeDelegateFor(BranchLabel otherLabel) {
this.forwardReferenceCount = indexInMerge;
}

@SuppressWarnings("unchecked")
protected void trackStackDepth(boolean branch) {
/* Control can reach an instruction with a label in two ways: (1) via a branch using that label or (2) by falling through from the previous instruction.
In both cases, we KNOW the stack depth at the instruction from which control flows to the instruction with the label.
Expand All @@ -132,8 +137,11 @@ protected void trackStackDepth(boolean branch) {
this.codeStream.classFile.referenceBinding.scope.problemReporter()
.operandStackSizeInappropriate(this.codeStream.classFile.referenceBinding.scope.referenceContext);
this.codeStream.stackDepth = 0; // FWIW
this.codeStream.switchSaveTypeBindings.clear();
}
this.targetStackDepth = this.codeStream.stackDepth;
this.switchSaveTypeBindings = (Stack<TypeBinding>) this.codeStream.switchSaveTypeBindings.clone();
// 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 {
// Stack depth known at label having encountered a previous branch and/or having fallen through to label
Expand All @@ -144,8 +152,10 @@ protected void trackStackDepth(boolean branch) {
if (this.targetStackDepth < this.codeStream.stackDepth)
this.targetStackDepth = this.codeStream.stackDepth; // FWIW, pick the higher water mark.
}
// TODO: check that contents slot count matches targetStackDepth
}
this.codeStream.stackDepth = this.targetStackDepth;
this.codeStream.switchSaveTypeBindings = (Stack<TypeBinding>) this.switchSaveTypeBindings.clone();
}
}
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ public class CodeStream {
public boolean wideMode = false;

public Stack<TypeBinding> switchSaveTypeBindings = new Stack<>();
public int lastSwitchCumulativeSyntheticVars = 0;

public Map<BlockScope, List<ExceptionLabel>> patternAccessorMap = new HashMap<>();
public Stack<BlockScope> accessorExceptionTrapScopes = new Stack<>();
Expand Down Expand Up @@ -4581,7 +4580,6 @@ public void init(ClassFile targetClassFile) {
this.position = 0;

this.clearTypeBindingStack();
this.lastSwitchCumulativeSyntheticVars = 0;
this.patternAccessorMap.clear();
this.accessorExceptionTrapScopes.clear();
}
Expand Down Expand Up @@ -6934,7 +6932,7 @@ public void pop2() {

public void pushExceptionOnStack(TypeBinding binding) {
this.stackDepth = 1;
// clearTypeBindingStack();
clearTypeBindingStack();
pushTypeBinding(binding);
if (this.stackDepth > this.stackMax)
this.stackMax = this.stackDepth;
Expand Down Expand Up @@ -7784,7 +7782,7 @@ private void pushTypeBindingArray() {
return;
popTypeBinding(); // index
TypeBinding type = popTypeBinding(); // arrayref
pushTypeBinding(((ArrayBinding) type).leafComponentType);
pushTypeBinding(((ArrayBinding) type).elementsType());
}
private TypeBinding getPopularBinding(char[] typeName) {
Scope scope = this.classFile.referenceBinding.scope;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7193,4 +7193,110 @@ static void print(double d) {}
},
"OK");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2335
// [Switch Expression] Internal compiler error: java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding cannot be cast to class org.eclipse.jdt.internal.compiler.lookup.ArrayBinding
public void testIssue2335() {
if (this.complianceLevel < ClassFileConstants.JDK14)
return;
this.runConformTest(
new String[] {
"X.java",
"""
public final class X {
public void show() {
int size1 = 1;
int size2 = 2;
int size3 = 3;
short[][][] array = new short[size1][size2][size3];
for (int i = 0; i < size1; i++) {
for (int j = 0; j < size2; j++) {
boolean on = false;
for (int k = 0; k < size3; k++) {
array[i][j][k] = on ? (short) 1 : (short) 0;
}
}
}
System.out.println(switch(42) {
default -> {
try {
yield 42;
} finally {
}
}
});
}
public static void main(String[] args) {
new X().show();
}
}
"""
},
"42");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2335
// [Switch Expression] Internal compiler error: java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding cannot be cast to class org.eclipse.jdt.internal.compiler.lookup.ArrayBinding
public void testIssue2335_min() {
if (this.complianceLevel < ClassFileConstants.JDK14)
return;
this.runConformTest(
new String[] {
"X.java",
"""
public final class X {
public static void main(String[] args) {
short[] array = new short[10];
for (int i = 0; i < 10; i++) {
boolean on = false;
array[i] = on ? (short) 1 : (short) 0;
}
System.out.println(switch(42) {
default -> {
try {
yield 42;
} finally {
}
}
});
}
}
"""
},
"42");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2335
// [Switch Expression] Internal compiler error: java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding cannot be cast to class org.eclipse.jdt.internal.compiler.lookup.ArrayBinding
public void testIssue2335_other() {
if (this.complianceLevel < ClassFileConstants.JDK14)
return;
this.runConformTest(
new String[] {
"X.java",
"""
public class X {
public static void main(String[] args) {
System.out.println(switch (1) {
default -> {
try {
System.out.println(switch (10) { default -> { try { yield 10; } finally {} } });
} finally {}
yield 1;
}
});
}
X() {}
}
"""
},
"10\n" +
"1");
}
}

0 comments on commit a524b63

Please sign in to comment.