Working on CFG building
This commit is contained in:
Generated
+2
@@ -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);
|
||||
}
|
||||
}
|
||||
+176
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user