Tests for basic control flow structures
This commit is contained in:
@@ -49,6 +49,8 @@ public interface JetControlFlowBuilder {
|
||||
void returnValue(@NotNull JetElement subroutine);
|
||||
void returnNoValue(@NotNull JetElement subroutine);
|
||||
|
||||
void writeNode(@NotNull JetElement assignment, @NotNull JetElement lValue);
|
||||
|
||||
// Other
|
||||
void unsupported(JetElement element);
|
||||
}
|
||||
|
||||
@@ -118,4 +118,9 @@ public class JetControlFlowBuilderAdapter implements JetControlFlowBuilder {
|
||||
public void unsupported(JetElement element) {
|
||||
builder.unsupported(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNode(@NotNull JetElement assignment, @NotNull JetElement lValue) {
|
||||
builder.writeNode(assignment, lValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,348 +0,0 @@
|
||||
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 element) {
|
||||
// createNode(element);
|
||||
// }
|
||||
//
|
||||
// @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 exitTryFinally() {
|
||||
// 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 enterTryFinally(JetBlockExpression element) {
|
||||
// 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;
|
||||
// }
|
||||
//}
|
||||
@@ -167,11 +167,24 @@ public class JetControlFlowProcessor {
|
||||
}
|
||||
builder.bindLabel(resultLabel);
|
||||
}
|
||||
else if (operationType == JetTokens.EQ) {
|
||||
JetExpression left = expression.getLeft();
|
||||
if (right != null) {
|
||||
value(right, false);
|
||||
}
|
||||
if (left instanceof JetSimpleNameExpression) {
|
||||
builder.writeNode(expression, left);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("Assignments to " + left + " are not supported yet"); // TODO
|
||||
}
|
||||
}
|
||||
else {
|
||||
value(expression.getLeft(), false);
|
||||
if (right != null) {
|
||||
value(right, false);
|
||||
}
|
||||
value(expression.getOperationReference(), false);
|
||||
builder.readNode(expression);
|
||||
}
|
||||
}
|
||||
@@ -194,7 +207,10 @@ public class JetControlFlowProcessor {
|
||||
value(expression.getCondition(), false);
|
||||
Label elseLabel = builder.createUnboundLabel();
|
||||
builder.jumpOnFalse(elseLabel);
|
||||
value(expression.getThen(), true);
|
||||
JetExpression then = expression.getThen();
|
||||
if (then != null) {
|
||||
value(then, true);
|
||||
}
|
||||
Label resultLabel = builder.createUnboundLabel();
|
||||
builder.jump(resultLabel);
|
||||
builder.bindLabel(elseLabel);
|
||||
@@ -368,6 +384,51 @@ public class JetControlFlowProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDotQualifiedExpression(JetDotQualifiedExpression expression) {
|
||||
value(expression.getReceiverExpression(), false);
|
||||
JetExpression selectorExpression = expression.getSelectorExpression();
|
||||
if (selectorExpression != null) {
|
||||
value(selectorExpression, false);
|
||||
}
|
||||
builder.readNode(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCallExpression(JetCallExpression expression) {
|
||||
for (JetTypeProjection typeArgument : expression.getTypeArguments()) {
|
||||
value(typeArgument, false);
|
||||
}
|
||||
|
||||
for (JetArgument argument : expression.getValueArguments()) {
|
||||
JetExpression argumentExpression = argument.getArgumentExpression();
|
||||
if (argumentExpression != null) {
|
||||
value(argumentExpression, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (JetExpression functionLiteral : expression.getFunctionLiteralArguments()) {
|
||||
value(functionLiteral, false);
|
||||
}
|
||||
|
||||
value(expression.getCalleeExpression(), false);
|
||||
builder.readNode(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitProperty(JetProperty property) {
|
||||
JetExpression initializer = property.getInitializer();
|
||||
if (initializer != null) {
|
||||
value(initializer, false);
|
||||
builder.writeNode(property, property);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeProjection(JetTypeProjection typeProjection) {
|
||||
// TODO : Support Type Arguments. Class object may be initialized at this point");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJetElement(JetElement elem) {
|
||||
builder.unsupported(elem);
|
||||
|
||||
@@ -54,4 +54,8 @@ public class InstructionVisitor {
|
||||
public void visitSubroutineEnter(SubroutineEnterInstruction instruction) {
|
||||
visitInstructionWithNext(instruction);
|
||||
}
|
||||
|
||||
public void visitWriteValue(WriteValueInstruction writeValueInstruction) {
|
||||
visitInstructionWithNext(writeValueInstruction);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -159,6 +159,11 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
|
||||
add(new ReturnNoValueInstruction(getExitPoint(subroutine)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNode(@NotNull JetElement assignment, @NotNull JetElement lValue) {
|
||||
add(new WriteValueInstruction(assignment, lValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNode(@NotNull JetExpression expression) {
|
||||
add(new ReadValueInstruction(expression));
|
||||
|
||||
@@ -193,21 +193,6 @@ public class Pseudocode {
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpGraph(@NotNull final PrintStream out) {
|
||||
String graphHeader = "digraph g";
|
||||
dumpSubgraph(out, graphHeader, new int[1], "", new HashMap<Instruction, String>());
|
||||
}
|
||||
|
||||
private void dumpSubgraph(final PrintStream out, String graphHeader, final int[] count, String style, final Map<Instruction, String> nodeToName) {
|
||||
out.println(graphHeader + " {");
|
||||
out.println(style);
|
||||
|
||||
dumpNodes(out, count, nodeToName);
|
||||
dumpEdges(out, count, nodeToName);
|
||||
|
||||
out.println("}");
|
||||
}
|
||||
|
||||
public void dumpEdges(final PrintStream out, final int[] count, final Map<Instruction, String> nodeToName) {
|
||||
for (final Instruction fromInst : instructions) {
|
||||
fromInst.accept(new InstructionVisitor() {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.jetbrains.jet.lang.cfg.pseudocode;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.psi.JetElement;
|
||||
import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class WriteValueInstruction extends InstructionWithNext {
|
||||
@NotNull
|
||||
private final JetElement lValue;
|
||||
|
||||
public WriteValueInstruction(@NotNull JetElement assignment, @NotNull JetElement lValue) {
|
||||
super(assignment);
|
||||
this.lValue = lValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(InstructionVisitor visitor) {
|
||||
visitor.visitWriteValue(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (lValue instanceof JetNamedDeclaration) {
|
||||
JetNamedDeclaration value = (JetNamedDeclaration) lValue;
|
||||
return value.getName() + " <- ";
|
||||
}
|
||||
return lValue.getText() + " <-";
|
||||
}
|
||||
}
|
||||
@@ -166,23 +166,10 @@ public class ClassDescriptorResolver {
|
||||
List<TypeParameterDescriptor> typeParameterDescriptors = resolveTypeParameters(functionDescriptor, innerScope, function.getTypeParameters());
|
||||
List<ValueParameterDescriptor> valueParameterDescriptors = resolveValueParameters(functionDescriptor, innerScope, function.getValueParameters());
|
||||
|
||||
JetType returnType;
|
||||
JetTypeReference returnTypeRef = function.getReturnTypeRef();
|
||||
JetExpression bodyExpression = function.getBodyExpression();
|
||||
JetType returnType = null;
|
||||
if (returnTypeRef != null) {
|
||||
returnType = typeResolver.resolveType(innerScope, returnTypeRef);
|
||||
// TODO : check body type, consider recursion
|
||||
if (bodyExpression != null) {
|
||||
semanticServices.getTypeInferrer(trace).getType(innerScope, bodyExpression, function.hasBlockBody());
|
||||
}
|
||||
} else {
|
||||
if (bodyExpression == null) {
|
||||
semanticServices.getErrorHandler().genericError(function.getNode(), "This function must either declare a return type or have a body element");
|
||||
returnType = ErrorUtils.createErrorType("No type, no body");
|
||||
} else {
|
||||
// TODO : Recursion possible
|
||||
returnType = semanticServices.getTypeInferrer(trace).safeGetType(innerScope, bodyExpression, function.hasBlockBody());
|
||||
}
|
||||
}
|
||||
|
||||
functionDescriptor.initialize(
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package org.jetbrains.jet.lang.resolve;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.JetSemanticServices;
|
||||
import org.jetbrains.jet.lang.cfg.JetControlFlowProcessor;
|
||||
import org.jetbrains.jet.lang.cfg.pseudocode.*;
|
||||
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTrace;
|
||||
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory;
|
||||
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowInstructionsGenerator;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.types.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -20,7 +19,7 @@ public class TopDownAnalyzer {
|
||||
|
||||
private final Map<JetClass, MutableClassDescriptor> classes = new LinkedHashMap<JetClass, MutableClassDescriptor>();
|
||||
private final Map<JetNamespace, WritableScope> namespaceScopes = new LinkedHashMap<JetNamespace, WritableScope>();
|
||||
private final Map<JetDeclaration, FunctionDescriptor> functions = new HashMap<JetDeclaration, FunctionDescriptor>();
|
||||
private final Map<JetDeclaration, FunctionDescriptor> functions = new LinkedHashMap<JetDeclaration, FunctionDescriptor>();
|
||||
private final Map<JetDeclaration, WritableScope> declaringScopes = new HashMap<JetDeclaration, WritableScope>();
|
||||
|
||||
private final JetSemanticServices semanticServices;
|
||||
@@ -28,6 +27,7 @@ public class TopDownAnalyzer {
|
||||
private final BindingTrace trace;
|
||||
private final JetTypeInferrer typeInferrer;
|
||||
private final JetControlFlowDataTraceFactory flowDataTraceFactory;
|
||||
private boolean readyToProcessExpressions = false;
|
||||
|
||||
public TopDownAnalyzer(JetSemanticServices semanticServices, @NotNull BindingTrace bindingTrace, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) {
|
||||
this.semanticServices = semanticServices;
|
||||
@@ -55,6 +55,7 @@ public class TopDownAnalyzer {
|
||||
collectTypeDeclarators(toplevelScope, declarations);
|
||||
resolveTypeDeclarations();
|
||||
processBehaviorDeclarators(toplevelScope, declarations);
|
||||
readyToProcessExpressions = true;
|
||||
resolveBehaviorDeclarationBodies();
|
||||
}
|
||||
|
||||
@@ -272,6 +273,7 @@ public class TopDownAnalyzer {
|
||||
|
||||
assert declaration instanceof JetFunction || declaration instanceof JetConstructor;
|
||||
JetDeclarationWithBody declarationWithBody = (JetDeclarationWithBody) declaration;
|
||||
|
||||
JetExpression bodyExpression = declarationWithBody.getBodyExpression();
|
||||
|
||||
if (bodyExpression != null) {
|
||||
@@ -286,13 +288,43 @@ public class TopDownAnalyzer {
|
||||
preferBlock = jetFunction.hasBlockBody();
|
||||
}
|
||||
|
||||
resolveExpression(parameterScope, bodyExpression, preferBlock, controlFlowDataTrace);
|
||||
JetType returnType = resolveExpression(parameterScope, bodyExpression, preferBlock, controlFlowDataTrace);
|
||||
|
||||
if (declaration instanceof JetFunction) {
|
||||
JetFunction function = (JetFunction) declaration;
|
||||
if (function.getReturnTypeRef() != null) {
|
||||
if (returnType != null) {
|
||||
if (!semanticServices.getTypeChecker().isConvertibleTo(returnType, descriptor.getUnsubstitutedReturnType())) {
|
||||
semanticServices.getErrorHandler().typeMismatch(bodyExpression, descriptor.getUnsubstitutedReturnType(), returnType);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
JetType safeReturnType = returnType;
|
||||
if (safeReturnType == null) {
|
||||
safeReturnType = ErrorUtils.createErrorType("Unable to infer body type");
|
||||
}
|
||||
((FunctionDescriptorImpl) descriptor).setUnsubstitutedReturnType(safeReturnType);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (declaration instanceof JetFunction) {
|
||||
JetFunction function = (JetFunction) declaration;
|
||||
if (function.getReturnTypeRef() == null) {
|
||||
semanticServices.getErrorHandler().genericError(function.getNode(), "This function must either declare a return type or have a body element");
|
||||
((FunctionDescriptorImpl) descriptor).setUnsubstitutedReturnType(ErrorUtils.createErrorType("No type, no body"));
|
||||
}
|
||||
}
|
||||
}
|
||||
assert descriptor.getUnsubstitutedReturnType() != null;
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveExpression(@NotNull JetScope scope, JetExpression expression, boolean preferBlock, JetControlFlowDataTrace controlFlowDataTrace) {
|
||||
typeInferrer.getType(scope, expression, preferBlock);
|
||||
@Nullable
|
||||
private JetType resolveExpression(@NotNull JetScope scope, JetExpression expression, boolean preferBlock, JetControlFlowDataTrace controlFlowDataTrace) {
|
||||
assert readyToProcessExpressions : "Must be ready collecting types";
|
||||
return typeInferrer.getType(scope, expression, preferBlock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.jetbrains.jet.lang.types;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -35,13 +36,17 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements
|
||||
public FunctionDescriptor initialize(
|
||||
@NotNull List<TypeParameterDescriptor> typeParameters,
|
||||
@NotNull List<ValueParameterDescriptor> unsubstitutedValueParameters,
|
||||
@NotNull JetType unsubstitutedReturnType) {
|
||||
@Nullable JetType unsubstitutedReturnType) {
|
||||
this.typeParameters = typeParameters;
|
||||
this.unsubstitutedValueParameters = unsubstitutedValueParameters;
|
||||
this.unsubstitutedReturnType = unsubstitutedReturnType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setUnsubstitutedReturnType(@NotNull JetType unsubstitutedReturnType) {
|
||||
this.unsubstitutedReturnType = unsubstitutedReturnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<TypeParameterDescriptor> getTypeParameters() {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
== assignments ==
|
||||
fun assignments() : Unit {
|
||||
var x = 1
|
||||
x = 2
|
||||
|
||||
x = if (true) 1 else 2
|
||||
|
||||
val y = true && false
|
||||
val z = false && true
|
||||
}
|
||||
---------------------
|
||||
l0:
|
||||
<START>
|
||||
r(1)
|
||||
x <-
|
||||
r(2)
|
||||
x <-
|
||||
r(true)
|
||||
jf(l2)
|
||||
r(1)
|
||||
jmp(l3)
|
||||
l2:
|
||||
r(2)
|
||||
l3:
|
||||
x <-
|
||||
r(true)
|
||||
jf(l4)
|
||||
r(false)
|
||||
l4:
|
||||
y <-
|
||||
r(false)
|
||||
jf(l5)
|
||||
r(true)
|
||||
l5:
|
||||
z <-
|
||||
l1:
|
||||
<END>
|
||||
=====================
|
||||
@@ -0,0 +1,9 @@
|
||||
fun assignments() : Unit {
|
||||
var x = 1
|
||||
x = 2
|
||||
|
||||
x = if (true) 1 else 2
|
||||
|
||||
val y = true && false
|
||||
val z = false && true
|
||||
}
|
||||
@@ -1,6 +1,97 @@
|
||||
== anonymous_0 ==
|
||||
{1}
|
||||
---------------------
|
||||
l2:
|
||||
<START>
|
||||
r(1)
|
||||
l3:
|
||||
<END>
|
||||
=====================
|
||||
== f ==
|
||||
fun f(a : Boolean) : Unit {
|
||||
1
|
||||
a
|
||||
2.lng
|
||||
foo(a, 3)
|
||||
genfun<Any>()
|
||||
flfun {1}
|
||||
3.equals(4)
|
||||
|
||||
3 equals 4
|
||||
|
||||
1 + 2
|
||||
|
||||
a && true
|
||||
a || false
|
||||
|
||||
}
|
||||
---------------------
|
||||
l0:
|
||||
<START>
|
||||
r(1)
|
||||
r(a)
|
||||
r(2)
|
||||
r(lng)
|
||||
r(2.lng)
|
||||
r(a)
|
||||
r(3)
|
||||
r(foo)
|
||||
r(foo(a, 3))
|
||||
r(genfun)
|
||||
r(genfun<Any>())
|
||||
rf({1})
|
||||
r(flfun)
|
||||
r(flfun {1})
|
||||
r(3)
|
||||
r(4)
|
||||
r(equals)
|
||||
r(equals(4))
|
||||
r(3.equals(4))
|
||||
r(3)
|
||||
r(4)
|
||||
r(equals)
|
||||
r(3 equals 4)
|
||||
r(1)
|
||||
r(2)
|
||||
r(+)
|
||||
r(1 + 2)
|
||||
r(a)
|
||||
jf(l4)
|
||||
r(true)
|
||||
l4:
|
||||
r(a)
|
||||
jt(l5)
|
||||
r(false)
|
||||
l1:
|
||||
l5:
|
||||
<END>
|
||||
l2:
|
||||
<START>
|
||||
r(1)
|
||||
l3:
|
||||
<END>
|
||||
=====================
|
||||
== foo ==
|
||||
fun foo(a : Boolean, b : Int) : Unit {}
|
||||
---------------------
|
||||
l0:
|
||||
<START>
|
||||
l1:
|
||||
<END>
|
||||
=====================
|
||||
== genfun ==
|
||||
fun genfun<T>() : Unit {}
|
||||
---------------------
|
||||
l0:
|
||||
<START>
|
||||
l1:
|
||||
<END>
|
||||
=====================
|
||||
== flfun ==
|
||||
fun flfun(f : {() : Any}) : Unit {}
|
||||
---------------------
|
||||
l0:
|
||||
<START>
|
||||
l1:
|
||||
<END>
|
||||
=====================
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
fun f(a : Boolean) {
|
||||
fun f(a : Boolean) : Unit {
|
||||
1
|
||||
a
|
||||
}
|
||||
2.lng
|
||||
foo(a, 3)
|
||||
genfun<Any>()
|
||||
flfun {1}
|
||||
3.equals(4)
|
||||
|
||||
3 equals 4
|
||||
|
||||
1 + 2
|
||||
|
||||
a && true
|
||||
a || false
|
||||
|
||||
}
|
||||
|
||||
fun foo(a : Boolean, b : Int) : Unit {}
|
||||
|
||||
fun genfun<T>() : Unit {}
|
||||
|
||||
fun flfun(f : {() : Any}) : Unit {}
|
||||
@@ -0,0 +1,68 @@
|
||||
== lazyBooleans ==
|
||||
fun lazyBooleans(a : Boolean, b : Boolean) : Unit {
|
||||
if (a) {
|
||||
1
|
||||
}
|
||||
else {
|
||||
2
|
||||
}
|
||||
3
|
||||
if (a && b) 5 else 6
|
||||
7
|
||||
if (a || b) 8 else 9
|
||||
10
|
||||
if (a) 11
|
||||
12
|
||||
if (a) else 13
|
||||
14
|
||||
}
|
||||
---------------------
|
||||
l0:
|
||||
<START>
|
||||
r(a)
|
||||
jf(l2)
|
||||
r(1)
|
||||
jmp(l3)
|
||||
l2:
|
||||
r(2)
|
||||
l3:
|
||||
r(3)
|
||||
r(a)
|
||||
jf(l4)
|
||||
r(b)
|
||||
l4:
|
||||
jf(l5)
|
||||
r(5)
|
||||
jmp(l6)
|
||||
l5:
|
||||
r(6)
|
||||
l6:
|
||||
r(7)
|
||||
r(a)
|
||||
jt(l7)
|
||||
r(b)
|
||||
l7:
|
||||
jf(l8)
|
||||
r(8)
|
||||
jmp(l9)
|
||||
l8:
|
||||
r(9)
|
||||
l9:
|
||||
r(10)
|
||||
r(a)
|
||||
jf(l10)
|
||||
r(11)
|
||||
jmp(l11)
|
||||
l10:
|
||||
l11:
|
||||
r(12)
|
||||
r(a)
|
||||
jf(l12)
|
||||
jmp(l13)
|
||||
l12:
|
||||
r(13)
|
||||
l13:
|
||||
r(14)
|
||||
l1:
|
||||
<END>
|
||||
=====================
|
||||
@@ -0,0 +1,17 @@
|
||||
fun lazyBooleans(a : Boolean, b : Boolean) : Unit {
|
||||
if (a) {
|
||||
1
|
||||
}
|
||||
else {
|
||||
2
|
||||
}
|
||||
3
|
||||
if (a && b) 5 else 6
|
||||
7
|
||||
if (a || b) 8 else 9
|
||||
10
|
||||
if (a) 11
|
||||
12
|
||||
if (a) else 13
|
||||
14
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
== foo ==
|
||||
fun foo() {
|
||||
if (b)
|
||||
return@a 1
|
||||
}
|
||||
---------------------
|
||||
l6:
|
||||
<START>
|
||||
r(b)
|
||||
jf(l8)
|
||||
r(1)
|
||||
ret(*)
|
||||
jmp(l9)
|
||||
l7:
|
||||
l8:
|
||||
l9:
|
||||
<END>
|
||||
=====================
|
||||
== anonymous_0 ==
|
||||
{a =>
|
||||
2
|
||||
fun foo() {
|
||||
if (b)
|
||||
return@a 1
|
||||
}
|
||||
|
||||
return@a 5;
|
||||
}
|
||||
---------------------
|
||||
l4:
|
||||
<START>
|
||||
r(2)
|
||||
r(5)
|
||||
ret(*)
|
||||
l5:
|
||||
<END>
|
||||
=====================
|
||||
== nonlocals1 ==
|
||||
fun nonlocals1(a : Boolean, b : Boolean) : Unit {
|
||||
if (a)
|
||||
return 1
|
||||
1
|
||||
@a{a =>
|
||||
2
|
||||
fun foo() {
|
||||
if (b)
|
||||
return@a 1
|
||||
}
|
||||
|
||||
return@a 5;
|
||||
}
|
||||
|
||||
1.lng
|
||||
|
||||
}
|
||||
---------------------
|
||||
l0:
|
||||
<START>
|
||||
r(a)
|
||||
jf(l2)
|
||||
r(1)
|
||||
ret(*)
|
||||
jmp(l3)
|
||||
l2:
|
||||
l3:
|
||||
r(1)
|
||||
rf({a =>
|
||||
2
|
||||
fun foo() {
|
||||
if (b)
|
||||
return@a 1
|
||||
}
|
||||
|
||||
return@a 5;
|
||||
})
|
||||
r(1)
|
||||
r(lng)
|
||||
r(1.lng)
|
||||
l1:
|
||||
<END>
|
||||
l4:
|
||||
<START>
|
||||
r(2)
|
||||
r(5)
|
||||
ret(*)
|
||||
l5:
|
||||
<END>
|
||||
=====================
|
||||
@@ -0,0 +1,17 @@
|
||||
fun nonlocals1(a : Boolean, b : Boolean) : Unit {
|
||||
if (a)
|
||||
return 1
|
||||
1
|
||||
@a{a =>
|
||||
2
|
||||
fun foo() {
|
||||
if (b)
|
||||
return@a 1
|
||||
}
|
||||
|
||||
return@a 5;
|
||||
}
|
||||
|
||||
1.lng
|
||||
|
||||
}
|
||||
@@ -42,62 +42,83 @@ public class JetControlFlowTest extends JetTestCaseBase {
|
||||
configureByFile(getTestFilePath());
|
||||
JetFile file = (JetFile) getFile();
|
||||
|
||||
final Map<JetElement, Pseudocode> data = new LinkedHashMap<JetElement, Pseudocode>();
|
||||
final JetControlFlowDataTrace jetControlFlowDataTrace = new JetControlFlowDataTrace() {
|
||||
|
||||
@Override
|
||||
public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) {
|
||||
data.put(element, pseudocode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
AnalyzingUtils.analyzeNamespace(file.getRootNamespace(), ErrorHandler.THROW_EXCEPTION, new JetControlFlowDataTraceFactory() {
|
||||
@NotNull
|
||||
@Override
|
||||
public JetControlFlowDataTrace createTrace(JetElement element) {
|
||||
return new JetControlFlowDataTrace() {
|
||||
private final Map<JetElement, Pseudocode> data = new LinkedHashMap<JetElement, Pseudocode>();
|
||||
return jetControlFlowDataTrace;
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) {
|
||||
data.put(element, pseudocode);
|
||||
}
|
||||
try {
|
||||
try {
|
||||
processCFData(name, data);
|
||||
} catch (AssertionFailedError e) {
|
||||
dumpDot(name, data.values());
|
||||
throw e;
|
||||
}
|
||||
dumpDot(name, data.values());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
try {
|
||||
processCFData(name);
|
||||
} catch (AssertionFailedError e) {
|
||||
dumpDot(name, data.values());
|
||||
throw e;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processCFData(String name) throws IOException {
|
||||
Collection<Pseudocode> pseudocodes = data.values();
|
||||
for (Pseudocode pseudocode : pseudocodes) {
|
||||
pseudocode.postProcess();
|
||||
}
|
||||
private void processCFData(String name, Map<JetElement, Pseudocode> data) throws IOException {
|
||||
Collection<Pseudocode> pseudocodes = data.values();
|
||||
for (Pseudocode pseudocode : pseudocodes) {
|
||||
pseudocode.postProcess();
|
||||
}
|
||||
|
||||
StringBuilder instructionDump = new StringBuilder();
|
||||
for (Pseudocode pseudocode : pseudocodes) {
|
||||
pseudocode.dumpInstructions(instructionDump);
|
||||
instructionDump.append("=====================\n");
|
||||
}
|
||||
StringBuilder instructionDump = new StringBuilder();
|
||||
int i = 0;
|
||||
for (Pseudocode pseudocode : pseudocodes) {
|
||||
JetElement correspondingElement = pseudocode.getCorrespondingElement();
|
||||
String label;
|
||||
if (correspondingElement instanceof JetNamedDeclaration) {
|
||||
JetNamedDeclaration namedDeclaration = (JetNamedDeclaration) correspondingElement;
|
||||
label = namedDeclaration.getName();
|
||||
}
|
||||
else {
|
||||
label = "anonymous_" + i++;
|
||||
}
|
||||
|
||||
String expectedInstructionsFileName = getTestDataPath() + "/" + getTestFilePath().replace(".jet", ".instructions");
|
||||
File expectedInstructionsFile = new File(expectedInstructionsFileName);
|
||||
if (!expectedInstructionsFile.exists()) {
|
||||
FileUtil.writeToFile(expectedInstructionsFile, instructionDump.toString());
|
||||
fail("No expected instructions for " + name + " generated result is written into " + expectedInstructionsFileName);
|
||||
}
|
||||
String expectedInstructions = FileUtil.loadFile(expectedInstructionsFile);
|
||||
instructionDump.append("== ").append(label).append(" ==\n");
|
||||
|
||||
assertEquals(expectedInstructions, instructionDump.toString());
|
||||
instructionDump.append(correspondingElement.getText());
|
||||
instructionDump.append("\n---------------------\n");
|
||||
pseudocode.dumpInstructions(instructionDump);
|
||||
instructionDump.append("=====================\n");
|
||||
}
|
||||
|
||||
String expectedInstructionsFileName = getTestDataPath() + "/" + getTestFilePath().replace(".jet", ".instructions");
|
||||
File expectedInstructionsFile = new File(expectedInstructionsFileName);
|
||||
if (!expectedInstructionsFile.exists()) {
|
||||
FileUtil.writeToFile(expectedInstructionsFile, instructionDump.toString());
|
||||
fail("No expected instructions for " + name + " generated result is written into " + expectedInstructionsFileName);
|
||||
}
|
||||
String expectedInstructions = FileUtil.loadFile(expectedInstructionsFile);
|
||||
|
||||
assertEquals(expectedInstructions, instructionDump.toString());
|
||||
|
||||
// StringBuilder graphDump = new StringBuilder();
|
||||
// for (Pseudocode pseudocode : pseudocodes) {
|
||||
// topOrderDump(pseudocode.)
|
||||
// }
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void dumpDot(String name, Collection<Pseudocode> pseudocodes) throws FileNotFoundException {
|
||||
|
||||
Reference in New Issue
Block a user