Updated exception table generation: try block return moved to uncatched interval
#KT-8148 Fixed
This commit is contained in:
@@ -1197,16 +1197,20 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StackValue visitBreakExpression(@NotNull JetBreakExpression expression, StackValue receiver) {
|
public StackValue visitBreakExpression(@NotNull JetBreakExpression expression, StackValue receiver) {
|
||||||
return generateBreakOrContinueExpression(expression, true);
|
return generateBreakOrContinueExpression(expression, true, new Label());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StackValue visitContinueExpression(@NotNull JetContinueExpression expression, StackValue receiver) {
|
public StackValue visitContinueExpression(@NotNull JetContinueExpression expression, StackValue receiver) {
|
||||||
return generateBreakOrContinueExpression(expression, false);
|
return generateBreakOrContinueExpression(expression, false, new Label());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private StackValue generateBreakOrContinueExpression(@NotNull JetExpressionWithLabel expression, boolean isBreak) {
|
private StackValue generateBreakOrContinueExpression(
|
||||||
|
@NotNull JetExpressionWithLabel expression,
|
||||||
|
boolean isBreak,
|
||||||
|
@NotNull Label afterBreakContinueLabel
|
||||||
|
) {
|
||||||
assert expression instanceof JetContinueExpression || expression instanceof JetBreakExpression;
|
assert expression instanceof JetContinueExpression || expression instanceof JetBreakExpression;
|
||||||
|
|
||||||
if (!blockStackElements.isEmpty()) {
|
if (!blockStackElements.isEmpty()) {
|
||||||
@@ -1215,7 +1219,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
if (stackElement instanceof FinallyBlockStackElement) {
|
if (stackElement instanceof FinallyBlockStackElement) {
|
||||||
FinallyBlockStackElement finallyBlockStackElement = (FinallyBlockStackElement) stackElement;
|
FinallyBlockStackElement finallyBlockStackElement = (FinallyBlockStackElement) stackElement;
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
genFinallyBlockOrGoto(finallyBlockStackElement, null);
|
genFinallyBlockOrGoto(finallyBlockStackElement, null, afterBreakContinueLabel);
|
||||||
}
|
}
|
||||||
else if (stackElement instanceof LoopBlockStackElement) {
|
else if (stackElement instanceof LoopBlockStackElement) {
|
||||||
LoopBlockStackElement loopBlockStackElement = (LoopBlockStackElement) stackElement;
|
LoopBlockStackElement loopBlockStackElement = (LoopBlockStackElement) stackElement;
|
||||||
@@ -1225,6 +1229,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
loopBlockStackElement.targetLabel != null &&
|
loopBlockStackElement.targetLabel != null &&
|
||||||
labelElement.getReferencedName().equals(loopBlockStackElement.targetLabel.getReferencedName())) {
|
labelElement.getReferencedName().equals(loopBlockStackElement.targetLabel.getReferencedName())) {
|
||||||
v.goTo(isBreak ? loopBlockStackElement.breakLabel : loopBlockStackElement.continueLabel);
|
v.goTo(isBreak ? loopBlockStackElement.breakLabel : loopBlockStackElement.continueLabel);
|
||||||
|
v.mark(afterBreakContinueLabel);
|
||||||
return StackValue.none();
|
return StackValue.none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1233,7 +1238,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockStackElements.pop();
|
blockStackElements.pop();
|
||||||
StackValue result = generateBreakOrContinueExpression(expression, isBreak);
|
StackValue result = generateBreakOrContinueExpression(expression, isBreak, afterBreakContinueLabel);
|
||||||
blockStackElements.push(stackElement);
|
blockStackElements.push(stackElement);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1754,12 +1759,12 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doFinallyOnReturn() {
|
private void doFinallyOnReturn(Label afterReturnLabel) {
|
||||||
if(!blockStackElements.isEmpty()) {
|
if(!blockStackElements.isEmpty()) {
|
||||||
BlockStackElement stackElement = blockStackElements.peek();
|
BlockStackElement stackElement = blockStackElements.peek();
|
||||||
if (stackElement instanceof FinallyBlockStackElement) {
|
if (stackElement instanceof FinallyBlockStackElement) {
|
||||||
FinallyBlockStackElement finallyBlockStackElement = (FinallyBlockStackElement) stackElement;
|
FinallyBlockStackElement finallyBlockStackElement = (FinallyBlockStackElement) stackElement;
|
||||||
genFinallyBlockOrGoto(finallyBlockStackElement, null);
|
genFinallyBlockOrGoto(finallyBlockStackElement, null, afterReturnLabel);
|
||||||
}
|
}
|
||||||
else if (stackElement instanceof LoopBlockStackElement) {
|
else if (stackElement instanceof LoopBlockStackElement) {
|
||||||
|
|
||||||
@@ -1768,7 +1773,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockStackElements.pop();
|
blockStackElements.pop();
|
||||||
doFinallyOnReturn();
|
doFinallyOnReturn(afterReturnLabel);
|
||||||
blockStackElements.push(stackElement);
|
blockStackElements.push(stackElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1784,7 +1789,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
|
|
||||||
private void genFinallyBlockOrGoto(
|
private void genFinallyBlockOrGoto(
|
||||||
@Nullable FinallyBlockStackElement finallyBlockStackElement,
|
@Nullable FinallyBlockStackElement finallyBlockStackElement,
|
||||||
@Nullable Label tryCatchBlockEnd
|
@Nullable Label tryCatchBlockEnd,
|
||||||
|
@Nullable Label afterReturnLabel
|
||||||
) {
|
) {
|
||||||
if (finallyBlockStackElement != null) {
|
if (finallyBlockStackElement != null) {
|
||||||
assert finallyBlockStackElement.gaps.size() % 2 == 0 : "Finally block gaps are inconsistent";
|
assert finallyBlockStackElement.gaps.size() % 2 == 0 : "Finally block gaps are inconsistent";
|
||||||
@@ -1809,8 +1815,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (finallyBlockStackElement != null) {
|
if (finallyBlockStackElement != null) {
|
||||||
Label finallyEnd = new Label();
|
Label finallyEnd = afterReturnLabel != null ? afterReturnLabel : new Label();
|
||||||
v.mark(finallyEnd);
|
if (afterReturnLabel == null) {
|
||||||
|
v.mark(finallyEnd);
|
||||||
|
}
|
||||||
finallyBlockStackElement.addGapLabel(finallyEnd);
|
finallyBlockStackElement.addGapLabel(finallyEnd);
|
||||||
|
|
||||||
blockStackElements.push(finallyBlockStackElement);
|
blockStackElements.push(finallyBlockStackElement);
|
||||||
@@ -1835,28 +1843,30 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
gen(returnedExpression, returnType);
|
gen(returnedExpression, returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateFinallyBlocksIfNeeded(returnType);
|
Label afterReturnLabel = new Label();
|
||||||
|
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel);
|
||||||
|
|
||||||
if (isNonLocalReturn) {
|
if (isNonLocalReturn) {
|
||||||
InlineCodegenUtil.generateGlobalReturnFlag(v, nonLocalReturn.labelName);
|
InlineCodegenUtil.generateGlobalReturnFlag(v, nonLocalReturn.labelName);
|
||||||
}
|
}
|
||||||
v.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
|
v.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
|
||||||
|
v.mark(afterReturnLabel);
|
||||||
|
|
||||||
return StackValue.none();
|
return StackValue.none();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateFinallyBlocksIfNeeded(Type returnType) {
|
public void generateFinallyBlocksIfNeeded(Type returnType, @NotNull Label afterReturnLabel) {
|
||||||
if (hasFinallyBlocks()) {
|
if (hasFinallyBlocks()) {
|
||||||
if (!Type.VOID_TYPE.equals(returnType)) {
|
if (!Type.VOID_TYPE.equals(returnType)) {
|
||||||
int returnValIndex = myFrameMap.enterTemp(returnType);
|
int returnValIndex = myFrameMap.enterTemp(returnType);
|
||||||
StackValue.Local localForReturnValue = StackValue.local(returnValIndex, returnType);
|
StackValue.Local localForReturnValue = StackValue.local(returnValIndex, returnType);
|
||||||
localForReturnValue.store(StackValue.onStack(returnType), v);
|
localForReturnValue.store(StackValue.onStack(returnType), v);
|
||||||
doFinallyOnReturn();
|
doFinallyOnReturn(afterReturnLabel);
|
||||||
localForReturnValue.put(returnType, v);
|
localForReturnValue.put(returnType, v);
|
||||||
myFrameMap.leaveTemp(returnType);
|
myFrameMap.leaveTemp(returnType);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
doFinallyOnReturn();
|
doFinallyOnReturn(afterReturnLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3571,7 +3581,7 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
|
|
||||||
Label end = new Label();
|
Label end = new Label();
|
||||||
|
|
||||||
genFinallyBlockOrGoto(finallyBlockStackElement, end);
|
genFinallyBlockOrGoto(finallyBlockStackElement, end, null);
|
||||||
|
|
||||||
List<JetCatchClause> clauses = expression.getCatchClauses();
|
List<JetCatchClause> clauses = expression.getCatchClauses();
|
||||||
for (int i = 0, size = clauses.size(); i < size; i++) {
|
for (int i = 0, size = clauses.size(); i < size; i++) {
|
||||||
@@ -3601,7 +3611,7 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
v.visitLocalVariable(descriptor.getName().asString(), descriptorType.getDescriptor(), null, clauseStart, clauseEnd,
|
v.visitLocalVariable(descriptor.getName().asString(), descriptorType.getDescriptor(), null, clauseStart, clauseEnd,
|
||||||
index);
|
index);
|
||||||
|
|
||||||
genFinallyBlockOrGoto(finallyBlockStackElement, i != size - 1 || finallyBlock != null ? end : null);
|
genFinallyBlockOrGoto(finallyBlockStackElement, i != size - 1 || finallyBlock != null ? end : null, null);
|
||||||
|
|
||||||
generateExceptionTable(clauseStart, tryBlockRegions, descriptorType.getInternalName());
|
generateExceptionTable(clauseStart, tryBlockRegions, descriptorType.getInternalName());
|
||||||
}
|
}
|
||||||
@@ -3621,7 +3631,7 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
List<Label> defaultCatchRegions = getCurrentCatchIntervals(finallyBlockStackElement, tryStart, defaultCatchEnd);
|
List<Label> defaultCatchRegions = getCurrentCatchIntervals(finallyBlockStackElement, tryStart, defaultCatchEnd);
|
||||||
|
|
||||||
|
|
||||||
genFinallyBlockOrGoto(finallyBlockStackElement, null);
|
genFinallyBlockOrGoto(finallyBlockStackElement, null, null);
|
||||||
|
|
||||||
v.load(savedException, JAVA_THROWABLE_TYPE);
|
v.load(savedException, JAVA_THROWABLE_TYPE);
|
||||||
myFrameMap.leaveTemp(JAVA_THROWABLE_TYPE);
|
myFrameMap.leaveTemp(JAVA_THROWABLE_TYPE);
|
||||||
|
|||||||
@@ -668,7 +668,7 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr);
|
MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr);
|
||||||
if (extension != null) {
|
if (extension != null) {
|
||||||
Label start = new Label();
|
Label start = new Label();
|
||||||
Label end = new Label();
|
//Label end = new Label();
|
||||||
|
|
||||||
MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
|
MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
|
||||||
finallyNode.visitLabel(start);
|
finallyNode.visitLabel(start);
|
||||||
@@ -684,12 +684,12 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
frameMap.enterTemp(Type.INT_TYPE);
|
frameMap.enterTemp(Type.INT_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType);
|
finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, extension.labelNode.getLabel());
|
||||||
finallyNode.visitLabel(end);
|
//finallyNode.visitLabel(end);
|
||||||
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
|
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
|
||||||
InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr);
|
InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr);
|
||||||
|
|
||||||
SimpleInterval splitBy = new SimpleInterval((LabelNode) start.info, (LabelNode) end.info);
|
SimpleInterval splitBy = new SimpleInterval((LabelNode) start.info, extension.labelNode);
|
||||||
processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, false);
|
processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, false);
|
||||||
|
|
||||||
processor.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, false);
|
processor.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, false);
|
||||||
|
|||||||
@@ -699,8 +699,10 @@ public class MethodInliner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//genetate finally block before nonLocalReturn flag/return/goto
|
//genetate finally block before nonLocalReturn flag/return/goto
|
||||||
result.add(new PointForExternalFinallyBlocks(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode())
|
LabelNode label = new LabelNode();
|
||||||
));
|
instructions.insert(insnNode, label);
|
||||||
|
result.add(new PointForExternalFinallyBlocks(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode()),
|
||||||
|
label));
|
||||||
}
|
}
|
||||||
insnNode = insnNode.getNext();
|
insnNode = insnNode.getNext();
|
||||||
}
|
}
|
||||||
@@ -714,9 +716,12 @@ public class MethodInliner {
|
|||||||
|
|
||||||
final Type returnType;
|
final Type returnType;
|
||||||
|
|
||||||
public PointForExternalFinallyBlocks(AbstractInsnNode beforeIns, Type returnType) {
|
final LabelNode labelNode;
|
||||||
|
|
||||||
|
public PointForExternalFinallyBlocks(@NotNull AbstractInsnNode beforeIns, @NotNull Type returnType, @NotNull LabelNode labelNode) {
|
||||||
this.beforeIns = beforeIns;
|
this.beforeIns = beforeIns;
|
||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
|
this.labelNode = labelNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
class A(var value: String)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a = A("start")
|
||||||
|
|
||||||
|
try {
|
||||||
|
test(a)
|
||||||
|
} catch(e: java.lang.RuntimeException) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.value != "start, try, finally1, finally2") return "fail: ${a.value}"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(a: A) : String {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
a.value += ", try"
|
||||||
|
return a.value
|
||||||
|
} finally {
|
||||||
|
a.value += ", finally1"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
a.value += ", finally2"
|
||||||
|
throw RuntimeException("fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
class A(var value: String)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a = A("start")
|
||||||
|
|
||||||
|
try {
|
||||||
|
test(a)
|
||||||
|
} catch(e: java.lang.RuntimeException) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.value != "start, try, finally1, finally2") return "fail: ${a.value}"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(a: A) : String {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
a.value += ", try"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
a.value += ", finally1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
a.value += ", finally2"
|
||||||
|
throw RuntimeException("fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
class A(var value: String)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a = A("start")
|
||||||
|
|
||||||
|
try {
|
||||||
|
test(a)
|
||||||
|
} catch(e: java.lang.RuntimeException) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.value != "start, try, finally1, finally2") return "fail: ${a.value}"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(a: A) : String {
|
||||||
|
while (a.value == "start") {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
a.value += ", try"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
a.value += ", finally1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
a.value += ", finally2"
|
||||||
|
throw RuntimeException("fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "fail"
|
||||||
|
}
|
||||||
+18
@@ -2143,6 +2143,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt8148.kt")
|
||||||
|
public void testKt8148() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt8148.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt8148_break.kt")
|
||||||
|
public void testKt8148_break() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt8148_break.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt8148_continue.kt")
|
||||||
|
public void testKt8148_continue() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt8148_continue.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt870.kt")
|
@TestMetadata("kt870.kt")
|
||||||
public void testKt870() throws Exception {
|
public void testKt870() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt870.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/kt870.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user