diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java index bcca35ce89e..74108942d7b 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -128,8 +128,11 @@ public class JetControlFlowProcessor { } private void visitLabeledExpression(@NotNull String labelName, @NotNull JetExpression labeledExpression) { - enterLabeledElement(labelName, labeledExpression); - value(labeledExpression, false); + JetExpression deparenthesized = JetPsiUtil.deparenthesize(labeledExpression); + if (deparenthesized != null) { + enterLabeledElement(labelName, deparenthesized); + value(labeledExpression, false); + } } @Override @@ -259,22 +262,45 @@ public class JetControlFlowProcessor { @Override public void visitBreakExpression(JetBreakExpression expression) { - JetSimpleNameExpression labelElement = expression.getTargetLabel(); - Label exitPoint = (labelElement != null) - ? builder.getExitPoint(resolveLabel(expression.getLabelName(), expression.getTargetLabel())) - : builder.getExitPoint(builder.getCurrentLoop()); - builder.jump(exitPoint); + JetElement loop = getCorrespondingLoop(expression); + if (loop != null) { + builder.jump(builder.getExitPoint(loop)); + } } @Override public void visitContinueExpression(JetContinueExpression expression) { - JetSimpleNameExpression labelElement = expression.getTargetLabel(); - if (labelElement != null) { - builder.jump(builder.getEntryPoint(resolveLabel(expression.getLabelName(), expression.getTargetLabel()))); + JetElement loop = getCorrespondingLoop(expression); + if (loop != null) { + builder.jump(builder.getEntryPoint(loop)); + } + } + + private JetElement getCorrespondingLoop(JetLabelQualifiedExpression expression) { + String labelName = expression.getLabelName(); + JetElement loop; + if (labelName != null) { + JetSimpleNameExpression targetLabel = expression.getTargetLabel(); + assert targetLabel != null; + loop = resolveLabel(labelName, targetLabel); + if (!isLoop(loop)) { + semanticServices.getErrorHandler().genericError(expression.getNode(), "The label '" + targetLabel.getText() + "' does not denote a loop"); + loop = null; + } } else { - builder.jump(builder.getEntryPoint(builder.getCurrentLoop())); + loop = builder.getCurrentLoop(); + if (loop == null) { + semanticServices.getErrorHandler().genericError(expression.getNode(), "'break' and 'continue' are only allowed inside a loop"); + } } + return loop; + } + + private boolean isLoop(JetElement loop) { + return loop instanceof JetWhileExpression || + loop instanceof JetDoWhileExpression || + loop instanceof JetForExpression; } @Override diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionVisitor.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionVisitor.java index 1841e9ee723..fc4e6c751f5 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionVisitor.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionVisitor.java @@ -50,4 +50,8 @@ public class InstructionVisitor { public void visitInstruction(Instruction instruction) { } + + public void visitSubroutineEnter(SubroutineEnterInstruction instruction) { + visitInstructionWithNext(instruction); + } } 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 4453869d388..32465492f99 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java @@ -121,6 +121,7 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd // subroutineInfo.push(blockInfo); elementToBlockInfo.put(subroutine, blockInfo); bindLabel(entryPoint); + add(new SubroutineEnterInstruction(subroutine)); } @Override diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java index 40f684f1630..0d504ea4e4f 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java @@ -147,8 +147,13 @@ public class Pseudocode { } public void dumpInstructions(@NotNull PrintStream out) { + List locals = new ArrayList(); for (int i = 0, instructionsSize = instructions.size(); i < instructionsSize; i++) { Instruction instruction = instructions.get(i); + if (instruction instanceof FunctionLiteralValueInstruction) { + FunctionLiteralValueInstruction functionLiteralValueInstruction = (FunctionLiteralValueInstruction) instruction; + locals.add(functionLiteralValueInstruction.getBody()); + } for (PseudocodeLabel label: labels) { if (label.getTargetInstructionIndex() == i) { out.println(label.getName() + ":"); @@ -156,6 +161,9 @@ public class Pseudocode { } out.println(" " + instruction); } + for (Pseudocode local : locals) { + local.dumpInstructions(out); + } } public void dumpGraph(@NotNull final PrintStream out) { @@ -191,6 +199,9 @@ public class Pseudocode { else if (node instanceof FunctionLiteralValueInstruction) { shape = "Mcircle"; } + else if (node instanceof SubroutineEnterInstruction || node instanceof SubroutineExitInstruction) { + shape = "roundrect, style=rounded"; + } out.println(name + "[label=\"" + text + "\", shape=" + shape + "];"); } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/SubroutineEnterInstruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/SubroutineEnterInstruction.java new file mode 100644 index 00000000000..067d6344563 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/SubroutineEnterInstruction.java @@ -0,0 +1,30 @@ +package org.jetbrains.jet.lang.cfg.pseudocode; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetElement; + +/** + * @author abreslav + */ +public class SubroutineEnterInstruction extends InstructionWithNext { + private final JetElement subroutine; + + public SubroutineEnterInstruction(@NotNull JetElement subroutine) { + super(subroutine); + this.subroutine = subroutine; + } + + public JetElement getSubroutine() { + return subroutine; + } + + @Override + public void accept(InstructionVisitor visitor) { + visitor.visitSubroutineEnter(this); + } + + @Override + public String toString() { + return ""; + } +} diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java b/idea/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java new file mode 100644 index 00000000000..c4c98193ea2 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java @@ -0,0 +1,18 @@ +package org.jetbrains.jet.lang.psi; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author abreslav + */ +public class JetPsiUtil { + @Nullable + public static JetExpression deparenthesize(@NotNull JetExpression expression) { + JetExpression result = expression; + while (result instanceof JetParenthesizedExpression) { + result = ((JetParenthesizedExpression) expression).getExpression(); + } + return result; + } +} diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index 8424d2ead48..a40150373e9 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -2,9 +2,14 @@ package org.jetbrains.jet.lang.resolve; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.cfg.JetControlFlowProcessor; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowInstructionsGenerator; +import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.*; +import java.io.FileNotFoundException; +import java.io.PrintStream; import java.util.*; /** @@ -225,21 +230,21 @@ public class TopDownAnalyzer { declaringScope.addFunctionDescriptor(descriptor); functions.put(function, descriptor); -// JetExpression bodyExpression = function.getBodyExpression(); -// if (bodyExpression != null) { -// System.out.println("-------------"); -// JetControlFlowInstructionsGenerator instructionsGenerator = new JetControlFlowInstructionsGenerator(function); -// new JetControlFlowProcessor(semanticServices, trace, instructionsGenerator).generate(function, bodyExpression); -// Pseudocode pseudocode = instructionsGenerator.getPseudocode(); -// pseudocode.postProcess(); -// pseudocode.dumpInstructions(System.out); -// System.out.println("-------------"); -// try { -// pseudocode.dumpGraph(new PrintStream("/Users/abreslav/work/cfg.dot")); -// } catch (FileNotFoundException e) { -// e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. -// } -// } + JetExpression bodyExpression = function.getBodyExpression(); + if (bodyExpression != null) { + System.out.println("-------------"); + JetControlFlowInstructionsGenerator instructionsGenerator = new JetControlFlowInstructionsGenerator(function); + new JetControlFlowProcessor(semanticServices, trace, instructionsGenerator).generate(function, bodyExpression); + Pseudocode pseudocode = instructionsGenerator.getPseudocode(); + pseudocode.postProcess(); + pseudocode.dumpInstructions(System.out); + System.out.println("-------------"); + try { + pseudocode.dumpGraph(new PrintStream("/Users/abreslav/work/cfg.dot")); + } catch (FileNotFoundException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } } private void processProperty(WritableScope declaringScope, JetProperty property) { diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 25cc86b6b2d..1cd31571da2 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -86,15 +86,6 @@ public class JetTypeInferrer { return new TypeInferrerVisitor(scope, preferBlock).getType(expression); } - @Nullable - private static JetExpression deparenthesize(@NotNull JetExpression expression) { - JetExpression result = expression; - while (result instanceof JetParenthesizedExpression) { - result = ((JetParenthesizedExpression) expression).getExpression(); - } - return result; - } - @Nullable private List getTypes(JetScope scope, List indexExpressions) { List argumentTypes = new ArrayList(); @@ -1318,7 +1309,7 @@ public class JetTypeInferrer { @Override protected void visitAssignment(JetBinaryExpression expression) { JetExpression left = expression.getLeft(); - JetExpression deparenthesized = deparenthesize(left); + JetExpression deparenthesized = JetPsiUtil.deparenthesize(left); JetExpression right = expression.getRight(); if (deparenthesized instanceof JetArrayAccessExpression) { JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) deparenthesized; diff --git a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java index 70b1519da38..fb257b919d0 100644 --- a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java @@ -29,6 +29,6 @@ public class JetPsiCheckerTest extends LightDaemonAnalyzerTestCase { } public void testQualifiedThis() throws Exception { -// doTest("/checker/QualifiedThis.jet", true, true); + doTest("/checker/QualifiedThis.jet", true, true); } }