Tests for basic control flow structures

This commit is contained in:
Andrey Breslav
2011-04-04 21:11:15 +04:00
parent 1fc134072c
commit fee824bd78
20 changed files with 570 additions and 431 deletions
@@ -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);
}
}
@@ -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>
=====================
+9
View File
@@ -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
}
+91
View File
@@ -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>
=====================
+22 -2
View File
@@ -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>
=====================
+17
View File
@@ -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
}
+88
View File
@@ -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>
=====================
+17
View File
@@ -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 {