Working on CFG building

This commit is contained in:
Andrey Breslav
2011-03-31 20:50:16 +04:00
parent ad7115ce20
commit 5888ac5a60
27 changed files with 1454 additions and 52 deletions
+2
View File
@@ -3,7 +3,9 @@
<words>
<w>accessor</w>
<w>inferrer</w>
<w>nondeterministic</w>
<w>nullable</w>
<w>pseudocode</w>
<w>substitutor</w>
<w>subtyping</w>
<w>supertype</w>
@@ -149,9 +149,9 @@ public class ExpressionCodegen extends JetVisitor {
@Override
public void visitBreakExpression(JetBreakExpression expression) {
String labelName = expression.getLabelName();
JetSimpleNameExpression labelElement = expression.getTargetLabel();
Label label = labelName == null ? myLoopEnds.peek() : null; // TODO:
Label label = labelElement == null ? myLoopEnds.peek() : null; // TODO:
v.goTo(label);
}
@@ -3,13 +3,15 @@
*/
package org.jetbrains.jet.lang.annotations;
import com.intellij.lang.ASTNode;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.JetHighlighter;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.psi.JetLabelQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetPrefixExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.psi.JetVisitor;
import org.jetbrains.jet.lexer.JetTokens;
public class LabelsAnnotator implements Annotator {
@@ -25,9 +27,9 @@ public class LabelsAnnotator implements Annotator {
@Override
public void visitLabelQualifiedExpression(JetLabelQualifiedExpression expression) {
ASTNode targetLabelNode = expression.getTargetLabelNode();
if (targetLabelNode != null) {
holder.createInfoAnnotation(targetLabelNode, null).setTextAttributes(JetHighlighter.JET_LABEL_IDENTIFIER);
JetSimpleNameExpression targetLabel = expression.getTargetLabel();
if (targetLabel != null) {
holder.createInfoAnnotation(targetLabel, null).setTextAttributes(JetHighlighter.JET_LABEL_IDENTIFIER);
}
}
@@ -0,0 +1,54 @@
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;
/**
* @author abreslav
*/
public interface JetControlFlowBuilder {
void readNode(@NotNull JetExpression expression);
// General label management
@NotNull
Label createUnboundLabel();
void bindLabel(@NotNull Label label);
// Jumps
void jump(@NotNull Label label);
void jumpOnFalse(@NotNull Label label);
void jumpOnTrue(@NotNull Label label);
void nondeterministicJump(Label label); // Maybe, jump to label
// Entry/exit points
Label getEntryPoint(@NotNull JetElement labelElement);
Label getExitPoint(@NotNull JetElement labelElement);
// Loops
Label enterLoop(@NotNull JetExpression expression, Label loopExitPoint);
void exitLoop(@NotNull JetExpression expression);
@Nullable
JetElement getCurrentLoop();
// Finally
void pushFinally(@NotNull JetBlockExpression expression);
void popFinally();
// Subroutines
void enterSubroutine(@NotNull JetElement subroutine);
void exitSubroutine(@NotNull JetElement subroutine);
@Nullable
JetElement getCurrentSubroutine();
void returnValue(@NotNull JetElement subroutine);
void returnNoValue(@NotNull JetElement subroutine);
// Other
void unsupported(JetElement element);
}
@@ -0,0 +1,348 @@
package org.jetbrains.jet.lang.cfg;
/**
* @author abreslav
*/
//public class JetControlFlowGraphBuilder extends AbstractControlFlowBuilder {
//
// private final List<CFGNode> nodes = new ArrayList<CFGNode>();
//
// private final List<CFGEdge> edges = new ArrayList<CFGEdge>();
// private final Map<Label, CFGNode> labelsToNodes = new HashMap<Label, CFGNode>();
// // toLabel -> edge
// private final Map<Label, List<CFGEdge>> pendingEdges = new HashMap<Label, List<CFGEdge>>();
//
// private List<Label> labelsToBeBound = new ArrayList<Label>();
//
// private CFGNode currentNode;
//
// public JetControlFlowGraphBuilder(JetExpression block) {
//// currentNode = createNode(block);
// }
//
// public void build(Pseudocode pseudocode) {
// List<Instruction> instructions = pseudocode.getInstructions();
// Map<Label, Integer> labels = pseudocode.getLabels();
//
//
// }
//
// @Override
// public void exitSubroutine(JetElement element) {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @NotNull
// private CFGNode closest(List<CFGNode> nodes, int index) {
// int size = nodes.size();
// for (int i = index; i < size; i++) {
// CFGNode cfgNode = nodes.get(i);
// if (cfgNode != null) return cfgNode;
// }
// return null;
// }
//
// public void dumpGraph(PrintStream out) {
// out.println("digraph g {");
//
// Map<CFGNode, String> nodeToName = new HashMap<CFGNode, String>();
// int count = 0;
// for (CFGNode node : nodes) {
// String name = "n" + count++;
// nodeToName.put(node, name);
// String text = node.getElement().getText();
// int newline = text.indexOf("\n");
// if (newline >= 0) {
// text = text.substring(0, newline);
// }
// String shape = node instanceof CFGConditionNode ? "diamond" : "box";
// out.println(name + "[label=\"" + text + "\", shape=" + shape + "];");
// }
//
// for (CFGEdge edge : edges) {
// String from = nodeToName.get(edge.getFrom());
// String to = nodeToName.get(edge.getTo());
//
// String label = edge.getDebugLabel();
// if (label != null) {
// label = "[label=\"" + label + "\"]";
// }
// else {
// label = "";
// }
// out.println(from + " -> " + to + label + ";");
// }
// out.println("}");
// out.close();
// }
//
// private CFGNode createNode(JetExpression block) {
// CFGNode node = new CFGReadNode(block);
// afterNodeCreation(node);
// return node;
// }
//
// private void afterNodeCreation(CFGNode node) {
// nodes.add(node);
// bindLabelIfNeeded(node);
//
// if (currentNode != null) {
// createEdgeIfNeeded(currentNode, node);
// }
//
// currentNode = node;
// }
//
// @Nullable
// private CFGEdge createEdgeIfNeeded(@NotNull CFGNode from, CFGNode to) {
// CFGEdge edge = new CFGEdge(from, to);
// if (from instanceof CFGConditionNode) {
// CFGConditionNode conditionNode = (CFGConditionNode) from;
// if (conditionNode.getElseEdge() == null) {
// conditionNode.setElseEdge(edge);
// }
// else if (conditionNode.getThenEdge() == null) {
// conditionNode.setThenEdge(edge);
// }
// else {
// return null;
// }
// }
// else if (from instanceof CFGReadNode) {
// CFGReadNode readNode = (CFGReadNode) from;
// if (readNode.getOutgoingEdge() == null) {
// readNode.setOutgoingEdge(edge);
// }
// else {
// return readNode.getOutgoingEdge();
// }
// }
// edges.add(edge);
// return edge;
// }
//
// private void bindLabelIfNeeded(CFGNode node) {
// for (Label label : labelsToBeBound) {
// labelsToNodes.put(label, node);
// List<CFGEdge> edges = pendingEdges.get(label);
// if (edges != null) {
// for (CFGEdge edge : edges) {
// edge.setTo(node);
// }
// }
// }
// labelsToBeBound.clear();
// }
//
// @Nullable
// private CFGEdge createEdgeToLabel(CFGNode fromNode, Label toLabel) {
// CFGNode toNode = labelsToNodes.get(toLabel);
// if (toNode != null) {
// return createEdgeIfNeeded(fromNode, toNode);
// }
// else {
// List<CFGEdge> edges = pendingEdges.get(toLabel);
// if (edges == null) {
// edges = new ArrayList<CFGEdge>();
// pendingEdges.put(toLabel, edges);
// }
// CFGEdge edge = createEdgeIfNeeded(fromNode, null);
// if (edge != null) {
// edges.add(edge);
// }
// return edge;
// }
// }
//
// @Override
// public void readNode(JetExpression expression) {
// createNode(expression);
// }
//
// @Override
// public void jump(Label label) {
// CFGNode target = labelsToNodes.get(label);
// if (target != null) {
// createEdgeIfNeeded(currentNode, target);
// }
// else {
// createEdgeToLabel(currentNode, label);
// }
// }
//
// @Override
// public void bindLabel(Label label) {
// assert !labelsToNodes.containsKey(label) : label;
// labelsToBeBound.add(label);
// }
//
// @Override
// public void jumpOnFalse(Label label) {
// CFGConditionNode conditionNode = new CFGConditionNode(currentNode.getElement());
// afterNodeCreation(conditionNode);
// conditionNode.setElseEdge(createEdgeToLabel(conditionNode, label));
// }
//
// @Override
// public void jumpOnTrue(Label label) {
// CFGConditionNode conditionNode = new CFGConditionNode(currentNode.getElement());
// afterNodeCreation(conditionNode);
// conditionNode.setThenEdge(createEdgeToLabel(conditionNode, label));
// }
//
// @Override
// public void createBoundLabel(JetSimpleNameExpression labelElement, JetExpression labeledExpression) {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public Label getEntryPoint(JetSimpleNameExpression labelElement) {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public void jumpToLoopExitPoint(JetSimpleNameExpression labelElement) {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public Label getCurrentSubroutineExitPoint() {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public void returnValue(Label label) {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public void nondeterministicJump(Label firstBranch, Label secondBranch) {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public void popFinally() {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public void popCatchClauses() {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public void pushCatchClauses(List<JetCatchClause> catchClauses) {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public void pushFinally(JetBlockExpression expression) {
// throw new UnsupportedOperationException(); // TODO
// }
//
// @Override
// public void unsupported(JetElement element) {
// throw new IllegalStateException("Unsupported element: " + element.getText() + " " + element);
// }
//
//}
//
//abstract class CFGNode {
// private JetElement element;
//
// public CFGNode(JetElement element) {
// this.element = element;
// }
//
// @NotNull
// public JetElement getElement() {
// return element;
// }
//
// public void setElement(JetElement element) {
// this.element = element;
// }
//}
//
//class CFGReadNode extends CFGNode {
// private CFGEdge outgoingEdge;
//
// public CFGReadNode(JetElement element) {
// super(element);
// }
//
// public CFGEdge getOutgoingEdge() {
// return outgoingEdge;
// }
//
// public void setOutgoingEdge(CFGEdge outgoingEdge) {
// this.outgoingEdge = outgoingEdge;
// }
//}
//
//class CFGConditionNode extends CFGNode {
//
// private CFGEdge thenEdge;
// private CFGEdge elseEdge;
//
// public CFGConditionNode(JetElement element) {
// super(element);
// }
//
// public CFGConditionNode() {
// super(null);
// }
//
// public CFGEdge getThenEdge() {
// return thenEdge;
// }
//
// public void setThenEdge(CFGEdge thenEdge) {
// this.thenEdge = thenEdge;
// thenEdge.setDebugLabel("yes");
// }
//
// public CFGEdge getElseEdge() {
// return elseEdge;
// }
//
// public void setElseEdge(CFGEdge elseEdge) {
// this.elseEdge = elseEdge;
// elseEdge.setDebugLabel("no");
// }
//}
//
//class CFGEdge {
// private CFGNode from;
// private CFGNode to;
// private String debugLabel = null;
//
// public CFGEdge(CFGNode from, CFGNode to) {
// this.from = from;
// this.to = to;
// }
//
// public CFGNode getFrom() {
// return from;
// }
//
// public void setFrom(CFGNode from) {
// this.from = from;
// }
//
// public CFGNode getTo() {
// return to;
// }
//
// public void setTo(CFGNode to) {
// this.to = to;
// }
//
// public String getDebugLabel() {
// return debugLabel;
// }
//
// public void setDebugLabel(String debugLabel) {
// this.debugLabel = debugLabel;
// }
//}
@@ -0,0 +1,250 @@
package org.jetbrains.jet.lang.cfg;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collections;
import java.util.List;
/**
* @author abreslav
*/
public class JetControlFlowProcessor {
private JetControlFlowBuilder builder;
public JetControlFlowProcessor(JetControlFlowBuilder builder) {
this.builder = builder;
}
public void generate(@NotNull JetElement subroutineElement, @NotNull JetExpression body) {
generateSubroutineControlFlow(subroutineElement, Collections.singletonList(body), false);
}
public void generateSubroutineControlFlow(@NotNull JetElement subroutineElement, @NotNull List<? extends JetElement> body, boolean preferBlocks) {
builder.enterSubroutine(subroutineElement);
for (JetElement statement : body) {
statement.accept(new CFPVisitor(preferBlocks));
}
builder.exitSubroutine(subroutineElement);
}
private void registerLabeledElement(@NotNull JetSimpleNameExpression label, @NotNull JetExpression labeledExpression) {
throw new UnsupportedOperationException(); // TODO
}
private JetElement resolveLabel(JetSimpleNameExpression labelElement) {
throw new UnsupportedOperationException(); // TODO
}
private class CFPVisitor extends JetVisitor {
private final boolean preferBlock;
private CFPVisitor(boolean preferBlock) {
this.preferBlock = preferBlock;
}
private void value(@NotNull JetElement element, boolean preferBlock) {
CFPVisitor visitor;
if (this.preferBlock == preferBlock) {
visitor = this;
}
else {
visitor = new CFPVisitor(preferBlock);
}
element.accept(visitor);
}
@Override
public void visitConstantExpression(JetConstantExpression expression) {
builder.readNode(expression);
}
@Override
public void visitSimpleNameExpression(JetSimpleNameExpression expression) {
builder.readNode(expression);
}
@Override
public void visitLabelQualifiedExpression(JetLabelQualifiedExpression expression) {
registerLabeledElement(expression.getTargetLabel(), expression.getLabeledExpression());
value(expression.getLabeledExpression(), false);
}
@Override
public void visitFunction(JetFunction function) {
generate(function, function.getBodyExpression());
}
@Override
public void visitBinaryExpression(JetBinaryExpression expression) {
IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
if (operationType == JetTokens.ANDAND) {
value(expression.getLeft(), false);
Label resultLabel = builder.createUnboundLabel();
builder.jumpOnFalse(resultLabel);
value(expression.getRight(), false);
builder.bindLabel(resultLabel);
}
else if (operationType == JetTokens.OROR) {
value(expression.getLeft(), false);
Label resultLabel = builder.createUnboundLabel();
builder.jumpOnTrue(resultLabel);
value(expression.getRight(), false);
builder.bindLabel(resultLabel);
}
else {
value(expression.getLeft(), false);
value(expression.getRight(), false);
builder.readNode(expression);
}
}
@Override
public void visitIfExpression(JetIfExpression expression) {
value(expression.getCondition(), false);
Label elseLabel = builder.createUnboundLabel();
builder.jumpOnFalse(elseLabel);
value(expression.getThen(), true);
Label resultLabel = builder.createUnboundLabel();
builder.jump(resultLabel);
builder.bindLabel(elseLabel);
JetExpression elseBranch = expression.getElse();
if (elseBranch != null) {
value(elseBranch, true);
}
builder.bindLabel(resultLabel);
// builder.readNode(expression);
}
@Override
public void visitTryExpression(JetTryExpression expression) {
JetFinallySection finallyBlock = expression.getFinallyBlock();
if (finallyBlock != null) {
builder.pushFinally(finallyBlock.getFinalExpression());
}
List<JetCatchClause> catchClauses = expression.getCatchClauses();
if (catchClauses.isEmpty()) {
value(expression.getTryBlock(), true);
}
else {
Label catchBlock = builder.createUnboundLabel();
builder.nondeterministicJump(catchBlock);
value(expression.getTryBlock(), true);
Label afterCatches = builder.createUnboundLabel();
builder.jump(afterCatches);
builder.bindLabel(catchBlock);
for (JetCatchClause catchClause : catchClauses) {
value(catchClause.getCatchBody(), true);
builder.nondeterministicJump(afterCatches);
}
builder.bindLabel(afterCatches);
}
if (finallyBlock != null) {
builder.popFinally();
}
}
@Override
public void visitWhileExpression(JetWhileExpression expression) {
Label loopExitPoint = builder.createUnboundLabel();
Label loopEntryPoint = builder.enterLoop(expression, loopExitPoint);
value(expression.getCondition(), false);
builder.jumpOnFalse(loopExitPoint);
value(expression.getBody(), true);
builder.jump(loopEntryPoint);
builder.exitLoop(expression);
}
@Override
public void visitDoWhileExpression(JetDoWhileExpression expression) {
Label loopExitPoint = builder.createUnboundLabel();
Label loopEntryPoint = builder.enterLoop(expression, loopExitPoint);
value(expression.getBody(), true);
value(expression.getCondition(), false);
builder.jumpOnTrue(loopEntryPoint);
builder.exitLoop(expression);
}
@Override
public void visitForExpression(JetForExpression expression) {
value(expression.getLoopRange(), false);
Label loopExitPoint = builder.createUnboundLabel();
Label loopEntryPoint = builder.enterLoop(expression, loopExitPoint);
value(expression.getBody(), true);
builder.nondeterministicJump(loopEntryPoint);
builder.exitLoop(expression);
}
@Override
public void visitBreakExpression(JetBreakExpression expression) {
JetSimpleNameExpression labelElement = expression.getTargetLabel();
Label exitPoint = (labelElement != null)
? builder.getExitPoint(labelElement)
: builder.getExitPoint(builder.getCurrentLoop());
builder.jump(exitPoint);
}
@Override
public void visitContinueExpression(JetContinueExpression expression) {
JetSimpleNameExpression labelElement = expression.getTargetLabel();
if (labelElement != null) {
builder.jump(builder.getEntryPoint(labelElement));
}
else {
builder.jump(builder.getEntryPoint(builder.getCurrentLoop()));
}
}
@Override
public void visitReturnExpression(JetReturnExpression expression) {
JetExpression returnedExpression = expression.getReturnedExpression();
if (returnedExpression != null) {
value(returnedExpression, false);
}
JetSimpleNameExpression labelElement = expression.getTargetLabel();
JetElement subroutine = (labelElement != null)
? resolveLabel(labelElement)
: builder.getCurrentSubroutine();
if (returnedExpression == null) {
builder.returnNoValue(subroutine);
}
else {
builder.returnValue(subroutine);
}
}
@Override
public void visitBlockExpression(JetBlockExpression expression) {
for (JetElement statement : expression.getStatements()) {
value(statement, true);
}
}
@Override
public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) {
if (preferBlock && !expression.hasParameterSpecification()) {
for (JetElement statement : expression.getBody()) {
value(statement, true);
}
}
else {
generateSubroutineControlFlow(expression, expression.getBody(), true);
}
}
@Override
public void visitJetElement(JetElement elem) {
builder.unsupported(elem);
}
}
}
@@ -0,0 +1,21 @@
package org.jetbrains.jet.lang.cfg;
/**
* @author abreslav
*/
public class Label {
private final String name;
public Label(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
}
@@ -0,0 +1,27 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.jet.lang.cfg.Label;
/**
* @author abreslav
*/
public abstract class AbstractJumpInstruction extends Instruction {
private final Label targetLabel;
private Instruction resolvedTarget;
public AbstractJumpInstruction(Label targetLabel) {
this.targetLabel = targetLabel;
}
public Label getTargetLabel() {
return targetLabel;
}
public Instruction getResolvedTarget() {
return resolvedTarget;
}
public void setResolvedTarget(Instruction resolvedTarget) {
this.resolvedTarget = outgoingEdgeTo(resolvedTarget);
}
}
@@ -0,0 +1,48 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.jet.lang.cfg.Label;
/**
* @author abreslav
*/
public class ConditionalJumpInstruction extends AbstractJumpInstruction {
private final boolean onTrue;
private Instruction nextOnTrue;
private Instruction nextOnFalse;
public ConditionalJumpInstruction(boolean onTrue, Label targetLabel) {
super(targetLabel);
this.onTrue = onTrue;
}
public boolean onTrue() {
return onTrue;
}
public Instruction getNextOnTrue() {
return nextOnTrue;
}
public void setNextOnTrue(Instruction nextOnTrue) {
this.nextOnTrue = outgoingEdgeTo(nextOnTrue);
}
public Instruction getNextOnFalse() {
return nextOnFalse;
}
public void setNextOnFalse(Instruction nextOnFalse) {
this.nextOnFalse = outgoingEdgeTo(nextOnFalse);
}
@Override
public void accept(InstructionVisitor visitor) {
visitor.visitConditionalJump(this);
}
@Override
public String toString() {
String instr = onTrue ? "jt" : "jf";
return instr + "(" + getTargetLabel().getName() + ")";
}
}
@@ -0,0 +1,20 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetExpression;
/**
* @author abreslav
*/
public abstract class ExpressionInstruction extends Instruction {
protected final JetExpression expression;
public ExpressionInstruction(@NotNull JetExpression expression) {
this.expression = expression;
}
@NotNull
public JetExpression getExpression() {
return expression;
}
}
@@ -0,0 +1,27 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* @author abreslav
*/
public abstract class Instruction {
private Collection<Instruction> previousInstructions = new LinkedHashSet<Instruction>();
public Collection<Instruction> getPreviousInstructions() {
return previousInstructions;
}
@Nullable
protected Instruction outgoingEdgeTo(@Nullable Instruction target) {
if (target != null) {
target.getPreviousInstructions().add(this);
}
return target;
}
public abstract void accept(InstructionVisitor visitor);
}
@@ -0,0 +1,37 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
/**
* @author abreslav
*/
public class InstructionVisitor {
public void visitRead(ValueInstruction instruction) {
visitInstruction(instruction);
}
public void visitUnconditionalJump(UnconditionalJumpInstruction instruction) {
visitJump(instruction);
}
public void visitConditionalJump(ConditionalJumpInstruction instruction) {
visitJump(instruction);
}
public void visitReturnValue(ReturnValueInstruction instruction) {
visitJump(instruction);
}
public void visitReturnNoValue(ReturnNoValueInstruction instruction) {
visitJump(instruction);
}
public void visitSubroutineExit(SubroutineExitInstruction instruction) {
visitInstruction(instruction);
}
public void visitJump(AbstractJumpInstruction instruction) {
visitInstruction(instruction);
}
public void visitInstruction(Instruction instruction) {
}
}
@@ -0,0 +1,22 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.jet.lang.psi.JetExpression;
/**
* @author abreslav
*/
public abstract class InstructionWithNext extends ExpressionInstruction {
private Instruction next;
public InstructionWithNext(JetExpression expression) {
super(expression);
}
public Instruction getNext() {
return next;
}
public void setNext(Instruction next) {
this.next = outgoingEdgeTo(next);
}
}
@@ -0,0 +1,176 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.cfg.JetControlFlowBuilder;
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 java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
* @author abreslav
*/
public class JetControlFlowInstructionsGenerator implements JetControlFlowBuilder {
private static class BlockInfo {
private final JetElement element;
private final Label entryPoint;
private final Label exitPoint;
private BlockInfo(JetElement element, Label entryPoint, Label exitPoint) {
this.element = element;
this.entryPoint = entryPoint;
this.exitPoint = exitPoint;
}
public JetElement getElement() {
return element;
}
public Label getEntryPoint() {
return entryPoint;
}
public Label getExitPoint() {
return exitPoint;
}
}
private final Stack<BlockInfo> loopInfo = new Stack<BlockInfo>();
private final Stack<BlockInfo> subroutineInfo = new Stack<BlockInfo>();
private final Map<JetElement, BlockInfo> elementToBlockInfo = new HashMap<JetElement, BlockInfo>();
private int labelCount = 0;
private final Pseudocode pseudocode = new Pseudocode();
public Pseudocode getPseudocode() {
return pseudocode;
}
private void add(Instruction instruction) {
pseudocode.getInstructions().add(instruction);
}
@NotNull
@Override
public final Label createUnboundLabel() {
return new Label("l" + labelCount++);
}
@Override
public final Label enterLoop(@NotNull JetExpression expression, Label loopExitPoint) {
Label label = createUnboundLabel();
bindLabel(label);
BlockInfo blockInfo = new BlockInfo(expression, label, loopExitPoint);
loopInfo.push(blockInfo);
elementToBlockInfo.put(expression, blockInfo);
return label;
}
@Override
public final void exitLoop(@NotNull JetExpression expression) {
BlockInfo info = loopInfo.pop();
elementToBlockInfo.remove(expression);
bindLabel(info.getExitPoint());
}
@Override
public JetElement getCurrentLoop() {
return loopInfo.empty() ? null : loopInfo.peek().getElement();
}
@Override
public void enterSubroutine(@NotNull JetElement subroutine) {
Label entryPoint = createUnboundLabel();
BlockInfo blockInfo = new BlockInfo(subroutine, entryPoint, createUnboundLabel());
subroutineInfo.push(blockInfo);
elementToBlockInfo.put(subroutine, blockInfo);
bindLabel(entryPoint);
}
@Override
public JetElement getCurrentSubroutine() {
return subroutineInfo.empty() ? null : subroutineInfo.peek().getElement();
}
@Override
public Label getEntryPoint(@NotNull JetElement labelElement) {
return elementToBlockInfo.get(labelElement).getEntryPoint();
}
@Override
public Label getExitPoint(@NotNull JetElement labelElement) {
return elementToBlockInfo.get(labelElement).getExitPoint();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void exitSubroutine(@NotNull JetElement subroutine) {
bindLabel(getExitPoint(subroutine));
add(new SubroutineExitInstruction(subroutine));
elementToBlockInfo.remove(subroutine);
}
@Override
public void returnValue(@NotNull JetElement subroutine) {
add(new ReturnValueInstruction(getExitPoint(subroutine)));
}
@Override
public void returnNoValue(@NotNull JetElement subroutine) {
add(new ReturnNoValueInstruction(getExitPoint(subroutine)));
}
@Override
public void readNode(@NotNull JetExpression expression) {
add(new ValueInstruction(expression));
}
@Override
public void jump(@NotNull Label label) {
add(new UnconditionalJumpInstruction(label));
}
@Override
public void jumpOnFalse(@NotNull Label label) {
add(new ConditionalJumpInstruction(false, label));
}
@Override
public void jumpOnTrue(@NotNull Label label) {
add(new ConditionalJumpInstruction(true, label));
}
@Override
public void bindLabel(@NotNull Label label) {
pseudocode.getLabels().put(label, pseudocode.getInstructions().size());
}
@Override
public void nondeterministicJump(Label label) {
throw new UnsupportedOperationException(); // TODO
}
@Override
public void popFinally() {
throw new UnsupportedOperationException(); // TODO
}
@Override
public void pushFinally(@NotNull JetBlockExpression expression) {
throw new UnsupportedOperationException(); // TODO
}
@Override
public void unsupported(JetElement element) {
throw new IllegalStateException("Unsupported element: " + element.getText() + " " + element);
}
}
@@ -0,0 +1,187 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.cfg.Label;
import java.io.PrintStream;
import java.util.*;
/**
* @author abreslav
*/
public class Pseudocode {
private final List<Instruction> instructions = new ArrayList<Instruction>();
private final Map<Label, Integer> labels = new LinkedHashMap<Label, Integer>();
public List<Instruction> getInstructions() {
return instructions;
}
public Map<Label, Integer> getLabels() {
return labels;
}
public void postProcess() {
for (int i = 0, instructionsSize = instructions.size(); i < instructionsSize; i++) {
Instruction instruction = instructions.get(i);
final int currentPosition = i;
instruction.accept(new InstructionVisitor() {
@Override
public void visitRead(ValueInstruction instruction) {
instruction.setNext(getNextPosition(currentPosition));
}
@Override
public void visitJump(AbstractJumpInstruction instruction) {
instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
}
@Override
public void visitConditionalJump(ConditionalJumpInstruction instruction) {
Instruction nextInstruction = getNextPosition(currentPosition);
Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel());
if (instruction.onTrue()) {
instruction.setNextOnFalse(nextInstruction);
instruction.setNextOnTrue(jumpTarget);
}
else {
instruction.setNextOnFalse(jumpTarget);
instruction.setNextOnTrue(nextInstruction);
}
visitJump(instruction);
}
@Override
public void visitSubroutineExit(SubroutineExitInstruction instruction) {
// Nothing
}
@Override
public void visitInstruction(Instruction instruction) {
throw new UnsupportedOperationException(instruction.toString());
}
});
}
}
@NotNull
private Instruction getJumpTarget(@NotNull Label targetLabel) {
Integer targetPosition = labels.get(targetLabel);
return getTargetInstruction(targetPosition);
}
@NotNull
private Instruction getTargetInstruction(@NotNull Integer targetPosition) {
while (true) {
assert targetPosition != null;
Instruction targetInstruction = instructions.get(targetPosition);
if (false == targetInstruction instanceof UnconditionalJumpInstruction) {
return targetInstruction;
}
Label label = ((UnconditionalJumpInstruction) targetInstruction).getTargetLabel();
targetPosition = labels.get(label);
}
}
@NotNull
private Instruction getNextPosition(int currentPosition) {
int targetPosition = currentPosition + 1;
assert targetPosition < instructions.size() : currentPosition;
return getTargetInstruction(targetPosition);
}
public void dumpInstructions(@NotNull PrintStream out) {
for (int i = 0, instructionsSize = instructions.size(); i < instructionsSize; i++) {
Instruction instruction = instructions.get(i);
for (Map.Entry<Label, Integer> entry : labels.entrySet()) {
if (entry.getValue() == i) {
out.println(entry.getKey() + ":");
}
}
out.println(" " + instruction);
}
}
public void dumpGraph(@NotNull final PrintStream out) {
out.println("digraph g {");
final Map<Instruction, String> nodeToName = new HashMap<Instruction, String>();
int count = 0;
for (Instruction node : instructions) {
if (node instanceof UnconditionalJumpInstruction) {
continue;
}
String name = "n" + count++;
nodeToName.put(node, name);
String text = node.toString();
int newline = text.indexOf("\n");
if (newline >= 0) {
text = text.substring(0, newline);
}
String shape = node instanceof ConditionalJumpInstruction ? "diamond" : "box";
out.println(name + "[label=\"" + text + "\", shape=" + shape + "];");
}
for (final Instruction fromInst : instructions) {
fromInst.accept(new InstructionVisitor() {
@Override
public void visitUnconditionalJump(UnconditionalJumpInstruction instruction) {
// Nothing
}
@Override
public void visitJump(AbstractJumpInstruction instruction) {
writeEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getResolvedTarget()), null);
}
@Override
public void visitReturnValue(ReturnValueInstruction instruction) {
super.visitReturnValue(instruction);
}
@Override
public void visitReturnNoValue(ReturnNoValueInstruction instruction) {
super.visitReturnNoValue(instruction);
}
@Override
public void visitConditionalJump(ConditionalJumpInstruction instruction) {
String from = nodeToName.get(instruction);
writeEdge(out, from, nodeToName.get(instruction.getNextOnFalse()), "no");
writeEdge(out, from, nodeToName.get(instruction.getNextOnTrue()), "yes");
}
@Override
public void visitRead(ValueInstruction instruction) {
writeEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getNext()), null);
}
@Override
public void visitSubroutineExit(SubroutineExitInstruction instruction) {
// Nothing
}
@Override
public void visitInstruction(Instruction instruction) {
throw new UnsupportedOperationException(instruction.toString());
}
});
}
out.println("}");
out.close();
}
private void writeEdge(PrintStream out, String from, String to, String label) {
if (label != null) {
label = "[label=\"" + label + "\"]";
}
else {
label = "";
}
out.println(from + " -> " + to + label + ";");
}
}
@@ -0,0 +1,23 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.jet.lang.cfg.Label;
/**
* @author abreslav
*/
public class ReturnNoValueInstruction extends AbstractJumpInstruction {
public ReturnNoValueInstruction(Label targetLabel) {
super(targetLabel);
}
@Override
public void accept(InstructionVisitor visitor) {
visitor.visitReturnNoValue(this);
}
@Override
public String toString() {
return "ret";
}
}
@@ -0,0 +1,23 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.jet.lang.cfg.Label;
/**
* @author abreslav
*/
public class ReturnValueInstruction extends AbstractJumpInstruction {
public ReturnValueInstruction(Label targetLabel) {
super(targetLabel);
}
@Override
public void accept(InstructionVisitor visitor) {
visitor.visitReturnValue(this);
}
@Override
public String toString() {
return "ret(*)";
}
}
@@ -0,0 +1,29 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetElement;
/**
* @author abreslav
*/
public class SubroutineExitInstruction extends Instruction {
private final JetElement subroutine;
public SubroutineExitInstruction(@NotNull JetElement subroutine) {
this.subroutine = subroutine;
}
public JetElement getSubroutine() {
return subroutine;
}
@Override
public void accept(InstructionVisitor visitor) {
visitor.visitSubroutineExit(this);
}
@Override
public String toString() {
return "<END>";
}
}
@@ -0,0 +1,25 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.jet.lang.cfg.Label;
/**
* @author abreslav
*/
public class UnconditionalJumpInstruction extends AbstractJumpInstruction {
public UnconditionalJumpInstruction(Label targetLabel) {
super(targetLabel);
}
@Override
public void accept(InstructionVisitor visitor) {
visitor.visitUnconditionalJump(this);
}
@Override
public String toString() {
return "jmp(" + getTargetLabel().getName() + ")";
}
}
@@ -0,0 +1,24 @@
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetExpression;
/**
* @author abreslav
*/
public class ValueInstruction extends InstructionWithNext {
public ValueInstruction(@NotNull JetExpression expression) {
super(expression);
}
@Override
public void accept(InstructionVisitor visitor) {
visitor.visitRead(this);
}
@Override
public String toString() {
return "r(" + expression.getText() + ")";
}
}
@@ -311,8 +311,8 @@ public class JetExpressionParsing extends AbstractJetParsing {
*
* postfixUnaryOperation
* : "++" : "--"
* : typeArguments? valueArguments (label? functionLiteral)
* : typeArguments (label? functionLiteral)
* : typeArguments? valueArguments (getEntryPoint? functionLiteral)
* : typeArguments (getEntryPoint? functionLiteral)
* : arrayAccess
* : memberAccessOperation postfixUnaryOperation // TODO: Review
* ;
@@ -375,8 +375,8 @@ public class JetExpressionParsing extends AbstractJetParsing {
/*
* callSuffix
* : typeArguments? valueArguments (label? functionLiteral*)
* : typeArguments (label? functionLiteral*)
* : typeArguments? valueArguments (getEntryPoint? functionLiteral*)
* : typeArguments (getEntryPoint? functionLiteral*)
* ;
*/
private boolean parseCallSuffix() {
@@ -427,7 +427,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
}
/*
* expression (label? functionLiteral)?
* expression (getEntryPoint? functionLiteral)?
*/
protected boolean parseCallWithClosure() {
boolean success = false;
@@ -448,7 +448,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
/*
* atomicExpression
* : tupleLiteral // or parenthesized expression
* : "this" label? ("<" type ">")?
* : "this" getEntryPoint? ("<" type ">")?
* : "typeof" "(" expression ")"
* : "new" constructorInvocation
* : objectLiteral
@@ -1311,8 +1311,8 @@ public class JetExpressionParsing extends AbstractJetParsing {
}
/*
* : "continue" label?
* : "break" label?
* : "continue" getEntryPoint?
* : "break" getEntryPoint?
*/
private void parseJump(JetNodeType type) {
assert _at(BREAK_KEYWORD) || _at(CONTINUE_KEYWORD);
@@ -1327,7 +1327,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
}
/*
* "return" label? expression?
* "return" getEntryPoint? expression?
*/
private void parseReturn() {
assert _at(RETURN_KEYWORD);
@@ -1456,7 +1456,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
}
/*
* "this" label? ("<" type ">")?
* "this" getEntryPoint? ("<" type ">")?
*/
private void parseThisExpression() {
assert _at(THIS_KEYWORD);
@@ -1,10 +1,7 @@
package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lexer.JetTokens;
/**
* @author max
@@ -18,10 +15,4 @@ public class JetBreakExpression extends JetLabelQualifiedExpression {
public void accept(JetVisitor visitor) {
visitor.visitBreakExpression(this);
}
@Nullable
public String getLabelName() {
PsiElement id = findChildByType(JetTokens.IDENTIFIER);
return id != null ? id.getText() : null;
}
}
@@ -2,6 +2,7 @@ package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.JetNodeTypes;
/**
* @author max
@@ -16,7 +17,7 @@ public class JetFinallySection extends JetElement {
visitor.visitFinallySection(this);
}
public JetExpression getFinalExpression() {
return findChildByClass(JetExpression.class);
public JetBlockExpression getFinalExpression() {
return (JetBlockExpression) findChildByType(JetNodeTypes.BLOCK);
}
}
@@ -3,7 +3,6 @@ package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
/**
@@ -16,14 +15,13 @@ public class JetLabelQualifiedExpression extends JetExpression {
}
@Nullable
public JetToken getTargetLabel() {
ASTNode targetLabelNode = getTargetLabelNode();
if (targetLabelNode == null) return null;
return (JetToken) targetLabelNode.getElementType();
public JetSimpleNameExpression getTargetLabel() {
return (JetSimpleNameExpression) findChildByType(JetTokens.LABELS);
}
@Nullable
public ASTNode getTargetLabelNode() {
return getNode().findChildByType(JetTokens.LABELS);
@Nullable @IfNotParsed
public JetExpression getLabeledExpression() {
return findChildByClass(JetExpression.class);
}
}
@@ -21,8 +21,8 @@ public class JetTryExpression extends JetExpression {
}
@NotNull
public JetExpression getTryBlock() {
return (JetExpression) findChildByType(JetNodeTypes.BLOCK);
public JetBlockExpression getTryBlock() {
return (JetBlockExpression) findChildByType(JetNodeTypes.BLOCK);
}
@NotNull
@@ -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.*;
/**
@@ -224,6 +229,22 @@ public class TopDownAnalyzer {
FunctionDescriptor descriptor = classDescriptorResolver.resolveFunctionDescriptor(declaringScope.getContainingDeclaration(), declaringScope, function);
declaringScope.addFunctionDescriptor(descriptor);
functions.put(function, descriptor);
JetExpression bodyExpression = function.getBodyExpression();
if (bodyExpression != null) {
System.out.println("-------------");
JetControlFlowInstructionsGenerator instructionsGenerator = new JetControlFlowInstructionsGenerator();
new JetControlFlowProcessor(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) {
@@ -116,6 +116,7 @@ public class JetTypeInferrer {
@NotNull JetReferenceExpression reference,
@NotNull String name,
@NotNull JetType receiverType,
@NotNull List<JetType> argumentTypes,
boolean reportUnresolved) {
OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name);
@@ -272,20 +273,20 @@ public class JetTypeInferrer {
};
}
private JetType getBlockReturnedType(@NotNull JetScope outerScope, List<JetElement> block) {
private JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull List<JetElement> block, @NotNull LabeledJumpDomain jumpDomain) {
if (block.isEmpty()) {
return JetStandardClasses.getUnitType();
}
DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration();
WritableScope scope = semanticServices.createWritableScope(outerScope, containingDescriptor);
return getBlockReturnedTypeWithWritableScope(scope, block);
return getBlockReturnedTypeWithWritableScope(scope, block, jumpDomain);
}
private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List<? extends JetElement> block) {
private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List<? extends JetElement> block, @NotNull LabeledJumpDomain jumpDomain) {
assert !block.isEmpty();
TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true);
TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true, jumpDomain);
JetType result = null;
for (JetElement statement : block) {
@@ -312,11 +313,18 @@ public class JetTypeInferrer {
private class TypeInferrerVisitor extends JetVisitor {
private final JetScope scope;
private final boolean preferBlock;
private final LabeledJumpDomain jumpDomain;
protected JetType result;
private TypeInferrerVisitor(JetScope scope, boolean preferBlock) {
private TypeInferrerVisitor(@NotNull JetScope scope, boolean preferBlock, @NotNull LabeledJumpDomain jumpDomain) {
this.scope = scope;
this.preferBlock = preferBlock;
this.jumpDomain = jumpDomain;
}
private TypeInferrerVisitor(JetScope scope, boolean preferBlock) {
this(scope, preferBlock, LabeledJumpDomain.EMPTY);
}
@Nullable
@@ -378,7 +386,7 @@ public class JetTypeInferrer {
@Override
public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) {
if (preferBlock && !expression.hasParameterSpecification()) {
result = getBlockReturnedType(scope, expression.getBody());
result = getBlockReturnedType(scope, expression.getBody(), LabeledJumpDomain.ERROR);
return;
}
@@ -415,7 +423,7 @@ public class JetTypeInferrer {
writableScope.addPropertyDescriptor(propertyDescriptor);
}
writableScope.setThisType(receiverType);
returnType = getBlockReturnedType(writableScope, body);
returnType = getBlockReturnedType(writableScope, body, LabeledJumpDomain.ERROR);
}
result = JetStandardClasses.getFunctionType(null, receiverTypeRef == null ? null : receiverType, parameterTypes, returnType);
}
@@ -466,20 +474,31 @@ public class JetTypeInferrer {
@Override
public void visitReturnExpression(JetReturnExpression expression) {
JetExpression returnedExpression = expression.getReturnedExpression();
JetType returnedType;
if (returnedExpression != null) {
getType(scope, returnedExpression, false);
returnedType = getType(scope, returnedExpression, false);
}
else {
returnedType = JetStandardClasses.getUnitType();
}
jumpDomain.registerReturn(expression, returnedType);
result = JetStandardClasses.getNothingType();
}
@Override
public void visitBreakExpression(JetBreakExpression expression) {
result = JetStandardClasses.getNothingType();
jumpDomain.registerBreakOrContinue(expression);
}
@Override
public void visitContinueExpression(JetContinueExpression expression) {
result = JetStandardClasses.getNothingType();
jumpDomain.registerBreakOrContinue(expression);
}
@Override
@@ -589,7 +608,7 @@ public class JetTypeInferrer {
@Override
public void visitBlockExpression(JetBlockExpression expression) {
result = getBlockReturnedType(scope, expression.getStatements());
result = getBlockReturnedType(scope, expression.getStatements(), jumpDomain);
}
@Override
@@ -678,7 +697,7 @@ public class JetTypeInferrer {
if (!function.hasParameterSpecification()) {
WritableScope writableScope = semanticServices.createWritableScope(scope, scope.getContainingDeclaration());
conditionScope = writableScope;
getBlockReturnedTypeWithWritableScope(writableScope, function.getBody());
getBlockReturnedTypeWithWritableScope(writableScope, function.getBody(), LabeledJumpDomain.ERROR); // TODO
} else {
getType(scope, body, true);
}
@@ -686,7 +705,7 @@ public class JetTypeInferrer {
else if (body != null) {
WritableScope writableScope = semanticServices.createWritableScope(scope, scope.getContainingDeclaration());
conditionScope = writableScope;
getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body));
getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body), LabeledJumpDomain.ERROR); // TODO
}
checkCondition(conditionScope, expression.getCondition());
result = JetStandardClasses.getUnitType();
@@ -1220,8 +1239,8 @@ public class JetTypeInferrer {
private class TypeInferrerVisitorWithWritableScope extends TypeInferrerVisitor {
private final WritableScope scope;
public TypeInferrerVisitorWithWritableScope(@NotNull WritableScope scope, boolean preferBlock) {
super(scope, preferBlock);
public TypeInferrerVisitorWithWritableScope(@NotNull WritableScope scope, boolean preferBlock, @NotNull LabeledJumpDomain jumpDomain) {
super(scope, preferBlock, jumpDomain);
this.scope = scope;
}
@@ -1337,4 +1356,31 @@ public class JetTypeInferrer {
semanticServices.getErrorHandler().genericError(elem.getNode(), "Unsupported element in a block: " + elem + " " + elem.getClass().getCanonicalName());
}
}
private interface LabeledJumpDomain {
LabeledJumpDomain EMPTY = new LabeledJumpDomain() {
@Override
public void registerReturn(@NotNull JetReturnExpression expression, JetType returnedExpressionType) {
}
@Override
public void registerBreakOrContinue(@NotNull JetLabelQualifiedExpression expression) {
}
};
LabeledJumpDomain ERROR = new LabeledJumpDomain() {
@Override
public void registerReturn(@NotNull JetReturnExpression expression, JetType returnedExpressionType) {
// throw new UnsupportedOperationException();
}
@Override
public void registerBreakOrContinue(@NotNull JetLabelQualifiedExpression expression) {
// throw new UnsupportedOperationException();
}
};
void registerReturn(@NotNull JetReturnExpression expression, JetType returnedExpressionType);
void registerBreakOrContinue(@NotNull JetLabelQualifiedExpression expression);
}
}