returnNodes) {
this.underlyingAST = underlyingAST;
this.treeLookupMap = treeLookupMap;
this.convertedTreeLookupMap = convertedTreeLookupMap;
this.nodeList = nodeList;
this.bindings = bindings;
this.leaders = leaders;
this.returnNodes = returnNodes;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (ExtendedNode n : nodeList) {
sb.append(nodeToString(n));
sb.append("\n");
}
return sb.toString();
}
protected String nodeToString(ExtendedNode n) {
if (n.getType() == ExtendedNodeType.CONDITIONAL_JUMP) {
ConditionalJump t = (ConditionalJump) n;
return "TwoTargetConditionalJump("
+ resolveLabel(t.getThenLabel())
+ ","
+ resolveLabel(t.getElseLabel())
+ ")";
} else if (n.getType() == ExtendedNodeType.UNCONDITIONAL_JUMP) {
return "UnconditionalJump(" + resolveLabel(n.getLabel()) + ")";
} else {
return n.toString();
}
}
private String resolveLabel(Label label) {
Integer index = bindings.get(label);
if (index == null) {
return "null";
}
return nodeToString(nodeList.get(index));
}
}
/**
* Class that performs phase one of the translation process. It generates
* the following information:
*
* A sequence of extended nodes.
* A set of bindings from {@link Label}s to positions in the node
* sequence.
* A set of leader nodes that give rise to basic blocks in phase two.
* A lookup map that gives the mapping from AST tree nodes to
* {@link Node}s.
*
*
*
*
* The return type of this scanner is {@link Node}. For expressions, the
* corresponding node is returned to allow linking between different nodes.
*
* However, for statements there is usually no single {@link Node} that is
* created, and thus no node is returned (rather, null is returned).
*
*
*
* Every {@code visit*} method is assumed to add at least one extended node
* to the list of nodes (which might only be a jump).
*
*/
public class CFGTranslationPhaseOne extends TreePathScanner {
public CFGTranslationPhaseOne() {}
/**
* Annotation processing environment and its associated type and tree
* utilities.
*/
protected ProcessingEnvironment env;
protected Elements elements;
protected Types types;
protected Trees trees;
protected TreeBuilder treeBuilder;
protected AnnotationProvider annotationProvider;
/**
* Current {@link Label} to which a break statement with no label should
* jump, or null if there is no valid destination.
*/
protected /*@Nullable*/ Label breakTargetL;
/**
* Map from AST label Names to CFG {@link Label}s for breaks. Each
* labeled statement creates two CFG {@link Label}s, one for break and
* one for continue.
*/
protected Map breakLabels;
/**
* Current {@link Label} to which a continue statement with no label
* should jump, or null if there is no valid destination.
*/
protected /*@Nullable*/ Label continueTargetL;
/**
* Map from AST label Names to CFG {@link Label}s for continues. Each
* labeled statement creates two CFG {@link Label}s, one for break and
* one for continue.
*/
protected Map continueLabels;
/**
* Maps from AST {@link Tree}s to {@link Node}s. Every Tree that produces
* a value will have at least one corresponding Node. Trees
* that undergo conversions, such as boxing or unboxing, can map to two
* distinct Nodes. The Node for the pre-conversion value is stored
* in the treeLookupMap, while the Node for the post-conversion value
* is stored in the convertedTreeLookupMap.
*/
protected IdentityHashMap treeLookupMap;
/** Map from AST {@link Tree}s to post-conversion {@link Node}s. */
protected IdentityHashMap convertedTreeLookupMap;
/** The list of extended nodes. */
protected ArrayList nodeList;
/**
* The bindings of labels to positions (i.e., indices) in the
* {@code nodeList}.
*/
protected Map bindings;
/** The set of leaders (represented as indices into {@code nodeList}). */
protected Set leaders;
/**
* All return nodes (if any) encountered. Only includes return
* statements that actually return something
*/
private List returnNodes;
/**
* Nested scopes of try-catch blocks in force at the current
* program point.
*/
private TryStack tryStack;
/**
* Performs the actual work of phase one.
*
* @param bodyPath
* path to the body of the underlying AST's method
* @param env
* annotation processing environment containing type
* utilities
* @param underlyingAST
* the AST for which the CFG is to be built
* @param exceptionalExitLabel
* the label for exceptional exits from the CFG
* @param treeBuilder
* builder for new AST nodes
* @param annotationProvider
* extracts annotations from AST nodes
* @return the result of phase one
*/
public PhaseOneResult process(
TreePath bodyPath,
ProcessingEnvironment env,
UnderlyingAST underlyingAST,
Label exceptionalExitLabel,
TreeBuilder treeBuilder,
AnnotationProvider annotationProvider) {
this.env = env;
this.tryStack = new TryStack(exceptionalExitLabel);
this.treeBuilder = treeBuilder;
this.annotationProvider = annotationProvider;
elements = env.getElementUtils();
types = env.getTypeUtils();
// initialize lists and maps
treeLookupMap = new IdentityHashMap<>();
convertedTreeLookupMap = new IdentityHashMap<>();
nodeList = new ArrayList<>();
bindings = new HashMap<>();
leaders = new HashSet<>();
breakLabels = new HashMap<>();
continueLabels = new HashMap<>();
returnNodes = new ArrayList<>();
// traverse AST of the method body
scan(bodyPath, null);
// add marker to indicate that the next block will be the exit block
// Note: if there is a return statement earlier in the method (which
// is always the case for non-void methods), then this is not
// strictly necessary. However, it is also not a problem, as it will
// just generate a degenerated control graph case that will be
// removed in a later phase.
nodeList.add(new UnconditionalJump(regularExitLabel));
return new PhaseOneResult(
underlyingAST,
treeLookupMap,
convertedTreeLookupMap,
nodeList,
bindings,
leaders,
returnNodes);
}
public PhaseOneResult process(
CompilationUnitTree root,
ProcessingEnvironment env,
UnderlyingAST underlyingAST,
Label exceptionalExitLabel,
TreeBuilder treeBuilder,
AnnotationProvider annotationProvider) {
trees = Trees.instance(env);
TreePath bodyPath = trees.getPath(root, underlyingAST.getCode());
return process(
bodyPath,
env,
underlyingAST,
exceptionalExitLabel,
treeBuilder,
annotationProvider);
}
/**
* Perform any actions required when CFG translation creates a
* new Tree that is not part of the original AST.
*
* @param tree the newly created Tree
*/
public void handleArtificialTree(Tree tree) {}
/* --------------------------------------------------------- */
/* Nodes and Labels Management */
/* --------------------------------------------------------- */
/**
* Add a node to the lookup map if it not already present.
*
* @param node
* The node to add to the lookup map.
*/
protected void addToLookupMap(Node node) {
Tree tree = node.getTree();
if (tree == null) {
return;
}
if (!treeLookupMap.containsKey(tree)) {
treeLookupMap.put(tree, node);
}
Tree enclosingParens = parenMapping.get(tree);
while (enclosingParens != null) {
treeLookupMap.put(enclosingParens, node);
enclosingParens = parenMapping.get(enclosingParens);
}
}
/**
* Add a node in the post-conversion lookup map. The node
* should refer to a Tree and that Tree should already be in
* the pre-conversion lookup map. This method is used to
* update the Tree-Node mapping with conversion nodes.
*
* @param node
* The node to add to the lookup map.
*/
protected void addToConvertedLookupMap(Node node) {
Tree tree = node.getTree();
addToConvertedLookupMap(tree, node);
}
/**
* Add a node in the post-conversion lookup map. The tree
* argument should already be in the pre-conversion lookup
* map. This method is used to update the Tree-Node mapping
* with conversion nodes.
*
* @param tree
* The tree used as a key in the map.
* @param node
* The node to add to the lookup map.
*/
protected void addToConvertedLookupMap(Tree tree, Node node) {
assert tree != null;
assert treeLookupMap.containsKey(tree);
convertedTreeLookupMap.put(tree, node);
}
/**
* Extend the list of extended nodes with a node.
*
* @param node
* The node to add.
* @return the same node (for convenience)
*/
protected T extendWithNode(T node) {
addToLookupMap(node);
extendWithExtendedNode(new NodeHolder(node));
return node;
}
/**
* Extend the list of extended nodes with a node, where
* {@code node} might throw the exception {@code cause}.
*
* @param node
* The node to add.
* @param cause
* An exception that the node might throw.
* @return the node holder
*/
protected NodeWithExceptionsHolder extendWithNodeWithException(
Node node, TypeMirror cause) {
addToLookupMap(node);
return extendWithNodeWithExceptions(node, Collections.singleton(cause));
}
/**
* Extend the list of extended nodes with a node, where
* {@code node} might throw any of the exception in
* {@code causes}.
*
* @param node
* The node to add.
* @param causes
* Set of exceptions that the node might throw.
* @return the node holder
*/
protected NodeWithExceptionsHolder extendWithNodeWithExceptions(
Node node, Set causes) {
addToLookupMap(node);
Map> exceptions = new HashMap<>();
for (TypeMirror cause : causes) {
exceptions.put(cause, tryStack.possibleLabels(cause));
}
NodeWithExceptionsHolder exNode = new NodeWithExceptionsHolder(node, exceptions);
extendWithExtendedNode(exNode);
return exNode;
}
/**
* Insert {@code node} after {@code pred} in
* the list of extended nodes, or append to the list if
* {@code pred} is not present.
*
* @param node
* The node to add.
* @param pred
* The desired predecessor of node.
* @return the node holder
*/
protected T insertNodeAfter(T node, Node pred) {
addToLookupMap(node);
insertExtendedNodeAfter(new NodeHolder(node), pred);
return node;
}
/**
* Insert a {@code node} that might throw the exception
* {@code cause} after {@code pred} in the list of
* extended nodes, or append to the list if {@code pred}
* is not present.
*
* @param node
* The node to add.
* @param causes
* Set of exceptions that the node might throw.
* @param pred
* The desired predecessor of node.
* @return the node holder
*/
protected NodeWithExceptionsHolder insertNodeWithExceptionsAfter(
Node node, Set causes, Node pred) {
addToLookupMap(node);
Map> exceptions = new HashMap<>();
for (TypeMirror cause : causes) {
exceptions.put(cause, tryStack.possibleLabels(cause));
}
NodeWithExceptionsHolder exNode = new NodeWithExceptionsHolder(node, exceptions);
insertExtendedNodeAfter(exNode, pred);
return exNode;
}
/**
* Extend the list of extended nodes with an extended node.
*
* @param n
* The extended node.
*/
protected void extendWithExtendedNode(ExtendedNode n) {
nodeList.add(n);
}
/**
* Insert {@code n} after the node {@code pred} in the
* list of extended nodes, or append {@code n} if {@code pred}
* is not present.
*
* @param n
* The extended node.
* @param pred
* The desired predecessor.
*/
protected void insertExtendedNodeAfter(ExtendedNode n, Node pred) {
int index = -1;
for (int i = 0; i < nodeList.size(); i++) {
ExtendedNode inList = nodeList.get(i);
if (inList instanceof NodeHolder || inList instanceof NodeWithExceptionsHolder) {
if (inList.getNode() == pred) {
index = i;
break;
}
}
}
if (index != -1) {
nodeList.add(index + 1, n);
// update bindings
for (Entry e : bindings.entrySet()) {
if (e.getValue() >= index + 1) {
bindings.put(e.getKey(), e.getValue() + 1);
}
}
// update leaders
Set newLeaders = new HashSet<>();
for (Integer l : leaders) {
if (l >= index + 1) {
newLeaders.add(l + 1);
} else {
newLeaders.add(l);
}
}
leaders = newLeaders;
} else {
nodeList.add(n);
}
}
/**
* Add the label {@code l} to the extended node that will be placed next
* in the sequence.
*/
protected void addLabelForNextNode(Label l) {
leaders.add(nodeList.size());
bindings.put(l, nodeList.size());
}
/* --------------------------------------------------------- */
/* Utility Methods */
/* --------------------------------------------------------- */
protected long uid = 0;
protected String uniqueName(String prefix) {
return prefix + "#num" + uid++;
}
/**
* If the input node is an unboxed primitive type, insert a call to the
* appropriate valueOf method, otherwise leave it alone.
*
* @param node
* in input node
* @return a Node representing the boxed version of the input, which may
* simply be the input node
*/
protected Node box(Node node) {
// For boxing conversion, see JLS 5.1.7
if (TypesUtils.isPrimitive(node.getType())) {
PrimitiveType primitive = types.getPrimitiveType(node.getType().getKind());
TypeMirror boxedType = types.getDeclaredType(types.boxedClass(primitive));
TypeElement boxedElement = (TypeElement) ((DeclaredType) boxedType).asElement();
IdentifierTree classTree = treeBuilder.buildClassUse(boxedElement);
handleArtificialTree(classTree);
ClassNameNode className = new ClassNameNode(classTree);
className.setInSource(false);
insertNodeAfter(className, node);
MemberSelectTree valueOfSelect = treeBuilder.buildValueOfMethodAccess(classTree);
handleArtificialTree(valueOfSelect);
MethodAccessNode valueOfAccess = new MethodAccessNode(valueOfSelect, className);
valueOfAccess.setInSource(false);
insertNodeAfter(valueOfAccess, className);
MethodInvocationTree valueOfCall =
treeBuilder.buildMethodInvocation(
valueOfSelect, (ExpressionTree) node.getTree());
handleArtificialTree(valueOfCall);
Node boxed =
new MethodInvocationNode(
valueOfCall,
valueOfAccess,
Collections.singletonList(node),
getCurrentPath());
boxed.setInSource(false);
// Add Throwable to account for unchecked exceptions
TypeElement throwableElement = elements.getTypeElement("java.lang.Throwable");
addToConvertedLookupMap(node.getTree(), boxed);
insertNodeWithExceptionsAfter(
boxed, Collections.singleton(throwableElement.asType()), valueOfAccess);
return boxed;
} else {
return node;
}
}
/**
* If the input node is a boxed type, unbox it, otherwise leave it
* alone.
*
* @param node
* in input node
* @return a Node representing the unboxed version of the input, which
* may simply be the input node
*/
protected Node unbox(Node node) {
if (TypesUtils.isBoxedPrimitive(node.getType())) {
MemberSelectTree primValueSelect =
treeBuilder.buildPrimValueMethodAccess(node.getTree());
handleArtificialTree(primValueSelect);
MethodAccessNode primValueAccess = new MethodAccessNode(primValueSelect, node);
primValueAccess.setInSource(false);
// Method access may throw NullPointerException
TypeElement npeElement = elements.getTypeElement("java.lang.NullPointerException");
insertNodeWithExceptionsAfter(
primValueAccess, Collections.singleton(npeElement.asType()), node);
MethodInvocationTree primValueCall =
treeBuilder.buildMethodInvocation(primValueSelect);
handleArtificialTree(primValueCall);
Node unboxed =
new MethodInvocationNode(
primValueCall,
primValueAccess,
Collections.emptyList(),
getCurrentPath());
unboxed.setInSource(false);
// Add Throwable to account for unchecked exceptions
TypeElement throwableElement = elements.getTypeElement("java.lang.Throwable");
addToConvertedLookupMap(node.getTree(), unboxed);
insertNodeWithExceptionsAfter(
unboxed, Collections.singleton(throwableElement.asType()), primValueAccess);
return unboxed;
} else {
return node;
}
}
private TreeInfo getTreeInfo(Tree tree) {
final TypeMirror type = InternalUtils.typeOf(tree);
final boolean boxed = TypesUtils.isBoxedPrimitive(type);
final TypeMirror unboxedType = boxed ? types.unboxedType(type) : type;
final boolean bool = TypesUtils.isBooleanType(type);
final boolean numeric = TypesUtils.isNumeric(unboxedType);
return new TreeInfo() {
@Override
public boolean isNumeric() {
return numeric;
}
@Override
public boolean isBoxed() {
return boxed;
}
@Override
public boolean isBoolean() {
return bool;
}
@Override
public TypeMirror unboxedType() {
return unboxedType;
}
};
}
/**
* @return the unboxed tree if necessary, as described in JLS 5.1.8
*/
private Node unboxAsNeeded(Node node, boolean boxed) {
return boxed ? unbox(node) : node;
}
/**
* Convert the input node to String type, if it isn't already.
*
* @param node
* an input node
* @return a Node with the value promoted to String, which may be the
* input node
*/
protected Node stringConversion(Node node) {
// For string conversion, see JLS 5.1.11
TypeElement stringElement = elements.getTypeElement("java.lang.String");
if (!TypesUtils.isString(node.getType())) {
Node converted =
new StringConversionNode(node.getTree(), node, stringElement.asType());
addToConvertedLookupMap(converted);
insertNodeAfter(converted, node);
return converted;
} else {
return node;
}
}
/**
* Perform unary numeric promotion on the input node.
*
* @param node
* a node producing a value of numeric primitive or boxed
* type
* @return a Node with the value promoted to the int, long float or
* double, which may be the input node
*/
protected Node unaryNumericPromotion(Node node) {
// For unary numeric promotion, see JLS 5.6.1
node = unbox(node);
switch (node.getType().getKind()) {
case BYTE:
case CHAR:
case SHORT:
{
TypeMirror intType = types.getPrimitiveType(TypeKind.INT);
Node widened = new WideningConversionNode(node.getTree(), node, intType);
addToConvertedLookupMap(widened);
insertNodeAfter(widened, node);
return widened;
}
default:
// Nothing to do.
break;
}
return node;
}
/**
* Returns true if the argument type is a numeric primitive or
* a boxed numeric primitive and false otherwise.
*/
protected boolean isNumericOrBoxed(TypeMirror type) {
if (TypesUtils.isBoxedPrimitive(type)) {
type = types.unboxedType(type);
}
return TypesUtils.isNumeric(type);
}
/**
* Compute the type to which two numeric types must be promoted
* before performing a binary numeric operation on them. The
* input types must both be numeric and the output type is primitive.
*
* @param left the type of the left operand
* @param right the type of the right operand
* @return a TypeMirror representing the binary numeric promoted type
*/
protected TypeMirror binaryPromotedType(TypeMirror left, TypeMirror right) {
if (TypesUtils.isBoxedPrimitive(left)) {
left = types.unboxedType(left);
}
if (TypesUtils.isBoxedPrimitive(right)) {
right = types.unboxedType(right);
}
TypeKind promotedTypeKind = TypesUtils.widenedNumericType(left, right);
return types.getPrimitiveType(promotedTypeKind);
}
/**
* Perform binary numeric promotion on the input node to make it match
* the expression type.
*
* @param node
* a node producing a value of numeric primitive or boxed
* type
* @param exprType
* the type to promote the value to
* @return a Node with the value promoted to the exprType, which may be
* the input node
*/
protected Node binaryNumericPromotion(Node node, TypeMirror exprType) {
// For binary numeric promotion, see JLS 5.6.2
node = unbox(node);
if (!types.isSameType(node.getType(), exprType)) {
Node widened = new WideningConversionNode(node.getTree(), node, exprType);
addToConvertedLookupMap(widened);
insertNodeAfter(widened, node);
return widened;
} else {
return node;
}
}
/**
* Perform widening primitive conversion on the input node to make it
* match the destination type.
*
* @param node
* a node producing a value of numeric primitive type
* @param destType
* the type to widen the value to
* @return a Node with the value widened to the exprType, which may be
* the input node
*/
protected Node widen(Node node, TypeMirror destType) {
// For widening conversion, see JLS 5.1.2
assert TypesUtils.isPrimitive(node.getType()) && TypesUtils.isPrimitive(destType)
: "widening must be applied to primitive types";
if (types.isSubtype(node.getType(), destType)
&& !types.isSameType(node.getType(), destType)) {
Node widened = new WideningConversionNode(node.getTree(), node, destType);
addToConvertedLookupMap(widened);
insertNodeAfter(widened, node);
return widened;
} else {
return node;
}
}
/**
* Perform narrowing conversion on the input node to make it match the
* destination type.
*
* @param node
* a node producing a value of numeric primitive type
* @param destType
* the type to narrow the value to
* @return a Node with the value narrowed to the exprType, which may be
* the input node
*/
protected Node narrow(Node node, TypeMirror destType) {
// For narrowing conversion, see JLS 5.1.3
assert TypesUtils.isPrimitive(node.getType()) && TypesUtils.isPrimitive(destType)
: "narrowing must be applied to primitive types";
if (types.isSubtype(destType, node.getType())
&& !types.isSameType(destType, node.getType())) {
Node narrowed = new NarrowingConversionNode(node.getTree(), node, destType);
addToConvertedLookupMap(narrowed);
insertNodeAfter(narrowed, node);
return narrowed;
} else {
return node;
}
}
/**
* Perform narrowing conversion and optionally boxing conversion on the
* input node to make it match the destination type.
*
* @param node
* a node producing a value of numeric primitive type
* @param destType
* the type to narrow the value to (possibly boxed)
* @return a Node with the value narrowed and boxed to the destType,
* which may be the input node
*/
protected Node narrowAndBox(Node node, TypeMirror destType) {
if (TypesUtils.isBoxedPrimitive(destType)) {
return box(narrow(node, types.unboxedType(destType)));
} else {
return narrow(node, destType);
}
}
/**
* Return whether a conversion from the type of the node to varType
* requires narrowing.
*
* @param varType the type of a variable (or general LHS) to be converted to
* @param node a node whose value is being converted
* @return whether this conversion requires narrowing to succeed
*/
protected boolean conversionRequiresNarrowing(TypeMirror varType, Node node) {
// Narrowing is restricted to cases where the left hand side
// is byte, char, short or Byte, Char, Short and the right
// hand side is a constant.
TypeMirror unboxedVarType =
TypesUtils.isBoxedPrimitive(varType) ? types.unboxedType(varType) : varType;
TypeKind unboxedVarKind = unboxedVarType.getKind();
boolean isLeftNarrowableTo =
unboxedVarKind == TypeKind.BYTE
|| unboxedVarKind == TypeKind.SHORT
|| unboxedVarKind == TypeKind.CHAR;
boolean isRightConstant = node instanceof ValueLiteralNode;
return isLeftNarrowableTo && isRightConstant;
}
/**
* Assignment conversion and method invocation conversion are almost
* identical, except that assignment conversion allows narrowing. We
* factor out the common logic here.
*
* @param node
* a Node producing a value
* @param varType
* the type of a variable
* @param contextAllowsNarrowing
* whether to allow narrowing (for assignment conversion) or
* not (for method invocation conversion)
* @return a Node with the value converted to the type of the variable,
* which may be the input node itself
*/
protected Node commonConvert(
Node node, TypeMirror varType, boolean contextAllowsNarrowing) {
// For assignment conversion, see JLS 5.2
// For method invocation conversion, see JLS 5.3
// Check for identical types or "identity conversion"
TypeMirror nodeType = node.getType();
boolean isSameType = types.isSameType(nodeType, varType);
if (isSameType) {
return node;
}
boolean isRightNumeric = TypesUtils.isNumeric(nodeType);
boolean isRightPrimitive = TypesUtils.isPrimitive(nodeType);
boolean isRightBoxed = TypesUtils.isBoxedPrimitive(nodeType);
boolean isRightReference = nodeType instanceof ReferenceType;
boolean isLeftNumeric = TypesUtils.isNumeric(varType);
boolean isLeftPrimitive = TypesUtils.isPrimitive(varType);
// boolean isLeftBoxed = TypesUtils.isBoxedPrimitive(varType);
boolean isLeftReference = varType instanceof ReferenceType;
boolean isSubtype = types.isSubtype(nodeType, varType);
if (isRightNumeric && isLeftNumeric && isSubtype) {
node = widen(node, varType);
nodeType = node.getType();
} else if (isRightReference && isLeftReference && isSubtype) {
// widening reference conversion is a no-op, but if it
// applies, then later conversions do not.
} else if (isRightPrimitive && isLeftReference) {
if (contextAllowsNarrowing && conversionRequiresNarrowing(varType, node)) {
node = narrowAndBox(node, varType);
nodeType = node.getType();
} else {
node = box(node);
nodeType = node.getType();
}
} else if (isRightBoxed && isLeftPrimitive) {
node = unbox(node);
nodeType = node.getType();
if (types.isSubtype(nodeType, varType) && !types.isSameType(nodeType, varType)) {
node = widen(node, varType);
nodeType = node.getType();
}
} else if (isRightPrimitive && isLeftPrimitive) {
if (contextAllowsNarrowing && conversionRequiresNarrowing(varType, node)) {
node = narrow(node, varType);
nodeType = node.getType();
}
}
// TODO: if checkers need to know about null references of
// a particular type, add logic for them here.
return node;
}
/**
* Perform assignment conversion so that it can be assigned to a
* variable of the given type.
*
* @param node
* a Node producing a value
* @param varType
* the type of a variable
* @return a Node with the value converted to the type of the variable,
* which may be the input node itself
*/
protected Node assignConvert(Node node, TypeMirror varType) {
return commonConvert(node, varType, true);
}
/**
* Perform method invocation conversion so that the node can be passed
* as a formal parameter of the given type.
*
* @param node
* a Node producing a value
* @param formalType
* the type of a formal parameter
* @return a Node with the value converted to the type of the formal,
* which may be the input node itself
*/
protected Node methodInvocationConvert(Node node, TypeMirror formalType) {
return commonConvert(node, formalType, false);
}
/**
* Given a method element and as list of argument expressions, return a
* list of {@link Node}s representing the arguments converted for a call
* of the method. This method applies to both method invocations and
* constructor calls.
*
* @param method
* an ExecutableElement representing a method to be called
* @param actualExprs
* a List of argument expressions to a call
* @return a List of {@link Node}s representing arguments after
* conversions required by a call to this method.
*/
protected List convertCallArguments(
ExecutableElement method, List actualExprs) {
// NOTE: It is important to convert one method argument before
// generating CFG nodes for the next argument, since label binding
// expects nodes to be generated in execution order. Therefore,
// this method first determines which conversions need to be applied
// and then iterates over the actual arguments.
List formals = method.getParameters();
ArrayList convertedNodes = new ArrayList();
int numFormals = formals.size();
int numActuals = actualExprs.size();
if (method.isVarArgs()) {
// Create a new array argument if the actuals outnumber
// the formals, or if the last actual is not assignable
// to the last formal.
int lastArgIndex = numFormals - 1;
TypeMirror lastParamType = formals.get(lastArgIndex).asType();
List dimensions = new ArrayList<>();
List initializers = new ArrayList<>();
if (numActuals == numFormals - 1) {
// Apply method invocation conversion to all actual
// arguments, then create and append an empty array
for (int i = 0; i < numActuals; i++) {
Node actualVal = scan(actualExprs.get(i), null);
convertedNodes.add(
methodInvocationConvert(actualVal, formals.get(i).asType()));
}
Node lastArgument =
new ArrayCreationNode(null, lastParamType, dimensions, initializers);
extendWithNode(lastArgument);
convertedNodes.add(lastArgument);
} else {
TypeMirror actualType = InternalUtils.typeOf(actualExprs.get(lastArgIndex));
if (numActuals == numFormals && types.isAssignable(actualType, lastParamType)) {
// Normal call with no array creation, apply method
// invocation conversion to all arguments.
for (int i = 0; i < numActuals; i++) {
Node actualVal = scan(actualExprs.get(i), null);
convertedNodes.add(
methodInvocationConvert(actualVal, formals.get(i).asType()));
}
} else {
assert lastParamType instanceof ArrayType
: "variable argument formal must be an array";
// Apply method invocation conversion to lastArgIndex
// arguments and use the remaining ones to initialize
// an array.
for (int i = 0; i < lastArgIndex; i++) {
Node actualVal = scan(actualExprs.get(i), null);
convertedNodes.add(
methodInvocationConvert(actualVal, formals.get(i).asType()));
}
TypeMirror elemType = ((ArrayType) lastParamType).getComponentType();
for (int i = lastArgIndex; i < numActuals; i++) {
Node actualVal = scan(actualExprs.get(i), null);
initializers.add(assignConvert(actualVal, elemType));
}
Node lastArgument =
new ArrayCreationNode(
null, lastParamType, dimensions, initializers);
extendWithNode(lastArgument);
convertedNodes.add(lastArgument);
}
}
} else {
for (int i = 0; i < numActuals; i++) {
Node actualVal = scan(actualExprs.get(i), null);
convertedNodes.add(methodInvocationConvert(actualVal, formals.get(i).asType()));
}
}
return convertedNodes;
}
/**
* Convert an operand of a conditional expression to the type of the
* whole expression.
*
* @param node
* a node occurring as the second or third operand of
* a conditional expression
* @param destType
* the type to promote the value to
* @return a Node with the value promoted to the destType, which may be
* the input node
*/
protected Node conditionalExprPromotion(Node node, TypeMirror destType) {
// For rules on converting operands of conditional expressions,
// JLS 15.25
TypeMirror nodeType = node.getType();
// If the operand is already the same type as the whole
// expression, then do nothing.
if (types.isSameType(nodeType, destType)) {
return node;
}
// If the operand is a primitive and the whole expression is
// boxed, then apply boxing.
if (TypesUtils.isPrimitive(nodeType) && TypesUtils.isBoxedPrimitive(destType)) {
return box(node);
}
// If the operand is byte or Byte and the whole expression is
// short, then convert to short.
boolean isBoxedPrimitive = TypesUtils.isBoxedPrimitive(nodeType);
TypeMirror unboxedNodeType = isBoxedPrimitive ? types.unboxedType(nodeType) : nodeType;
TypeMirror unboxedDestType =
TypesUtils.isBoxedPrimitive(destType) ? types.unboxedType(destType) : destType;
if (TypesUtils.isNumeric(unboxedNodeType) && TypesUtils.isNumeric(unboxedDestType)) {
if (unboxedNodeType.getKind() == TypeKind.BYTE
&& destType.getKind() == TypeKind.SHORT) {
if (isBoxedPrimitive) {
node = unbox(node);
}
return widen(node, destType);
}
// If the operand is Byte, Short or Character and the whole expression
// is the unboxed version of it, then apply unboxing.
TypeKind destKind = destType.getKind();
if (destKind == TypeKind.BYTE
|| destKind == TypeKind.CHAR
|| destKind == TypeKind.SHORT) {
if (isBoxedPrimitive) {
return unbox(node);
} else if (nodeType.getKind() == TypeKind.INT) {
return narrow(node, destType);
}
}
return binaryNumericPromotion(node, destType);
}
// For the final case in JLS 15.25, apply boxing but not lub.
if (TypesUtils.isPrimitive(nodeType)
&& (destType.getKind() == TypeKind.DECLARED
|| destType.getKind() == TypeKind.UNION
|| destType.getKind() == TypeKind.INTERSECTION)) {
return box(node);
}
return node;
}
/**
* Returns the label {@link Name} of the leaf in the argument path, or
* null if the leaf is not a labeled statement.
*/
protected /*@Nullable*/ Name getLabel(TreePath path) {
if (path.getParentPath() != null) {
Tree parent = path.getParentPath().getLeaf();
if (parent.getKind() == Tree.Kind.LABELED_STATEMENT) {
return ((LabeledStatementTree) parent).getLabel();
}
}
return null;
}
/* --------------------------------------------------------- */
/* Visitor Methods */
/* --------------------------------------------------------- */
@Override
public Node visitAnnotatedType(AnnotatedTypeTree tree, Void p) {
return scan(tree.getUnderlyingType(), p);
}
@Override
public Node visitAnnotation(AnnotationTree tree, Void p) {
assert false : "AnnotationTree is unexpected in AST to CFG translation";
return null;
}
@Override
public MethodInvocationNode visitMethodInvocation(MethodInvocationTree tree, Void p) {
// see JLS 15.12.4
// First, compute the receiver, if any (15.12.4.1)
// Second, evaluate the actual arguments, left to right and
// possibly some arguments are stored into an array for variable
// arguments calls (15.12.4.2)
// Third, test the receiver, if any, for nullness (15.12.4.4)
// Fourth, convert the arguments to the type of the formal
// parameters (15.12.4.5)
// Fifth, if the method is synchronized, lock the receiving
// object or class (15.12.4.5)
ExecutableElement method = TreeUtils.elementFromUse(tree);
if (method == null) {
// The method wasn't found, e.g. because of a compilation error.
return null;
}
// TODO? Variable wasn't used.
// boolean isBooleanMethod = TypesUtils.isBooleanType(method.getReturnType());
ExpressionTree methodSelect = tree.getMethodSelect();
assert TreeUtils.isMethodAccess(methodSelect)
: "Expected a method access, but got: " + methodSelect;
List actualExprs = tree.getArguments();
// Look up method to invoke and possibly throw NullPointerException
Node receiver = getReceiver(methodSelect, TreeUtils.enclosingClass(getCurrentPath()));
MethodAccessNode target = new MethodAccessNode(methodSelect, receiver);
ExecutableElement element = TreeUtils.elementFromUse(tree);
if (ElementUtils.isStatic(element) || receiver instanceof ThisLiteralNode) {
// No NullPointerException can be thrown, use normal node
extendWithNode(target);
} else {
TypeElement npeElement = elements.getTypeElement("java.lang.NullPointerException");
extendWithNodeWithException(target, npeElement.asType());
}
List arguments = new ArrayList<>();
// Don't convert arguments for enum super calls. The AST contains
// no actual arguments, while the method element expects two arguments,
// leading to an exception in convertCallArguments. Since no actual
// arguments are present in the AST that is being checked, it shouldn't
// cause any harm to omit the conversions.
// See also BaseTypeVisitor.visitMethodInvocation and
// QualifierPolymorphism.annotate
if (!TreeUtils.isEnumSuper(tree)) {
arguments = convertCallArguments(method, actualExprs);
}
// TODO: lock the receiver for synchronized methods
MethodInvocationNode node =
new MethodInvocationNode(tree, target, arguments, getCurrentPath());
Set thrownSet = new HashSet<>();
// Add exceptions explicitly mentioned in the throws clause.
List thrownTypes = element.getThrownTypes();
thrownSet.addAll(thrownTypes);
// Add Throwable to account for unchecked exceptions
TypeElement throwableElement = elements.getTypeElement("java.lang.Throwable");
thrownSet.add(throwableElement.asType());
ExtendedNode extendedNode = extendWithNodeWithExceptions(node, thrownSet);
/* Check for the TerminatesExecution annotation. */
Element methodElement = InternalUtils.symbol(tree);
boolean terminatesExecution =
annotationProvider.getDeclAnnotation(methodElement, TerminatesExecution.class)
!= null;
if (terminatesExecution) {
extendedNode.setTerminatesExecution(true);
}
return node;
}
@Override
public Node visitAssert(AssertTree tree, Void p) {
// see JLS 14.10
// If assertions are enabled, then we can just translate the
// assertion.
if (assumeAssertionsEnabled || assumeAssertionsEnabledFor(tree)) {
translateAssertWithAssertionsEnabled(tree);
return null;
}
// If assertions are disabled, then nothing is executed.
if (assumeAssertionsDisabled) {
return null;
}
// Otherwise, we don't know if assertions are enabled, so we use a
// variable "ea" and case-split on it. One branch does execute the
// assertion, while the other assumes assertions are disabled.
VariableTree ea = getAssertionsEnabledVariable();
// all necessary labels
Label assertionEnabled = new Label();
Label assertionDisabled = new Label();
extendWithNode(new LocalVariableNode(ea));
extendWithExtendedNode(new ConditionalJump(assertionEnabled, assertionDisabled));
// 'then' branch (i.e. check the assertion)
addLabelForNextNode(assertionEnabled);
translateAssertWithAssertionsEnabled(tree);
// 'else' branch
addLabelForNextNode(assertionDisabled);
return null;
}
/**
* Should assertions be assumed to be executed for a given
* {@link AssertTree}? False by default.
*/
protected boolean assumeAssertionsEnabledFor(AssertTree tree) {
return false;
}
/**
* The {@link VariableTree} that indicates whether assertions are
* enabled or not.
*/
protected VariableTree ea = null;
/**
* Get a synthetic {@link VariableTree} that indicates whether assertions are
* enabled or not.
*/
protected VariableTree getAssertionsEnabledVariable() {
if (ea == null) {
String name = uniqueName("assertionsEnabled");
MethodTree enclosingMethod = TreeUtils.enclosingMethod(getCurrentPath());
Element owner;
if (enclosingMethod != null) {
owner = TreeUtils.elementFromDeclaration(enclosingMethod);
} else {
ClassTree enclosingClass = TreeUtils.enclosingClass(getCurrentPath());
owner = TreeUtils.elementFromDeclaration(enclosingClass);
}
ExpressionTree initializer = null;
ea =
treeBuilder.buildVariableDecl(
types.getPrimitiveType(TypeKind.BOOLEAN), name, owner, initializer);
}
return ea;
}
/**
* Translates an assertion statement to the correct CFG nodes. The
* translation assumes that assertions are enabled.
*/
protected void translateAssertWithAssertionsEnabled(AssertTree tree) {
// all necessary labels
Label assertEnd = new Label();
Label elseEntry = new Label();
// basic block for the condition
Node condition = unbox(scan(tree.getCondition(), null));
ConditionalJump cjump = new ConditionalJump(assertEnd, elseEntry);
extendWithExtendedNode(cjump);
// else branch
Node detail = null;
addLabelForNextNode(elseEntry);
if (tree.getDetail() != null) {
detail = scan(tree.getDetail(), null);
}
TypeElement assertException = elements.getTypeElement("java.lang.AssertionError");
AssertionErrorNode assertNode =
new AssertionErrorNode(tree, condition, detail, assertException.asType());
extendWithNode(assertNode);
NodeWithExceptionsHolder exNode =
extendWithNodeWithException(
new ThrowNode(null, assertNode, env.getTypeUtils()),
assertException.asType());
exNode.setTerminatesExecution(true);
// then branch (nothing happens)
addLabelForNextNode(assertEnd);
}
@Override
public Node visitAssignment(AssignmentTree tree, Void p) {
// see JLS 15.26.1
AssignmentNode assignmentNode;
ExpressionTree variable = tree.getVariable();
TypeMirror varType = InternalUtils.typeOf(variable);
// case 1: field access
if (TreeUtils.isFieldAccess(variable)) {
// visit receiver
Node receiver = getReceiver(variable, TreeUtils.enclosingClass(getCurrentPath()));
// visit expression
Node expression = scan(tree.getExpression(), p);
expression = assignConvert(expression, varType);
// visit field access (throws null-pointer exception)
FieldAccessNode target = new FieldAccessNode(variable, receiver);
target.setLValue();
Element element = TreeUtils.elementFromUse(variable);
if (ElementUtils.isStatic(element) || receiver instanceof ThisLiteralNode) {
// No NullPointerException can be thrown, use normal node
extendWithNode(target);
} else {
TypeElement npeElement =
elements.getTypeElement("java.lang.NullPointerException");
extendWithNodeWithException(target, npeElement.asType());
}
// add assignment node
assignmentNode = new AssignmentNode(tree, target, expression);
extendWithNode(assignmentNode);
}
// case 2: other cases
else {
Node target = scan(variable, p);
target.setLValue();
assignmentNode = translateAssignment(tree, target, tree.getExpression());
}
return assignmentNode;
}
/**
* Translate an assignment.
*/
protected AssignmentNode translateAssignment(Tree tree, Node target, ExpressionTree rhs) {
Node expression = scan(rhs, null);
return translateAssignment(tree, target, expression);
}
/**
* Translate an assignment where the RHS has already been scanned.
*/
protected AssignmentNode translateAssignment(Tree tree, Node target, Node expression) {
assert tree instanceof AssignmentTree || tree instanceof VariableTree;
target.setLValue();
expression = assignConvert(expression, target.getType());
AssignmentNode assignmentNode = new AssignmentNode(tree, target, expression);
extendWithNode(assignmentNode);
return assignmentNode;
}
/**
* Note 1: Requires {@code tree} to be a field or method access
* tree.
*
* Note 2: Visits the receiver and adds all necessary blocks to the CFG.
*
* @param tree
* the field access tree containing the receiver
* @param classTree
* the ClassTree enclosing the field access
* @return the receiver of the field access
*/
private Node getReceiver(Tree tree, ClassTree classTree) {
assert TreeUtils.isFieldAccess(tree) || TreeUtils.isMethodAccess(tree);
if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
MemberSelectTree mtree = (MemberSelectTree) tree;
return scan(mtree.getExpression(), null);
} else {
TypeMirror classType = InternalUtils.typeOf(classTree);
Node node = new ImplicitThisLiteralNode(classType);
extendWithNode(node);
return node;
}
}
/**
* Map an operation with assignment to the corresponding operation
* without assignment.
*
* @param kind a Tree.Kind representing an operation with assignment
* @return the Tree.Kind for the same operation without assignment
*/
protected Tree.Kind withoutAssignment(Tree.Kind kind) {
switch (kind) {
case DIVIDE_ASSIGNMENT:
return Tree.Kind.DIVIDE;
case MULTIPLY_ASSIGNMENT:
return Tree.Kind.MULTIPLY;
case REMAINDER_ASSIGNMENT:
return Tree.Kind.REMAINDER;
case MINUS_ASSIGNMENT:
return Tree.Kind.MINUS;
case PLUS_ASSIGNMENT:
return Tree.Kind.PLUS;
case LEFT_SHIFT_ASSIGNMENT:
return Tree.Kind.LEFT_SHIFT;
case RIGHT_SHIFT_ASSIGNMENT:
return Tree.Kind.RIGHT_SHIFT;
case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
return Tree.Kind.UNSIGNED_RIGHT_SHIFT;
case AND_ASSIGNMENT:
return Tree.Kind.AND;
case OR_ASSIGNMENT:
return Tree.Kind.OR;
case XOR_ASSIGNMENT:
return Tree.Kind.XOR;
default:
return Tree.Kind.ERRONEOUS;
}
}
@Override
public Node visitCompoundAssignment(CompoundAssignmentTree tree, Void p) {
// According the JLS 15.26.2, E1 op= E2 is equivalent to
// E1 = (T) ((E1) op (E2)), where T is the type of E1,
// except that E1 is evaluated only once.
//
Tree.Kind kind = tree.getKind();
switch (kind) {
case DIVIDE_ASSIGNMENT:
case MULTIPLY_ASSIGNMENT:
case REMAINDER_ASSIGNMENT:
{
// see JLS 15.17 and 15.26.2
Node targetLHS = scan(tree.getVariable(), p);
Node value = scan(tree.getExpression(), p);
TypeMirror exprType = InternalUtils.typeOf(tree);
TypeMirror leftType = InternalUtils.typeOf(tree.getVariable());
TypeMirror rightType = InternalUtils.typeOf(tree.getExpression());
TypeMirror promotedType = binaryPromotedType(leftType, rightType);
Node targetRHS = binaryNumericPromotion(targetLHS, promotedType);
value = binaryNumericPromotion(value, promotedType);
BinaryTree operTree =
treeBuilder.buildBinary(
promotedType,
withoutAssignment(kind),
tree.getVariable(),
tree.getExpression());
handleArtificialTree(operTree);
Node operNode;
if (kind == Tree.Kind.MULTIPLY_ASSIGNMENT) {
operNode = new NumericalMultiplicationNode(operTree, targetRHS, value);
} else if (kind == Tree.Kind.DIVIDE_ASSIGNMENT) {
if (TypesUtils.isIntegral(exprType)) {
operNode = new IntegerDivisionNode(operTree, targetRHS, value);
} else {
operNode = new FloatingDivisionNode(operTree, targetRHS, value);
}
} else {
assert kind == Kind.REMAINDER_ASSIGNMENT;
if (TypesUtils.isIntegral(exprType)) {
operNode = new IntegerRemainderNode(operTree, targetRHS, value);
} else {
operNode = new FloatingRemainderNode(operTree, targetRHS, value);
}
}
extendWithNode(operNode);
TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree);
handleArtificialTree(castTree);
TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType);
castNode.setInSource(false);
extendWithNode(castNode);
AssignmentNode assignNode = new AssignmentNode(tree, targetLHS, castNode);
extendWithNode(assignNode);
return assignNode;
}
case MINUS_ASSIGNMENT:
case PLUS_ASSIGNMENT:
{
// see JLS 15.18 and 15.26.2
Node targetLHS = scan(tree.getVariable(), p);
Node value = scan(tree.getExpression(), p);
TypeMirror leftType = InternalUtils.typeOf(tree.getVariable());
TypeMirror rightType = InternalUtils.typeOf(tree.getExpression());
if (TypesUtils.isString(leftType) || TypesUtils.isString(rightType)) {
assert (kind == Tree.Kind.PLUS_ASSIGNMENT);
Node targetRHS = stringConversion(targetLHS);
value = stringConversion(value);
Node r = new StringConcatenateAssignmentNode(tree, targetRHS, value);
extendWithNode(r);
return r;
} else {
TypeMirror promotedType = binaryPromotedType(leftType, rightType);
Node targetRHS = binaryNumericPromotion(targetLHS, promotedType);
value = binaryNumericPromotion(value, promotedType);
BinaryTree operTree =
treeBuilder.buildBinary(
promotedType,
withoutAssignment(kind),
tree.getVariable(),
tree.getExpression());
handleArtificialTree(operTree);
Node operNode;
if (kind == Tree.Kind.PLUS_ASSIGNMENT) {
operNode = new NumericalAdditionNode(operTree, targetRHS, value);
} else {
assert kind == Kind.MINUS_ASSIGNMENT;
operNode = new NumericalSubtractionNode(operTree, targetRHS, value);
}
extendWithNode(operNode);
TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree);
handleArtificialTree(castTree);
TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType);
castNode.setInSource(false);
extendWithNode(castNode);
// Map the compound assignment tree to an assignment node, which
// will have the correct type.
AssignmentNode assignNode =
new AssignmentNode(tree, targetLHS, castNode);
extendWithNode(assignNode);
return assignNode;
}
}
case LEFT_SHIFT_ASSIGNMENT:
case RIGHT_SHIFT_ASSIGNMENT:
case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
{
// see JLS 15.19 and 15.26.2
Node targetLHS = scan(tree.getVariable(), p);
Node value = scan(tree.getExpression(), p);
TypeMirror leftType = InternalUtils.typeOf(tree.getVariable());
Node targetRHS = unaryNumericPromotion(targetLHS);
value = unaryNumericPromotion(value);
BinaryTree operTree =
treeBuilder.buildBinary(
leftType,
withoutAssignment(kind),
tree.getVariable(),
tree.getExpression());
handleArtificialTree(operTree);
Node operNode;
if (kind == Tree.Kind.LEFT_SHIFT_ASSIGNMENT) {
operNode = new LeftShiftNode(operTree, targetRHS, value);
} else if (kind == Tree.Kind.RIGHT_SHIFT_ASSIGNMENT) {
operNode = new SignedRightShiftNode(operTree, targetRHS, value);
} else {
assert kind == Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT;
operNode = new UnsignedRightShiftNode(operTree, targetRHS, value);
}
extendWithNode(operNode);
TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree);
handleArtificialTree(castTree);
TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType);
castNode.setInSource(false);
extendWithNode(castNode);
AssignmentNode assignNode = new AssignmentNode(tree, targetLHS, castNode);
extendWithNode(assignNode);
return assignNode;
}
case AND_ASSIGNMENT:
case OR_ASSIGNMENT:
case XOR_ASSIGNMENT:
// see JLS 15.22
Node targetLHS = scan(tree.getVariable(), p);
Node value = scan(tree.getExpression(), p);
TypeMirror leftType = InternalUtils.typeOf(tree.getVariable());
TypeMirror rightType = InternalUtils.typeOf(tree.getExpression());
Node targetRHS = null;
if (isNumericOrBoxed(leftType) && isNumericOrBoxed(rightType)) {
TypeMirror promotedType = binaryPromotedType(leftType, rightType);
targetRHS = binaryNumericPromotion(targetLHS, promotedType);
value = binaryNumericPromotion(value, promotedType);
} else if (TypesUtils.isBooleanType(leftType)
&& TypesUtils.isBooleanType(rightType)) {
targetRHS = unbox(targetLHS);
value = unbox(value);
} else {
assert false
: "Both argument to logical operation must be numeric or boolean";
}
BinaryTree operTree =
treeBuilder.buildBinary(
leftType,
withoutAssignment(kind),
tree.getVariable(),
tree.getExpression());
handleArtificialTree(operTree);
Node operNode;
if (kind == Tree.Kind.AND_ASSIGNMENT) {
operNode = new BitwiseAndNode(operTree, targetRHS, value);
} else if (kind == Tree.Kind.OR_ASSIGNMENT) {
operNode = new BitwiseOrNode(operTree, targetRHS, value);
} else {
assert kind == Kind.XOR_ASSIGNMENT;
operNode = new BitwiseXorNode(operTree, targetRHS, value);
}
extendWithNode(operNode);
TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree);
handleArtificialTree(castTree);
TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType);
castNode.setInSource(false);
extendWithNode(castNode);
AssignmentNode assignNode = new AssignmentNode(tree, targetLHS, castNode);
extendWithNode(assignNode);
return assignNode;
default:
assert false : "unexpected compound assignment type";
break;
}
assert false : "unexpected compound assignment type";
return null;
}
@Override
public Node visitBinary(BinaryTree tree, Void p) {
// Note that for binary operations it is important to perform any required
// promotion on the left operand before generating any Nodes for the right
// operand, because labels must be inserted AFTER ALL preceding Nodes and
// BEFORE ALL following Nodes.
Node r = null;
Tree leftTree = tree.getLeftOperand();
Tree rightTree = tree.getRightOperand();
Tree.Kind kind = tree.getKind();
switch (kind) {
case DIVIDE:
case MULTIPLY:
case REMAINDER:
{
// see JLS 15.17
TypeMirror exprType = InternalUtils.typeOf(tree);
TypeMirror leftType = InternalUtils.typeOf(leftTree);
TypeMirror rightType = InternalUtils.typeOf(rightTree);
TypeMirror promotedType = binaryPromotedType(leftType, rightType);
Node left = binaryNumericPromotion(scan(leftTree, p), promotedType);
Node right = binaryNumericPromotion(scan(rightTree, p), promotedType);
if (kind == Tree.Kind.MULTIPLY) {
r = new NumericalMultiplicationNode(tree, left, right);
} else if (kind == Tree.Kind.DIVIDE) {
if (TypesUtils.isIntegral(exprType)) {
r = new IntegerDivisionNode(tree, left, right);
} else {
r = new FloatingDivisionNode(tree, left, right);
}
} else {
assert kind == Kind.REMAINDER;
if (TypesUtils.isIntegral(exprType)) {
r = new IntegerRemainderNode(tree, left, right);
} else {
r = new FloatingRemainderNode(tree, left, right);
}
}
break;
}
case MINUS:
case PLUS:
{
// see JLS 15.18
// TypeMirror exprType = InternalUtils.typeOf(tree);
TypeMirror leftType = InternalUtils.typeOf(leftTree);
TypeMirror rightType = InternalUtils.typeOf(rightTree);
if (TypesUtils.isString(leftType) || TypesUtils.isString(rightType)) {
assert (kind == Tree.Kind.PLUS);
Node left = stringConversion(scan(leftTree, p));
Node right = stringConversion(scan(rightTree, p));
r = new StringConcatenateNode(tree, left, right);
} else {
TypeMirror promotedType = binaryPromotedType(leftType, rightType);
Node left = binaryNumericPromotion(scan(leftTree, p), promotedType);
Node right = binaryNumericPromotion(scan(rightTree, p), promotedType);
// TODO: Decide whether to deal with floating-point value
// set conversion.
if (kind == Tree.Kind.PLUS) {
r = new NumericalAdditionNode(tree, left, right);
} else {
assert kind == Kind.MINUS;
r = new NumericalSubtractionNode(tree, left, right);
}
}
break;
}
case LEFT_SHIFT:
case RIGHT_SHIFT:
case UNSIGNED_RIGHT_SHIFT:
{
// see JLS 15.19
Node left = unaryNumericPromotion(scan(leftTree, p));
Node right = unaryNumericPromotion(scan(rightTree, p));
if (kind == Tree.Kind.LEFT_SHIFT) {
r = new LeftShiftNode(tree, left, right);
} else if (kind == Tree.Kind.RIGHT_SHIFT) {
r = new SignedRightShiftNode(tree, left, right);
} else {
assert kind == Kind.UNSIGNED_RIGHT_SHIFT;
r = new UnsignedRightShiftNode(tree, left, right);
}
break;
}
case GREATER_THAN:
case GREATER_THAN_EQUAL:
case LESS_THAN:
case LESS_THAN_EQUAL:
{
// see JLS 15.20.1
TypeMirror leftType = InternalUtils.typeOf(leftTree);
if (TypesUtils.isBoxedPrimitive(leftType)) {
leftType = types.unboxedType(leftType);
}
TypeMirror rightType = InternalUtils.typeOf(rightTree);
if (TypesUtils.isBoxedPrimitive(rightType)) {
rightType = types.unboxedType(rightType);
}
TypeMirror promotedType = binaryPromotedType(leftType, rightType);
Node left = binaryNumericPromotion(scan(leftTree, p), promotedType);
Node right = binaryNumericPromotion(scan(rightTree, p), promotedType);
Node node;
if (kind == Tree.Kind.GREATER_THAN) {
node = new GreaterThanNode(tree, left, right);
} else if (kind == Tree.Kind.GREATER_THAN_EQUAL) {
node = new GreaterThanOrEqualNode(tree, left, right);
} else if (kind == Tree.Kind.LESS_THAN) {
node = new LessThanNode(tree, left, right);
} else {
assert kind == Tree.Kind.LESS_THAN_EQUAL;
node = new LessThanOrEqualNode(tree, left, right);
}
extendWithNode(node);
return node;
}
case EQUAL_TO:
case NOT_EQUAL_TO:
{
// see JLS 15.21
TreeInfo leftInfo = getTreeInfo(leftTree);
TreeInfo rightInfo = getTreeInfo(rightTree);
Node left = scan(leftTree, p);
Node right = scan(rightTree, p);
if (leftInfo.isNumeric()
&& rightInfo.isNumeric()
&& !(leftInfo.isBoxed() && rightInfo.isBoxed())) {
// JLS 15.21.1 numerical equality
TypeMirror promotedType =
binaryPromotedType(
leftInfo.unboxedType(), rightInfo.unboxedType());
left = binaryNumericPromotion(left, promotedType);
right = binaryNumericPromotion(right, promotedType);
} else if (leftInfo.isBoolean()
&& rightInfo.isBoolean()
&& !(leftInfo.isBoxed() && rightInfo.isBoxed())) {
// JSL 15.21.2 boolean equality
left = unboxAsNeeded(left, leftInfo.isBoxed());
right = unboxAsNeeded(right, rightInfo.isBoxed());
}
Node node;
if (kind == Tree.Kind.EQUAL_TO) {
node = new EqualToNode(tree, left, right);
} else {
assert kind == Kind.NOT_EQUAL_TO;
node = new NotEqualNode(tree, left, right);
}
extendWithNode(node);
return node;
}
case AND:
case OR:
case XOR:
{
// see JLS 15.22
TypeMirror leftType = InternalUtils.typeOf(leftTree);
TypeMirror rightType = InternalUtils.typeOf(rightTree);
boolean isBooleanOp =
TypesUtils.isBooleanType(leftType)
&& TypesUtils.isBooleanType(rightType);
Node left;
Node right;
if (isBooleanOp) {
left = unbox(scan(leftTree, p));
right = unbox(scan(rightTree, p));
} else if (isNumericOrBoxed(leftType) && isNumericOrBoxed(rightType)) {
TypeMirror promotedType = binaryPromotedType(leftType, rightType);
left = binaryNumericPromotion(scan(leftTree, p), promotedType);
right = binaryNumericPromotion(scan(rightTree, p), promotedType);
} else {
left = unbox(scan(leftTree, p));
right = unbox(scan(rightTree, p));
}
Node node;
if (kind == Tree.Kind.AND) {
node = new BitwiseAndNode(tree, left, right);
} else if (kind == Tree.Kind.OR) {
node = new BitwiseOrNode(tree, left, right);
} else {
assert kind == Kind.XOR;
node = new BitwiseXorNode(tree, left, right);
}
extendWithNode(node);
return node;
}
case CONDITIONAL_AND:
case CONDITIONAL_OR:
{
// see JLS 15.23 and 15.24
// all necessary labels
Label rightStartL = new Label();
Label shortCircuitL = new Label();
// left-hand side
Node left = scan(leftTree, p);
ConditionalJump cjump;
if (kind == Tree.Kind.CONDITIONAL_AND) {
cjump = new ConditionalJump(rightStartL, shortCircuitL);
cjump.setFalseFlowRule(Store.FlowRule.ELSE_TO_ELSE);
} else {
cjump = new ConditionalJump(shortCircuitL, rightStartL);
cjump.setTrueFlowRule(Store.FlowRule.THEN_TO_THEN);
}
extendWithExtendedNode(cjump);
// right-hand side
addLabelForNextNode(rightStartL);
Node right = scan(rightTree, p);
// conditional expression itself
addLabelForNextNode(shortCircuitL);
Node node;
if (kind == Tree.Kind.CONDITIONAL_AND) {
node = new ConditionalAndNode(tree, left, right);
} else {
node = new ConditionalOrNode(tree, left, right);
}
extendWithNode(node);
return node;
}
default:
assert false : "unexpected binary tree: " + kind;
break;
}
assert r != null : "unexpected binary tree";
return extendWithNode(r);
}
@Override
public Node visitBlock(BlockTree tree, Void p) {
for (StatementTree n : tree.getStatements()) {
scan(n, null);
}
return null;
}
@Override
public Node visitBreak(BreakTree tree, Void p) {
Name label = tree.getLabel();
if (label == null) {
assert breakTargetL != null : "no target for break statement";
extendWithExtendedNode(new UnconditionalJump(breakTargetL));
} else {
assert breakLabels.containsKey(label);
extendWithExtendedNode(new UnconditionalJump(breakLabels.get(label)));
}
return null;
}
@Override
public Node visitSwitch(SwitchTree tree, Void p) {
SwitchBuilder builder = new SwitchBuilder(tree, p);
builder.build();
return null;
}
private class SwitchBuilder {
final private SwitchTree switchTree;
final private Label[] caseBodyLabels;
final private Void p;
private Node switchExpr;
private SwitchBuilder(SwitchTree tree, Void p) {
this.switchTree = tree;
this.caseBodyLabels = new Label[switchTree.getCases().size() + 1];
this.p = p;
}
public void build() {
Label oldBreakTargetL = breakTargetL;
breakTargetL = new Label();
int cases = caseBodyLabels.length - 1;
for (int i = 0; i < cases; ++i) {
caseBodyLabels[i] = new Label();
}
caseBodyLabels[cases] = breakTargetL;
switchExpr = unbox(scan(switchTree.getExpression(), p));
extendWithNode(
new MarkerNode(
switchTree, "start of switch statement", env.getTypeUtils()));
Integer defaultIndex = null;
for (int i = 0; i < cases; ++i) {
CaseTree caseTree = switchTree.getCases().get(i);
if (caseTree.getExpression() == null) {
defaultIndex = i;
} else {
buildCase(caseTree, i);
}
}
if (defaultIndex != null) {
// the checks of all cases must happen before the default case,
// therefore we build the default case last.
// fallthrough is still handled correctly with the caseBodyLabels.
buildCase(switchTree.getCases().get(defaultIndex), defaultIndex);
}
addLabelForNextNode(breakTargetL);
breakTargetL = oldBreakTargetL;
}
private void buildCase(CaseTree tree, int index) {
final Label thisBodyL = caseBodyLabels[index];
final Label nextBodyL = caseBodyLabels[index + 1];
final Label nextCaseL = new Label();
ExpressionTree exprTree = tree.getExpression();
if (exprTree != null) {
Node expr = scan(exprTree, p);
CaseNode test = new CaseNode(tree, switchExpr, expr, env.getTypeUtils());
extendWithNode(test);
extendWithExtendedNode(new ConditionalJump(thisBodyL, nextCaseL));
}
addLabelForNextNode(thisBodyL);
for (StatementTree stmt : tree.getStatements()) {
scan(stmt, p);
}
extendWithExtendedNode(new UnconditionalJump(nextBodyL));
addLabelForNextNode(nextCaseL);
}
}
@Override
public Node visitCase(CaseTree tree, Void p) {
throw new AssertionError("case visitor is implemented in SwitchBuilder");
}
@Override
public Node visitCatch(CatchTree tree, Void p) {
scan(tree.getParameter(), p);
scan(tree.getBlock(), p);
return null;
}
@Override
public Node visitClass(ClassTree tree, Void p) {
declaredClasses.add(tree);
return null;
}
@Override
public Node visitConditionalExpression(ConditionalExpressionTree tree, Void p) {
// see JLS 15.25
TypeMirror exprType = InternalUtils.typeOf(tree);
Label trueStart = new Label();
Label falseStart = new Label();
Label merge = new Label();
Node condition = unbox(scan(tree.getCondition(), p));
ConditionalJump cjump = new ConditionalJump(trueStart, falseStart);
extendWithExtendedNode(cjump);
addLabelForNextNode(trueStart);
Node trueExpr = scan(tree.getTrueExpression(), p);
trueExpr = conditionalExprPromotion(trueExpr, exprType);
extendWithExtendedNode(new UnconditionalJump(merge));
addLabelForNextNode(falseStart);
Node falseExpr = scan(tree.getFalseExpression(), p);
falseExpr = conditionalExprPromotion(falseExpr, exprType);
addLabelForNextNode(merge);
Node node = new TernaryExpressionNode(tree, condition, trueExpr, falseExpr);
extendWithNode(node);
return node;
}
@Override
public Node visitContinue(ContinueTree tree, Void p) {
Name label = tree.getLabel();
if (label == null) {
assert continueTargetL != null : "no target for continue statement";
extendWithExtendedNode(new UnconditionalJump(continueTargetL));
} else {
assert continueLabels.containsKey(label);
extendWithExtendedNode(new UnconditionalJump(continueLabels.get(label)));
}
return null;
}
@Override
public Node visitDoWhileLoop(DoWhileLoopTree tree, Void p) {
Name parentLabel = getLabel(getCurrentPath());
Label loopEntry = new Label();
Label loopExit = new Label();
// If the loop is a labeled statement, then its continue
// target is identical for continues with no label and
// continues with the loop's label.
Label conditionStart;
if (parentLabel != null) {
conditionStart = continueLabels.get(parentLabel);
} else {
conditionStart = new Label();
}
Label oldBreakTargetL = breakTargetL;
breakTargetL = loopExit;
Label oldContinueTargetL = continueTargetL;
continueTargetL = conditionStart;
// Loop body
addLabelForNextNode(loopEntry);
if (tree.getStatement() != null) {
scan(tree.getStatement(), p);
}
// Condition
addLabelForNextNode(conditionStart);
if (tree.getCondition() != null) {
unbox(scan(tree.getCondition(), p));
ConditionalJump cjump = new ConditionalJump(loopEntry, loopExit);
extendWithExtendedNode(cjump);
}
// Loop exit
addLabelForNextNode(loopExit);
breakTargetL = oldBreakTargetL;
continueTargetL = oldContinueTargetL;
return null;
}
@Override
public Node visitErroneous(ErroneousTree tree, Void p) {
assert false : "ErroneousTree is unexpected in AST to CFG translation";
return null;
}
@Override
public Node visitExpressionStatement(ExpressionStatementTree tree, Void p) {
return scan(tree.getExpression(), p);
}
@Override
public Node visitEnhancedForLoop(EnhancedForLoopTree tree, Void p) {
// see JLS 14.14.2
Name parentLabel = getLabel(getCurrentPath());
Label conditionStart = new Label();
Label loopEntry = new Label();
Label loopExit = new Label();
// If the loop is a labeled statement, then its continue
// target is identical for continues with no label and
// continues with the loop's label.
Label updateStart;
if (parentLabel != null) {
updateStart = continueLabels.get(parentLabel);
} else {
updateStart = new Label();
}
Label oldBreakTargetL = breakTargetL;
breakTargetL = loopExit;
Label oldContinueTargetL = continueTargetL;
continueTargetL = updateStart;
// Distinguish loops over Iterables from loops over arrays.
TypeElement iterableElement = elements.getTypeElement("java.lang.Iterable");
TypeMirror iterableType = types.erasure(iterableElement.asType());
VariableTree variable = tree.getVariable();
VariableElement variableElement = TreeUtils.elementFromDeclaration(variable);
ExpressionTree expression = tree.getExpression();
StatementTree statement = tree.getStatement();
TypeMirror exprType = InternalUtils.typeOf(expression);
if (types.isSubtype(exprType, iterableType)) {
// Take the upper bound of a type variable or wildcard
exprType = TypesUtils.upperBound(exprType);
assert (exprType instanceof DeclaredType) : "an Iterable must be a DeclaredType";
DeclaredType declaredExprType = (DeclaredType) exprType;
declaredExprType.getTypeArguments();
MemberSelectTree iteratorSelect = treeBuilder.buildIteratorMethodAccess(expression);
handleArtificialTree(iteratorSelect);
MethodInvocationTree iteratorCall =
treeBuilder.buildMethodInvocation(iteratorSelect);
handleArtificialTree(iteratorCall);
VariableTree iteratorVariable =
createEnhancedForLoopIteratorVariable(iteratorCall, variableElement);
handleArtificialTree(iteratorVariable);
VariableDeclarationNode iteratorVariableDecl =
new VariableDeclarationNode(iteratorVariable);
iteratorVariableDecl.setInSource(false);
extendWithNode(iteratorVariableDecl);
Node expressionNode = scan(expression, p);
MethodAccessNode iteratorAccessNode =
new MethodAccessNode(iteratorSelect, expressionNode);
iteratorAccessNode.setInSource(false);
extendWithNode(iteratorAccessNode);
MethodInvocationNode iteratorCallNode =
new MethodInvocationNode(
iteratorCall,
iteratorAccessNode,
Collections.emptyList(),
getCurrentPath());
iteratorCallNode.setInSource(false);
extendWithNode(iteratorCallNode);
translateAssignment(
iteratorVariable,
new LocalVariableNode(iteratorVariable),
iteratorCallNode);
// Test the loop ending condition
addLabelForNextNode(conditionStart);
IdentifierTree iteratorUse1 = treeBuilder.buildVariableUse(iteratorVariable);
handleArtificialTree(iteratorUse1);
LocalVariableNode iteratorReceiverNode = new LocalVariableNode(iteratorUse1);
iteratorReceiverNode.setInSource(false);
extendWithNode(iteratorReceiverNode);
MemberSelectTree hasNextSelect = treeBuilder.buildHasNextMethodAccess(iteratorUse1);
handleArtificialTree(hasNextSelect);
MethodAccessNode hasNextAccessNode =
new MethodAccessNode(hasNextSelect, iteratorReceiverNode);
hasNextAccessNode.setInSource(false);
extendWithNode(hasNextAccessNode);
MethodInvocationTree hasNextCall = treeBuilder.buildMethodInvocation(hasNextSelect);
handleArtificialTree(hasNextCall);
MethodInvocationNode hasNextCallNode =
new MethodInvocationNode(
hasNextCall,
hasNextAccessNode,
Collections.emptyList(),
getCurrentPath());
hasNextCallNode.setInSource(false);
extendWithNode(hasNextCallNode);
extendWithExtendedNode(new ConditionalJump(loopEntry, loopExit));
// Loop body, starting with declaration of the loop iteration variable
addLabelForNextNode(loopEntry);
extendWithNode(new VariableDeclarationNode(variable));
IdentifierTree iteratorUse2 = treeBuilder.buildVariableUse(iteratorVariable);
handleArtificialTree(iteratorUse2);
LocalVariableNode iteratorReceiverNode2 = new LocalVariableNode(iteratorUse2);
iteratorReceiverNode2.setInSource(false);
extendWithNode(iteratorReceiverNode2);
MemberSelectTree nextSelect = treeBuilder.buildNextMethodAccess(iteratorUse2);
handleArtificialTree(nextSelect);
MethodAccessNode nextAccessNode =
new MethodAccessNode(nextSelect, iteratorReceiverNode2);
nextAccessNode.setInSource(false);
extendWithNode(nextAccessNode);
MethodInvocationTree nextCall = treeBuilder.buildMethodInvocation(nextSelect);
handleArtificialTree(nextCall);
MethodInvocationNode nextCallNode =
new MethodInvocationNode(
nextCall,
nextAccessNode,
Collections.emptyList(),
getCurrentPath());
nextCallNode.setInSource(false);
extendWithNode(nextCallNode);
translateAssignment(variable, new LocalVariableNode(variable), nextCall);
if (statement != null) {
scan(statement, p);
}
// Loop back edge
addLabelForNextNode(updateStart);
extendWithExtendedNode(new UnconditionalJump(conditionStart));
} else {
// TODO: Shift any labels after the initialization of the
// temporary array variable.
VariableTree arrayVariable =
createEnhancedForLoopArrayVariable(expression, variableElement);
handleArtificialTree(arrayVariable);
VariableDeclarationNode arrayVariableNode =
new VariableDeclarationNode(arrayVariable);
arrayVariableNode.setInSource(false);
extendWithNode(arrayVariableNode);
Node expressionNode = scan(expression, p);
translateAssignment(
arrayVariable, new LocalVariableNode(arrayVariable), expressionNode);
// Declare and initialize the loop index variable
TypeMirror intType = types.getPrimitiveType(TypeKind.INT);
LiteralTree zero = treeBuilder.buildLiteral(new Integer(0));
handleArtificialTree(zero);
VariableTree indexVariable =
treeBuilder.buildVariableDecl(
intType,
uniqueName("index"),
variableElement.getEnclosingElement(),
zero);
handleArtificialTree(indexVariable);
VariableDeclarationNode indexVariableNode =
new VariableDeclarationNode(indexVariable);
indexVariableNode.setInSource(false);
extendWithNode(indexVariableNode);
IntegerLiteralNode zeroNode = extendWithNode(new IntegerLiteralNode(zero));
translateAssignment(indexVariable, new LocalVariableNode(indexVariable), zeroNode);
// Compare index to array length
addLabelForNextNode(conditionStart);
IdentifierTree indexUse1 = treeBuilder.buildVariableUse(indexVariable);
handleArtificialTree(indexUse1);
LocalVariableNode indexNode1 = new LocalVariableNode(indexUse1);
indexNode1.setInSource(false);
extendWithNode(indexNode1);
IdentifierTree arrayUse1 = treeBuilder.buildVariableUse(arrayVariable);
handleArtificialTree(arrayUse1);
LocalVariableNode arrayNode1 = extendWithNode(new LocalVariableNode(arrayUse1));
MemberSelectTree lengthSelect = treeBuilder.buildArrayLengthAccess(arrayUse1);
handleArtificialTree(lengthSelect);
FieldAccessNode lengthAccessNode = new FieldAccessNode(lengthSelect, arrayNode1);
lengthAccessNode.setInSource(false);
extendWithNode(lengthAccessNode);
BinaryTree lessThan = treeBuilder.buildLessThan(indexUse1, lengthSelect);
handleArtificialTree(lessThan);
LessThanNode lessThanNode =
new LessThanNode(lessThan, indexNode1, lengthAccessNode);
lessThanNode.setInSource(false);
extendWithNode(lessThanNode);
extendWithExtendedNode(new ConditionalJump(loopEntry, loopExit));
// Loop body, starting with declaration of the loop iteration variable
addLabelForNextNode(loopEntry);
extendWithNode(new VariableDeclarationNode(variable));
IdentifierTree arrayUse2 = treeBuilder.buildVariableUse(arrayVariable);
handleArtificialTree(arrayUse2);
LocalVariableNode arrayNode2 = new LocalVariableNode(arrayUse2);
arrayNode2.setInSource(false);
extendWithNode(arrayNode2);
IdentifierTree indexUse2 = treeBuilder.buildVariableUse(indexVariable);
handleArtificialTree(indexUse2);
LocalVariableNode indexNode2 = new LocalVariableNode(indexUse2);
indexNode2.setInSource(false);
extendWithNode(indexNode2);
ArrayAccessTree arrayAccess = treeBuilder.buildArrayAccess(arrayUse2, indexUse2);
handleArtificialTree(arrayAccess);
ArrayAccessNode arrayAccessNode =
new ArrayAccessNode(arrayAccess, arrayNode2, indexNode2);
arrayAccessNode.setInSource(false);
extendWithNode(arrayAccessNode);
translateAssignment(variable, new LocalVariableNode(variable), arrayAccessNode);
if (statement != null) {
scan(statement, p);
}
// Loop back edge
addLabelForNextNode(updateStart);
IdentifierTree indexUse3 = treeBuilder.buildVariableUse(indexVariable);
handleArtificialTree(indexUse3);
LocalVariableNode indexNode3 = new LocalVariableNode(indexUse3);
indexNode3.setInSource(false);
extendWithNode(indexNode3);
LiteralTree oneTree = treeBuilder.buildLiteral(Integer.valueOf(1));
handleArtificialTree(oneTree);
Node one = new IntegerLiteralNode(oneTree);
one.setInSource(false);
extendWithNode(one);
BinaryTree addOneTree =
treeBuilder.buildBinary(intType, Tree.Kind.PLUS, indexUse3, oneTree);
handleArtificialTree(addOneTree);
Node addOneNode = new NumericalAdditionNode(addOneTree, indexNode3, one);
addOneNode.setInSource(false);
extendWithNode(addOneNode);
AssignmentTree assignTree = treeBuilder.buildAssignment(indexUse3, addOneTree);
handleArtificialTree(assignTree);
Node assignNode = new AssignmentNode(assignTree, indexNode3, addOneNode);
assignNode.setInSource(false);
extendWithNode(assignNode);
extendWithExtendedNode(new UnconditionalJump(conditionStart));
}
// Loop exit
addLabelForNextNode(loopExit);
breakTargetL = oldBreakTargetL;
continueTargetL = oldContinueTargetL;
return null;
}
protected VariableTree createEnhancedForLoopIteratorVariable(
MethodInvocationTree iteratorCall, VariableElement variableElement) {
TypeMirror iteratorType = InternalUtils.typeOf(iteratorCall);
// Declare and initialize a new, unique iterator variable
VariableTree iteratorVariable =
treeBuilder.buildVariableDecl(
iteratorType, // annotatedIteratorTypeTree,
uniqueName("iter"),
variableElement.getEnclosingElement(),
iteratorCall);
return iteratorVariable;
}
protected VariableTree createEnhancedForLoopArrayVariable(
ExpressionTree expression, VariableElement variableElement) {
TypeMirror arrayType = InternalUtils.typeOf(expression);
// Declare and initialize a temporary array variable
VariableTree arrayVariable =
treeBuilder.buildVariableDecl(
arrayType,
uniqueName("array"),
variableElement.getEnclosingElement(),
expression);
return arrayVariable;
}
@Override
public Node visitForLoop(ForLoopTree tree, Void p) {
Name parentLabel = getLabel(getCurrentPath());
Label conditionStart = new Label();
Label loopEntry = new Label();
Label loopExit = new Label();
// If the loop is a labeled statement, then its continue
// target is identical for continues with no label and
// continues with the loop's label.
Label updateStart;
if (parentLabel != null) {
updateStart = continueLabels.get(parentLabel);
} else {
updateStart = new Label();
}
Label oldBreakTargetL = breakTargetL;
breakTargetL = loopExit;
Label oldContinueTargetL = continueTargetL;
continueTargetL = updateStart;
// Initializer
for (StatementTree init : tree.getInitializer()) {
scan(init, p);
}
// Condition
addLabelForNextNode(conditionStart);
if (tree.getCondition() != null) {
unbox(scan(tree.getCondition(), p));
ConditionalJump cjump = new ConditionalJump(loopEntry, loopExit);
extendWithExtendedNode(cjump);
}
// Loop body
addLabelForNextNode(loopEntry);
if (tree.getStatement() != null) {
scan(tree.getStatement(), p);
}
// Update
addLabelForNextNode(updateStart);
for (ExpressionStatementTree update : tree.getUpdate()) {
scan(update, p);
}
extendWithExtendedNode(new UnconditionalJump(conditionStart));
// Loop exit
addLabelForNextNode(loopExit);
breakTargetL = oldBreakTargetL;
continueTargetL = oldContinueTargetL;
return null;
}
@Override
public Node visitIdentifier(IdentifierTree tree, Void p) {
Node node;
if (TreeUtils.isFieldAccess(tree)) {
Node receiver = getReceiver(tree, TreeUtils.enclosingClass(getCurrentPath()));
node = new FieldAccessNode(tree, receiver);
} else {
Element element = TreeUtils.elementFromUse(tree);
switch (element.getKind()) {
case ANNOTATION_TYPE:
case CLASS:
case ENUM:
case INTERFACE:
case TYPE_PARAMETER:
node = new ClassNameNode(tree);
break;
case FIELD:
// Note that "this"/"super" is a field, but not a field access.
if (element.getSimpleName().contentEquals("this")) {
node = new ExplicitThisLiteralNode(tree);
} else {
node = new SuperNode(tree);
}
break;
case EXCEPTION_PARAMETER:
case LOCAL_VARIABLE:
case RESOURCE_VARIABLE:
case PARAMETER:
node = new LocalVariableNode(tree);
break;
case PACKAGE:
node = new PackageNameNode(tree);
break;
default:
throw new IllegalArgumentException(
"unexpected element kind : " + element.getKind());
}
}
extendWithNode(node);
return node;
}
@Override
public Node visitIf(IfTree tree, Void p) {
// all necessary labels
Label thenEntry = new Label();
Label elseEntry = new Label();
Label endIf = new Label();
// basic block for the condition
unbox(scan(tree.getCondition(), p));
ConditionalJump cjump = new ConditionalJump(thenEntry, elseEntry);
extendWithExtendedNode(cjump);
// then branch
addLabelForNextNode(thenEntry);
StatementTree thenStatement = tree.getThenStatement();
scan(thenStatement, p);
extendWithExtendedNode(new UnconditionalJump(endIf));
// else branch
addLabelForNextNode(elseEntry);
StatementTree elseStatement = tree.getElseStatement();
if (elseStatement != null) {
scan(elseStatement, p);
}
// label the end of the if statement
addLabelForNextNode(endIf);
return null;
}
@Override
public Node visitImport(ImportTree tree, Void p) {
assert false : "ImportTree is unexpected in AST to CFG translation";
return null;
}
@Override
public Node visitArrayAccess(ArrayAccessTree tree, Void p) {
Node array = scan(tree.getExpression(), p);
Node index = unaryNumericPromotion(scan(tree.getIndex(), p));
return extendWithNode(new ArrayAccessNode(tree, array, index));
}
@Override
public Node visitLabeledStatement(LabeledStatementTree tree, Void p) {
// This method can set the break target after generating all Nodes
// in the contained statement, but it can't set the continue target,
// which may be in the middle of a sequence of nodes. Labeled loops
// must look up and use the continue Labels.
Name labelName = tree.getLabel();
Label breakL = new Label(labelName + "_break");
Label continueL = new Label(labelName + "_continue");
breakLabels.put(labelName, breakL);
continueLabels.put(labelName, continueL);
scan(tree.getStatement(), p);
addLabelForNextNode(breakL);
breakLabels.remove(labelName);
continueLabels.remove(labelName);
return null;
}
@Override
public Node visitLiteral(LiteralTree tree, Void p) {
Node r = null;
switch (tree.getKind()) {
case BOOLEAN_LITERAL:
r = new BooleanLiteralNode(tree);
break;
case CHAR_LITERAL:
r = new CharacterLiteralNode(tree);
break;
case DOUBLE_LITERAL:
r = new DoubleLiteralNode(tree);
break;
case FLOAT_LITERAL:
r = new FloatLiteralNode(tree);
break;
case INT_LITERAL:
r = new IntegerLiteralNode(tree);
break;
case LONG_LITERAL:
r = new LongLiteralNode(tree);
break;
case NULL_LITERAL:
r = new NullLiteralNode(tree);
break;
case STRING_LITERAL:
r = new StringLiteralNode(tree);
break;
default:
assert false : "unexpected literal tree";
break;
}
assert r != null : "unexpected literal tree";
Node result = extendWithNode(r);
return result;
}
@Override
public Node visitMethod(MethodTree tree, Void p) {
assert false : "MethodTree is unexpected in AST to CFG translation";
return null;
}
@Override
public Node visitModifiers(ModifiersTree tree, Void p) {
assert false : "ModifiersTree is unexpected in AST to CFG translation";
return null;
}
@Override
public Node visitNewArray(NewArrayTree tree, Void p) {
// see JLS 15.10
ArrayType type = (ArrayType) InternalUtils.typeOf(tree);
TypeMirror elemType = type.getComponentType();
List dimensions = tree.getDimensions();
List initializers = tree.getInitializers();
List dimensionNodes = new ArrayList();
if (dimensions != null) {
for (ExpressionTree dim : dimensions) {
dimensionNodes.add(unaryNumericPromotion(scan(dim, p)));
}
}
List initializerNodes = new ArrayList();
if (initializers != null) {
for (ExpressionTree init : initializers) {
initializerNodes.add(assignConvert(scan(init, p), elemType));
}
}
Node node = new ArrayCreationNode(tree, type, dimensionNodes, initializerNodes);
return extendWithNode(node);
}
@Override
public Node visitNewClass(NewClassTree tree, Void p) {
// see JLS 15.9
Tree enclosingExpr = tree.getEnclosingExpression();
if (enclosingExpr != null) {
scan(enclosingExpr, p);
}
// We ignore any class body because its methods should
// be visited separately.
// Convert constructor arguments
ExecutableElement constructor = TreeUtils.elementFromUse(tree);
List actualExprs = tree.getArguments();
List arguments = convertCallArguments(constructor, actualExprs);
Node constructorNode = scan(tree.getIdentifier(), p);
Node node = new ObjectCreationNode(tree, constructorNode, arguments);
Set thrownSet = new HashSet<>();
// Add exceptions explicitly mentioned in the throws clause.
List thrownTypes = constructor.getThrownTypes();
thrownSet.addAll(thrownTypes);
// Add Throwable to account for unchecked exceptions
TypeElement throwableElement = elements.getTypeElement("java.lang.Throwable");
thrownSet.add(throwableElement.asType());
extendWithNodeWithExceptions(node, thrownSet);
return node;
}
/**
* Maps a {@code Tree} its directly enclosing {@code ParenthesizedTree} if one exists.
*
* This map is used by {@link CFGTranslationPhaseOne#addToLookupMap(Node)} to
* associate a {@code ParenthesizedTree} with the dataflow {@code Node} that was used
* during inference. This map is necessary because dataflow does
* not create a {@code Node} for a {@code ParenthesizedTree.}
*/
private final Map parenMapping = new HashMap<>();
@Override
public Node visitParenthesized(ParenthesizedTree tree, Void p) {
parenMapping.put(tree.getExpression(), tree);
return scan(tree.getExpression(), p);
}
@Override
public Node visitReturn(ReturnTree tree, Void p) {
ExpressionTree ret = tree.getExpression();
// TODO: also have a return-node if nothing is returned
ReturnNode result = null;
if (ret != null) {
Node node = scan(ret, p);
Tree enclosing =
TreeUtils.enclosingOfKind(
getCurrentPath(),
new HashSet(
Arrays.asList(Kind.METHOD, Kind.LAMBDA_EXPRESSION)));
if (enclosing.getKind() == Kind.LAMBDA_EXPRESSION) {
LambdaExpressionTree lambdaTree = (LambdaExpressionTree) enclosing;
TreePath lambdaTreePath =
TreePath.getPath(getCurrentPath().getCompilationUnit(), lambdaTree);
Context ctx = ((JavacProcessingEnvironment) env).getContext();
Element overriddenElement =
com.sun.tools.javac.code.Types.instance(ctx)
.findDescriptorSymbol(
((Type) trees.getTypeMirror(lambdaTreePath)).tsym);
result =
new ReturnNode(
tree,
node,
env.getTypeUtils(),
lambdaTree,
(MethodSymbol) overriddenElement);
} else {
result = new ReturnNode(tree, node, env.getTypeUtils(), (MethodTree) enclosing);
}
returnNodes.add(result);
extendWithNode(result);
}
extendWithExtendedNode(new UnconditionalJump(regularExitLabel));
// TODO: return statements should also flow to an enclosing finally block
return result;
}
@Override
public Node visitMemberSelect(MemberSelectTree tree, Void p) {
Node expr = scan(tree.getExpression(), p);
if (!TreeUtils.isFieldAccess(tree)) {
// Could be a selector of a class or package
Node result = null;
Element element = TreeUtils.elementFromUse(tree);
switch (element.getKind()) {
case ANNOTATION_TYPE:
case CLASS:
case ENUM:
case INTERFACE:
result = extendWithNode(new ClassNameNode(tree, expr));
break;
case PACKAGE:
result = extendWithNode(new PackageNameNode(tree, (PackageNameNode) expr));
break;
default:
assert false : "Unexpected element kind: " + element.getKind();
return null;
}
return result;
}
Node node = new FieldAccessNode(tree, expr);
Element element = TreeUtils.elementFromUse(tree);
if (ElementUtils.isStatic(element)
|| expr instanceof ImplicitThisLiteralNode
|| expr instanceof ExplicitThisLiteralNode) {
// No NullPointerException can be thrown, use normal node
extendWithNode(node);
} else {
TypeElement npeElement = elements.getTypeElement("java.lang.NullPointerException");
extendWithNodeWithException(node, npeElement.asType());
}
return node;
}
@Override
public Node visitEmptyStatement(EmptyStatementTree tree, Void p) {
return null;
}
@Override
public Node visitSynchronized(SynchronizedTree tree, Void p) {
// see JLS 14.19
Node synchronizedExpr = scan(tree.getExpression(), p);
SynchronizedNode synchronizedStartNode =
new SynchronizedNode(tree, synchronizedExpr, true, env.getTypeUtils());
extendWithNode(synchronizedStartNode);
scan(tree.getBlock(), p);
SynchronizedNode synchronizedEndNode =
new SynchronizedNode(tree, synchronizedExpr, false, env.getTypeUtils());
extendWithNode(synchronizedEndNode);
return null;
}
@Override
public Node visitThrow(ThrowTree tree, Void p) {
Node expression = scan(tree.getExpression(), p);
TypeMirror exception = expression.getType();
ThrowNode throwsNode = new ThrowNode(tree, expression, env.getTypeUtils());
NodeWithExceptionsHolder exNode = extendWithNodeWithException(throwsNode, exception);
exNode.setTerminatesExecution(true);
return throwsNode;
}
@Override
public Node visitCompilationUnit(CompilationUnitTree tree, Void p) {
assert false : "CompilationUnitTree is unexpected in AST to CFG translation";
return null;
}
@Override
public Node visitTry(TryTree tree, Void p) {
List catches = tree.getCatches();
BlockTree finallyBlock = tree.getFinallyBlock();
extendWithNode(new MarkerNode(tree, "start of try statement", env.getTypeUtils()));
// TODO: Should we handle try-with-resources blocks by also generating code
// for automatically closing the resources?
List resources = tree.getResources();
for (Tree resource : resources) {
scan(resource, p);
}
List> catchLabels = new ArrayList<>();
for (CatchTree c : catches) {
TypeMirror type = InternalUtils.typeOf(c.getParameter().getType());
assert type != null : "exception parameters must have a type";
catchLabels.add(Pair.of(type, new Label()));
}
Label finallyLabel = null;
if (finallyBlock != null) {
finallyLabel = new Label();
tryStack.pushFrame(new TryFinallyFrame(finallyLabel));
}
Label doneLabel = new Label();
tryStack.pushFrame(new TryCatchFrame(types, catchLabels));
scan(tree.getBlock(), p);
extendWithExtendedNode(new UnconditionalJump(firstNonNull(finallyLabel, doneLabel)));
tryStack.popFrame();
int catchIndex = 0;
for (CatchTree c : catches) {
addLabelForNextNode(catchLabels.get(catchIndex).second);
scan(c, p);
catchIndex++;
extendWithExtendedNode(
new UnconditionalJump(firstNonNull(finallyLabel, doneLabel)));
}
if (finallyLabel != null) {
tryStack.popFrame();
addLabelForNextNode(finallyLabel);
scan(finallyBlock, p);
TypeMirror throwableType = elements.getTypeElement("java.lang.Throwable").asType();
extendWithNodeWithException(
new MarkerNode(tree, "end of finally block", env.getTypeUtils()),
throwableType);
}
addLabelForNextNode(doneLabel);
return null;
}
@Override
public Node visitParameterizedType(ParameterizedTypeTree tree, Void p) {
return extendWithNode(new ParameterizedTypeNode(tree));
}
@Override
public Node visitUnionType(UnionTypeTree tree, Void p) {
assert false : "UnionTypeTree is unexpected in AST to CFG translation";
return null;
}
@Override
public Node visitArrayType(ArrayTypeTree tree, Void p) {
return extendWithNode(new ArrayTypeNode(tree));
}
@Override
public Node visitTypeCast(TypeCastTree tree, Void p) {
final Node operand = scan(tree.getExpression(), p);
final TypeMirror type = InternalUtils.typeOf(tree.getType());
final Node node = new TypeCastNode(tree, operand, type);
final TypeElement cceElement = elements.getTypeElement("java.lang.ClassCastException");
extendWithNodeWithException(node, cceElement.asType());
return node;
}
@Override
public Node visitPrimitiveType(PrimitiveTypeTree tree, Void p) {
return extendWithNode(new PrimitiveTypeNode(tree));
}
@Override
public Node visitTypeParameter(TypeParameterTree tree, Void p) {
assert false : "TypeParameterTree is unexpected in AST to CFG translation";
return null;
}
@Override
public Node visitInstanceOf(InstanceOfTree tree, Void p) {
Node operand = scan(tree.getExpression(), p);
TypeMirror refType = InternalUtils.typeOf(tree.getType());
InstanceOfNode node = new InstanceOfNode(tree, operand, refType, types);
extendWithNode(node);
return node;
}
@Override
public Node visitUnary(UnaryTree tree, Void p) {
Node result = null;
Tree.Kind kind = tree.getKind();
switch (kind) {
case BITWISE_COMPLEMENT:
case UNARY_MINUS:
case UNARY_PLUS:
{
// see JLS 15.14 and 15.15
Node expr = scan(tree.getExpression(), p);
expr = unaryNumericPromotion(expr);
// TypeMirror exprType = InternalUtils.typeOf(tree);
switch (kind) {
case BITWISE_COMPLEMENT:
result = extendWithNode(new BitwiseComplementNode(tree, expr));
break;
case UNARY_MINUS:
result = extendWithNode(new NumericalMinusNode(tree, expr));
break;
case UNARY_PLUS:
result = extendWithNode(new NumericalPlusNode(tree, expr));
break;
default:
assert false;
break;
}
break;
}
case LOGICAL_COMPLEMENT:
{
// see JLS 15.15.6
Node expr = scan(tree.getExpression(), p);
result = extendWithNode(new ConditionalNotNode(tree, unbox(expr)));
break;
}
case POSTFIX_DECREMENT:
case POSTFIX_INCREMENT:
{
ExpressionTree exprTree = tree.getExpression();
TypeMirror exprType = InternalUtils.typeOf(exprTree);
TypeMirror oneType = types.getPrimitiveType(TypeKind.INT);
Node expr = scan(exprTree, p);
TypeMirror promotedType = binaryPromotedType(exprType, oneType);
LiteralTree oneTree = treeBuilder.buildLiteral(Integer.valueOf(1));
handleArtificialTree(oneTree);
Node exprRHS = binaryNumericPromotion(expr, promotedType);
Node one = new IntegerLiteralNode(oneTree);
one.setInSource(false);
extendWithNode(one);
one = binaryNumericPromotion(one, promotedType);
BinaryTree operTree =
treeBuilder.buildBinary(
promotedType,
(kind == Tree.Kind.POSTFIX_INCREMENT
? Tree.Kind.PLUS
: Tree.Kind.MINUS),
exprTree,
oneTree);
handleArtificialTree(operTree);
Node operNode;
if (kind == Tree.Kind.POSTFIX_INCREMENT) {
operNode = new NumericalAdditionNode(operTree, exprRHS, one);
} else {
assert kind == Tree.Kind.POSTFIX_DECREMENT;
operNode = new NumericalSubtractionNode(operTree, exprRHS, one);
}
extendWithNode(operNode);
Node narrowed = narrowAndBox(operNode, exprType);
// TODO: By using the assignment as the result of the expression, we
// act like a pre-increment/decrement. Fix this by saving the initial
// value of the expression in a temporary.
AssignmentNode assignNode = new AssignmentNode(tree, expr, narrowed);
extendWithNode(assignNode);
result = assignNode;
break;
}
case PREFIX_DECREMENT:
case PREFIX_INCREMENT:
{
ExpressionTree exprTree = tree.getExpression();
TypeMirror exprType = InternalUtils.typeOf(exprTree);
TypeMirror oneType = types.getPrimitiveType(TypeKind.INT);
Node expr = scan(exprTree, p);
TypeMirror promotedType = binaryPromotedType(exprType, oneType);
LiteralTree oneTree = treeBuilder.buildLiteral(Integer.valueOf(1));
handleArtificialTree(oneTree);
Node exprRHS = binaryNumericPromotion(expr, promotedType);
Node one = new IntegerLiteralNode(oneTree);
one.setInSource(false);
extendWithNode(one);
one = binaryNumericPromotion(one, promotedType);
BinaryTree operTree =
treeBuilder.buildBinary(
promotedType,
(kind == Tree.Kind.PREFIX_INCREMENT
? Tree.Kind.PLUS
: Tree.Kind.MINUS),
exprTree,
oneTree);
handleArtificialTree(operTree);
Node operNode;
if (kind == Tree.Kind.PREFIX_INCREMENT) {
operNode = new NumericalAdditionNode(operTree, exprRHS, one);
} else {
assert kind == Tree.Kind.PREFIX_DECREMENT;
operNode = new NumericalSubtractionNode(operTree, exprRHS, one);
}
extendWithNode(operNode);
Node narrowed = narrowAndBox(operNode, exprType);
AssignmentNode assignNode = new AssignmentNode(tree, expr, narrowed);
extendWithNode(assignNode);
result = assignNode;
break;
}
case OTHER:
default:
// special node NLLCHK
if (tree.toString().startsWith("<*nullchk*>")) {
Node expr = scan(tree.getExpression(), p);
result = extendWithNode(new NullChkNode(tree, expr));
break;
}
assert false : "Unknown kind (" + kind + ") of unary expression: " + tree;
}
return result;
}
@Override
public Node visitVariable(VariableTree tree, Void p) {
// see JLS 14.4
boolean isField =
getCurrentPath().getParentPath() != null
&& getCurrentPath().getParentPath().getLeaf().getKind() == Kind.CLASS;
Node node = null;
ClassTree enclosingClass = TreeUtils.enclosingClass(getCurrentPath());
TypeElement classElem = TreeUtils.elementFromDeclaration(enclosingClass);
Node receiver = new ImplicitThisLiteralNode(classElem.asType());
if (isField) {
ExpressionTree initializer = tree.getInitializer();
assert initializer != null;
node =
translateAssignment(
tree,
new FieldAccessNode(
tree, TreeUtils.elementFromDeclaration(tree), receiver),
initializer);
} else {
// local variable definition
VariableDeclarationNode decl = new VariableDeclarationNode(tree);
extendWithNode(decl);
// initializer
ExpressionTree initializer = tree.getInitializer();
if (initializer != null) {
node =
translateAssignment(
tree, new LocalVariableNode(tree, receiver), initializer);
}
}
return node;
}
@Override
public Node visitWhileLoop(WhileLoopTree tree, Void p) {
Name parentLabel = getLabel(getCurrentPath());
Label loopEntry = new Label();
Label loopExit = new Label();
// If the loop is a labeled statement, then its continue
// target is identical for continues with no label and
// continues with the loop's label.
Label conditionStart;
if (parentLabel != null) {
conditionStart = continueLabels.get(parentLabel);
} else {
conditionStart = new Label();
}
Label oldBreakTargetL = breakTargetL;
breakTargetL = loopExit;
Label oldContinueTargetL = continueTargetL;
continueTargetL = conditionStart;
// Condition
addLabelForNextNode(conditionStart);
if (tree.getCondition() != null) {
unbox(scan(tree.getCondition(), p));
ConditionalJump cjump = new ConditionalJump(loopEntry, loopExit);
extendWithExtendedNode(cjump);
}
// Loop body
addLabelForNextNode(loopEntry);
if (tree.getStatement() != null) {
scan(tree.getStatement(), p);
}
extendWithExtendedNode(new UnconditionalJump(conditionStart));
// Loop exit
addLabelForNextNode(loopExit);
breakTargetL = oldBreakTargetL;
continueTargetL = oldContinueTargetL;
return null;
}
@Override
public Node visitLambdaExpression(LambdaExpressionTree tree, Void p) {
declaredLambdas.add(tree);
Node node = new FunctionalInterfaceNode(tree);
extendWithNode(node);
return node;
}
@Override
public Node visitMemberReference(MemberReferenceTree tree, Void p) {
Tree enclosingExpr = tree.getQualifierExpression();
if (enclosingExpr != null) {
scan(enclosingExpr, p);
}
Node node = new FunctionalInterfaceNode(tree);
extendWithNode(node);
return node;
}
@Override
public Node visitWildcard(WildcardTree tree, Void p) {
assert false : "WildcardTree is unexpected in AST to CFG translation";
return null;
}
@Override
public Node visitOther(Tree tree, Void p) {
assert false : "Unknown AST element encountered in AST to CFG translation.";
return null;
}
}
/**
* A tuple with 4 named elements.
*/
private interface TreeInfo {
boolean isBoxed();
boolean isNumeric();
boolean isBoolean();
TypeMirror unboxedType();
}
private static A firstNonNull(A first, A second) {
if (first != null) {
return first;
} else if (second != null) {
return second;
} else {
throw new NullPointerException();
}
}
/* --------------------------------------------------------- */
/* Utility routines for debugging CFG building */
/* --------------------------------------------------------- */
/**
* Print a set of {@link Block}s and the edges between them. This is useful
* for examining the results of phase two.
*/
protected static void printBlocks(Set blocks) {
for (Block b : blocks) {
System.out.print(b.hashCode() + ": " + b);
switch (b.getType()) {
case REGULAR_BLOCK:
case SPECIAL_BLOCK:
{
Block succ = ((SingleSuccessorBlockImpl) b).getSuccessor();
System.out.println(" -> " + (succ != null ? succ.hashCode() : "||"));
break;
}
case EXCEPTION_BLOCK:
{
Block succ = ((SingleSuccessorBlockImpl) b).getSuccessor();
System.out.print(" -> " + (succ != null ? succ.hashCode() : "||") + " {");
for (Map.Entry> entry :
((ExceptionBlockImpl) b).getExceptionalSuccessors().entrySet()) {
System.out.print(entry.getKey() + " : " + entry.getValue() + ", ");
}
System.out.println("}");
break;
}
case CONDITIONAL_BLOCK:
{
Block tSucc = ((ConditionalBlockImpl) b).getThenSuccessor();
Block eSucc = ((ConditionalBlockImpl) b).getElseSuccessor();
System.out.println(
" -> T "
+ (tSucc != null ? tSucc.hashCode() : "||")
+ " F "
+ (eSucc != null ? eSucc.hashCode() : "||"));
break;
}
}
}
}
}