diff --git a/idea/src/org/jetbrains/jet/lang/cfg/GenerationTrigger.java b/idea/src/org/jetbrains/jet/lang/cfg/GenerationTrigger.java new file mode 100644 index 00000000000..06c03e1da91 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/cfg/GenerationTrigger.java @@ -0,0 +1,8 @@ +package org.jetbrains.jet.lang.cfg; + +/** + * @author abreslav + */ +public interface GenerationTrigger { + void generate(); +} diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java index 64f40a198cd..8f5867c01bb 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java @@ -2,7 +2,6 @@ package org.jetbrains.jet.lang.cfg; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.psi.JetBlockExpression; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetExpression; @@ -37,7 +36,7 @@ public interface JetControlFlowBuilder { @Nullable JetElement getCurrentLoop(); // Finally - void enterTryFinally(@NotNull JetBlockExpression expression); + void enterTryFinally(@NotNull GenerationTrigger trigger); void exitTryFinally(); // Subroutines diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java index 626b584c9ca..c0ff7133c8d 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java @@ -2,7 +2,6 @@ package org.jetbrains.jet.lang.cfg; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.psi.JetBlockExpression; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetExpression; @@ -84,8 +83,8 @@ public class JetControlFlowBuilderAdapter implements JetControlFlowBuilder { } @Override - public void enterTryFinally(@NotNull JetBlockExpression expression) { - builder.enterTryFinally(expression); + public void enterTryFinally(@NotNull GenerationTrigger trigger) { + builder.enterTryFinally(trigger); } @Override diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java index ea44db9c24a..23960868aae 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -272,25 +272,26 @@ public class JetControlFlowProcessor { @Override public void visitTryExpression(JetTryExpression expression) { - JetFinallySection finallyBlock = expression.getFinallyBlock(); + final JetFinallySection finallyBlock = expression.getFinallyBlock(); if (finallyBlock != null) { - builder.enterTryFinally(finallyBlock.getFinalExpression()); + builder.enterTryFinally(new GenerationTrigger() { + @Override + public void generate() { + value(finallyBlock.getFinalExpression(), true, inCondition); + } + }); } + Label onException = builder.createUnboundLabel(); + builder.nondeterministicJump(onException); + value(expression.getTryBlock(), true, inCondition); + List catchClauses = expression.getCatchClauses(); - if (catchClauses.isEmpty()) { - value(expression.getTryBlock(), true, false); - } - else { - Label catchBlock = builder.createUnboundLabel(); - builder.nondeterministicJump(catchBlock); - - value(expression.getTryBlock(), true, false); - + if (!catchClauses.isEmpty()) { Label afterCatches = builder.createUnboundLabel(); builder.jump(afterCatches); - builder.bindLabel(catchBlock); + builder.bindLabel(onException); for (Iterator iterator = catchClauses.iterator(); iterator.hasNext(); ) { JetCatchClause catchClause = iterator.next(); JetExpression catchBody = catchClause.getCatchBody(); @@ -303,10 +304,13 @@ public class JetControlFlowProcessor { } builder.bindLabel(afterCatches); + } else { + builder.bindLabel(onException); } if (finallyBlock != null) { builder.exitTryFinally(); + value(finallyBlock.getFinalExpression(), true, inCondition); } } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java index 61511da0f48..181e16fbb33 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java @@ -1,10 +1,10 @@ package org.jetbrains.jet.lang.cfg.pseudocode; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.cfg.GenerationTrigger; import org.jetbrains.jet.lang.cfg.JetControlFlowBuilder; import org.jetbrains.jet.lang.cfg.JetControlFlowBuilderAdapter; import org.jetbrains.jet.lang.cfg.Label; -import org.jetbrains.jet.lang.psi.JetBlockExpression; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression; @@ -16,11 +16,14 @@ import java.util.*; */ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAdapter { - private final Stack loopInfo = new Stack(); - private final Map elementToBlockInfo = new HashMap(); + private final Stack loopInfo = new Stack(); + private final Map elementToBlockInfo = new HashMap(); private int labelCount = 0; private final Stack builders = new Stack(); + + private final Stack allBlocks = new Stack(); + private final JetPseudocodeTrace trace; public JetControlFlowInstructionsGenerator(JetPseudocodeTrace trace) { @@ -94,16 +97,18 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd public final Label enterLoop(@NotNull JetExpression expression, Label loopExitPoint) { Label label = createUnboundLabel(); bindLabel(label); - BlockInfo blockInfo = new BlockInfo(expression, label, loopExitPoint); + BreakableBlockInfo blockInfo = new BreakableBlockInfo(expression, label, loopExitPoint); loopInfo.push(blockInfo); elementToBlockInfo.put(expression, blockInfo); + allBlocks.push(blockInfo); return label; } @Override public final void exitLoop(@NotNull JetExpression expression) { - BlockInfo info = loopInfo.pop(); + BreakableBlockInfo info = loopInfo.pop(); elementToBlockInfo.remove(expression); + allBlocks.pop(); bindLabel(info.getExitPoint()); } @@ -115,9 +120,10 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd @Override public void enterSubroutine(@NotNull JetElement subroutine, boolean isFunctionLiteral) { Label entryPoint = createUnboundLabel(); - BlockInfo blockInfo = new BlockInfo(subroutine, entryPoint, createUnboundLabel()); + BreakableBlockInfo blockInfo = new BreakableBlockInfo(subroutine, entryPoint, createUnboundLabel()); // subroutineInfo.push(blockInfo); elementToBlockInfo.put(subroutine, blockInfo); + allBlocks.push(blockInfo); bindLabel(entryPoint); add(new SubroutineEnterInstruction(subroutine)); } @@ -134,29 +140,54 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd @Override public Label getExitPoint(@NotNull JetElement labelElement) { - BlockInfo blockInfo = elementToBlockInfo.get(labelElement); + BreakableBlockInfo blockInfo = elementToBlockInfo.get(labelElement); assert blockInfo != null : labelElement.getText(); return blockInfo.getExitPoint(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + private void handleJumpInsideTryFinally(Label jumpTarget) { + List finallyBlocks = new ArrayList(); + + for (int i = allBlocks.size() - 1; i >= 0; i--) { + BlockInfo blockInfo = allBlocks.get(i); + if (blockInfo instanceof BreakableBlockInfo) { + BreakableBlockInfo breakableBlockInfo = (BreakableBlockInfo) blockInfo; + if (jumpTarget == breakableBlockInfo.getExitPoint() || jumpTarget == breakableBlockInfo.getEntryPoint()) { + for (int j = finallyBlocks.size() - 1; j >= 0; j--) { + finallyBlocks.get(j).generateFinallyBlock(); + } + break; + } + } + else if (blockInfo instanceof TryFinallyBlockInfo) { + TryFinallyBlockInfo tryFinallyBlockInfo = (TryFinallyBlockInfo) blockInfo; + finallyBlocks.add(tryFinallyBlockInfo); + } + } + } + @Override public void exitSubroutine(@NotNull JetElement subroutine, boolean functionLiteral) { bindLabel(getExitPoint(subroutine)); add(new SubroutineExitInstruction(subroutine)); elementToBlockInfo.remove(subroutine); -// subroutineInfo.pop(); + allBlocks.pop(); } @Override public void returnValue(@NotNull JetExpression returnExpression, @NotNull JetElement subroutine) { - add(new ReturnValueInstruction(returnExpression, getExitPoint(subroutine))); + Label exitPoint = getExitPoint(subroutine); + handleJumpInsideTryFinally(exitPoint); + add(new ReturnValueInstruction(returnExpression, exitPoint)); } @Override public void returnNoValue(@NotNull JetElement returnExpression, @NotNull JetElement subroutine) { - add(new ReturnNoValueInstruction(returnExpression, getExitPoint(subroutine))); + Label exitPoint = getExitPoint(subroutine); + handleJumpInsideTryFinally(exitPoint); + add(new ReturnNoValueInstruction(returnExpression, exitPoint)); } @Override @@ -176,16 +207,19 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd @Override public void jump(@NotNull Label label) { + handleJumpInsideTryFinally(label); add(new UnconditionalJumpInstruction(label)); } @Override public void jumpOnFalse(@NotNull Label label) { + handleJumpInsideTryFinally(label); add(new ConditionalJumpInstruction(false, label)); } @Override public void jumpOnTrue(@NotNull Label label) { + handleJumpInsideTryFinally(label); add(new ConditionalJumpInstruction(true, label)); } @@ -196,17 +230,19 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd @Override public void nondeterministicJump(Label label) { + handleJumpInsideTryFinally(label); add(new NondeterministicJumpInstruction(label)); } @Override - public void enterTryFinally(@NotNull JetBlockExpression expression) { - throw new UnsupportedOperationException(); // TODO + public void enterTryFinally(@NotNull GenerationTrigger generationTrigger) { + allBlocks.push(new TryFinallyBlockInfo(generationTrigger)); } @Override public void exitTryFinally() { - throw new UnsupportedOperationException(); // TODO + BlockInfo pop = allBlocks.pop(); + assert pop instanceof TryFinallyBlockInfo; } @Override @@ -215,12 +251,26 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd } } - private static class BlockInfo { + private static abstract class BlockInfo {} + + private static class TryFinallyBlockInfo extends BlockInfo { + private final GenerationTrigger finallyBlock; + + private TryFinallyBlockInfo(GenerationTrigger finallyBlock) { + this.finallyBlock = finallyBlock; + } + + public void generateFinallyBlock() { + finallyBlock.generate(); + } + } + + private static class BreakableBlockInfo extends BlockInfo { private final JetElement element; private final Label entryPoint; private final Label exitPoint; - private BlockInfo(JetElement element, Label entryPoint, Label exitPoint) { + private BreakableBlockInfo(JetElement element, Label entryPoint, Label exitPoint) { this.element = element; this.entryPoint = entryPoint; this.exitPoint = exitPoint; diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index 9d91b53f8d2..3c14d0047f7 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -1238,7 +1238,9 @@ public class JetExpressionParsing extends AbstractJetParsing { myJetParsing.parseBlock(); + boolean catchOrFinally = false; while (at(CATCH_KEYWORD)) { + catchOrFinally = true; PsiBuilder.Marker catchBlock = mark(); advance(); // CATCH_KEYWORD @@ -1249,6 +1251,7 @@ public class JetExpressionParsing extends AbstractJetParsing { } if (at(FINALLY_KEYWORD)) { + catchOrFinally = true; PsiBuilder.Marker finallyBlock = mark(); advance(); // FINALLY_KEYWORD @@ -1258,6 +1261,10 @@ public class JetExpressionParsing extends AbstractJetParsing { finallyBlock.done(FINALLY); } + if (!catchOrFinally) { + error("Expecting 'catch' or 'finally'"); + } + tryExpression.done(TRY); } diff --git a/idea/testData/cfg/Finally.instructions b/idea/testData/cfg/Finally.instructions new file mode 100644 index 00000000000..e118850b4b6 --- /dev/null +++ b/idea/testData/cfg/Finally.instructions @@ -0,0 +1,437 @@ +== t3 == +fun t3() { + try { + 1 + } finally { + 2 + } +} +--------------------- +l0: + + jmp?(l2) + r(1) +l2: + r(2) +l1: + +===================== +== t3 == +fun t3() { + try { + 1 + if (2 > 3) { + return + } + } finally { + 2 + } +} +--------------------- +l0: + + jmp?(l2) + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l3) + r(2) + ret l1 + jmp(l4) +l3: + read (Unit) +l2: +l4: + r(2) +l1: + +===================== +== anonymous_0 == +{ () => + if (2 > 3) { + return@ + } + } +--------------------- +l3: + + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + ret l4 + jmp(l6) +l5: + read (Unit) +l4: +l6: + +===================== +== t3 == +fun t3() { + try { + 1 + @{ () => + if (2 > 3) { + return@ + } + } + } finally { + 2 + } +} +--------------------- +l0: + + jmp?(l2) + r(1) + rf({ () => + if (2 > 3) { + return@ + } + }) +l2: + r(2) +l1: + +l3: + + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + ret l4 + jmp(l6) +l5: + read (Unit) +l4: +l6: + +===================== +== anonymous_1 == +{ () => + try { + 1 + if (2 > 3) { + return@ + } + } finally { + 2 + } + } +--------------------- +l2: + + jmp?(l4) + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + r(2) + ret l3 + jmp(l6) +l5: + read (Unit) +l4: +l6: + r(2) +l3: + +===================== +== t3 == +fun t3() { + @{ () => + try { + 1 + if (2 > 3) { + return@ + } + } finally { + 2 + } + } +} +--------------------- +l0: + + rf({ () => + try { + 1 + if (2 > 3) { + return@ + } + } finally { + 2 + } + }) +l1: + +l2: + + jmp?(l4) + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + r(2) + ret l3 + jmp(l6) +l5: + read (Unit) +l4: +l6: + r(2) +l3: + +===================== +== t3 == +fun t3() { + @ while(true) { + try { + 1 + if (2 > 3) { + break @ + } + } finally { + 2 + } + } +} +--------------------- +l0: + +l3: + r(true) + jf(l2) + jmp?(l4) + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + r(2) + jmp(l2) + jmp(l6) +l5: + read (Unit) +l4: +l6: + r(2) + jmp(l3) +l1: +l2: + +===================== +== t3 == +fun t3() { + try { + @ while(true) { + 1 + if (2 > 3) { + break @ + } + } + 5 + } finally { + 2 + } +} +--------------------- +l0: + + jmp?(l2) +l4: + r(true) + jf(l3) + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + jmp(l3) + jmp(l6) +l5: + read (Unit) +l6: + jmp(l4) +l3: + r(5) +l2: + r(2) +l1: + +===================== +== t3 == +fun t3() { + try { + @ while(true) { + 1 + if (2 > 3) { + break @ + } + } + } finally { + 2 + } +} +--------------------- +l0: + + jmp?(l2) +l4: + r(true) + jf(l3) + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + jmp(l3) + jmp(l6) +l5: + read (Unit) +l6: + jmp(l4) +l2: +l3: + r(2) +l1: + +===================== +== t3 == +fun t3(a : Int) { + @ for (i in 1..a) { + try { + 1 + if (2 > 3) { + continue @ + } + } finally { + 2 + } + } +} +--------------------- +l0: + + r(1) + r(a) + r(..) + r(1..a) + jmp?(l2) +l3: + jmp?(l4) + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + r(2) + jmp(l3) + jmp(l6) +l5: + read (Unit) +l4: +l6: + r(2) + jmp?(l3) +l1: +l2: + +===================== +== t3 == +fun t3(a : Int) { + try { + @ for (i in 1..a) { + 1 + if (2 > 3) { + continue @ + } + } + 5 + } finally { + 2 + } +} +--------------------- +l0: + + jmp?(l2) + r(1) + r(a) + r(..) + r(1..a) + jmp?(l3) +l4: + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + jmp(l4) + jmp(l6) +l5: + read (Unit) +l6: + jmp?(l4) +l3: + r(5) +l2: + r(2) +l1: + +===================== +== t3 == +fun t3(a : Int) { + try { + @ for (i in 1..a) { + 1 + if (2 > 3) { + continue @ + } + } + } finally { + 2 + } +} +--------------------- +l0: + + jmp?(l2) + r(1) + r(a) + r(..) + r(1..a) + jmp?(l3) +l4: + r(1) + r(2) + r(3) + r(>) + r(2 > 3) + jf(l5) + jmp(l4) + jmp(l6) +l5: + read (Unit) +l6: + jmp?(l4) +l2: +l3: + r(2) +l1: + +===================== diff --git a/idea/testData/cfg/Finally.jet b/idea/testData/cfg/Finally.jet new file mode 100644 index 00000000000..76296e3f616 --- /dev/null +++ b/idea/testData/cfg/Finally.jet @@ -0,0 +1,125 @@ +fun t3() { + try { + 1 + } finally { + 2 + } +} + +fun t3() { + try { + 1 + if (2 > 3) { + return + } + } finally { + 2 + } +} + +fun t3() { + try { + 1 + @{ () => + if (2 > 3) { + return@ + } + } + } finally { + 2 + } +} + +fun t3() { + @{ () => + try { + 1 + if (2 > 3) { + return@ + } + } finally { + 2 + } + } +} + +fun t3() { + @ while(true) { + try { + 1 + if (2 > 3) { + break @ + } + } finally { + 2 + } + } +} + +fun t3() { + try { + @ while(true) { + 1 + if (2 > 3) { + break @ + } + } + 5 + } finally { + 2 + } +} + +fun t3() { + try { + @ while(true) { + 1 + if (2 > 3) { + break @ + } + } + } finally { + 2 + } +} + +fun t3(a : Int) { + @ for (i in 1..a) { + try { + 1 + if (2 > 3) { + continue @ + } + } finally { + 2 + } + } +} + +fun t3(a : Int) { + try { + @ for (i in 1..a) { + 1 + if (2 > 3) { + continue @ + } + } + 5 + } finally { + 2 + } +} + +fun t3(a : Int) { + try { + @ for (i in 1..a) { + 1 + if (2 > 3) { + continue @ + } + } + } finally { + 2 + } +} + diff --git a/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java b/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java index 34b89515c3b..9d576b1c191 100644 --- a/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java +++ b/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java @@ -68,11 +68,14 @@ public class JetControlFlowTest extends JetTestCaseBase { try { processCFData(name, data); + } + catch (IOException e) { + throw new RuntimeException(e); + } + finally { if ("true".equals(System.getProperty("jet.control.flow.test.dump.graphs"))) { dumpDot(name, data.values()); } - } catch (IOException e) { - throw new RuntimeException(e); } }