aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java')
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java4448
1 files changed, 4448 insertions, 0 deletions
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java
new file mode 100644
index 0000000000..2cff0f2802
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java
@@ -0,0 +1,4448 @@
+package org.checkerframework.dataflow.cfg;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.analysis.Store;
+import org.checkerframework.dataflow.cfg.CFGBuilder.ExtendedNode.ExtendedNodeType;
+import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGMethod;
+import org.checkerframework.dataflow.cfg.block.Block;
+import org.checkerframework.dataflow.cfg.block.Block.BlockType;
+import org.checkerframework.dataflow.cfg.block.BlockImpl;
+import org.checkerframework.dataflow.cfg.block.ConditionalBlockImpl;
+import org.checkerframework.dataflow.cfg.block.ExceptionBlockImpl;
+import org.checkerframework.dataflow.cfg.block.RegularBlockImpl;
+import org.checkerframework.dataflow.cfg.block.SingleSuccessorBlockImpl;
+import org.checkerframework.dataflow.cfg.block.SpecialBlock.SpecialBlockType;
+import org.checkerframework.dataflow.cfg.block.SpecialBlockImpl;
+import org.checkerframework.dataflow.cfg.node.*;
+import org.checkerframework.dataflow.qual.TerminatesExecution;
+import org.checkerframework.dataflow.util.MostlySingleton;
+import org.checkerframework.javacutil.AnnotationProvider;
+import org.checkerframework.javacutil.BasicAnnotationProvider;
+import org.checkerframework.javacutil.ElementUtils;
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.Pair;
+import org.checkerframework.javacutil.TreeUtils;
+import org.checkerframework.javacutil.TypesUtils;
+import org.checkerframework.javacutil.trees.TreeBuilder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.ReferenceType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.UnionType;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import com.sun.source.tree.*;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Context;
+
+/**
+ * Builds the control flow graph of some Java code (either a method, or an
+ * arbitrary statement).
+ *
+ * <p>
+ *
+ * The translation of the AST to the CFG is split into three phases:
+ * <ol>
+ * <li><em>Phase one.</em> In the first phase, the AST is translated into a
+ * sequence of {@link org.checkerframework.dataflow.cfg.CFGBuilder.ExtendedNode}s. An extended node can either be a
+ * {@link Node}, or one of several meta elements such as a conditional or
+ * unconditional jump or a node with additional information about exceptions.
+ * Some of the extended nodes contain labels (e.g., for the jump target), and
+ * phase one additionally creates a mapping from labels to extended nodes.
+ * Finally, the list of leaders is computed: A leader is an extended node which
+ * will give rise to a basic block in phase two.</li>
+ * <li><em>Phase two.</em> In this phase, the sequence of extended nodes is
+ * translated to a graph of control flow blocks that contain nodes. The meta
+ * elements from phase one are translated into the correct edges.</li>
+ * <li><em>Phase three.</em> The control flow graph generated in phase two can
+ * contain degenerate basic blocks such as empty regular basic blocks or
+ * conditional basic blocks that have the same block as both 'then' and 'else'
+ * successor. This phase removes these cases while preserving the control flow
+ * structure.</li>
+ * </ol>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class CFGBuilder {
+
+ /** Can assertions be assumed to be disabled? */
+ protected final boolean assumeAssertionsDisabled;
+
+ /** Can assertions be assumed to be enabled? */
+ protected final boolean assumeAssertionsEnabled;
+
+ public CFGBuilder(boolean assumeAssertionsEnabled, boolean assumeAssertionsDisabled) {
+ assert !(assumeAssertionsDisabled && assumeAssertionsEnabled);
+ this.assumeAssertionsEnabled = assumeAssertionsEnabled;
+ this.assumeAssertionsDisabled = assumeAssertionsDisabled;
+ }
+
+ /**
+ * Class declarations that have been encountered when building the
+ * control-flow graph for a method.
+ */
+ protected final List<ClassTree> declaredClasses = new LinkedList<>();
+
+ public List<ClassTree> getDeclaredClasses() {
+ return declaredClasses;
+ }
+
+ /**
+ * Lambdas encountered when building the control-flow graph for
+ * a method, variable initializer, or initializer.
+ */
+ protected final List<LambdaExpressionTree> declaredLambdas = new LinkedList<>();
+
+ public List<LambdaExpressionTree> getDeclaredLambdas() {
+ return declaredLambdas;
+ }
+
+ /**
+ * Build the control flow graph of some code.
+ */
+ public static ControlFlowGraph build(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST, boolean assumeAssertionsEnabled, boolean assumeAssertionsDisabled) {
+ return new CFGBuilder(assumeAssertionsEnabled, assumeAssertionsDisabled).run(root, env, underlyingAST);
+ }
+
+ /**
+ * Build the control flow graph of some code (method, initializer block, ...).
+ * bodyPath is the TreePath to the body of that code.
+ */
+ public static ControlFlowGraph build(
+ TreePath bodyPath, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST, boolean assumeAssertionsEnabled, boolean assumeAssertionsDisabled) {
+ return new CFGBuilder(assumeAssertionsEnabled, assumeAssertionsDisabled).run(bodyPath, env, underlyingAST);
+ }
+
+ /**
+ * Build the control flow graph of a method.
+ */
+ public static ControlFlowGraph build(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ MethodTree tree, ClassTree classTree, boolean assumeAssertionsEnabled, boolean assumeAssertionsDisabled) {
+ return new CFGBuilder(assumeAssertionsEnabled, assumeAssertionsDisabled).run(root, env, tree, classTree);
+ }
+
+ /**
+ * Build the control flow graph of some code.
+ */
+ public static ControlFlowGraph build(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST) {
+ return new CFGBuilder(false, false).run(root, env, underlyingAST);
+ }
+
+ /**
+ * Build the control flow graph of a method.
+ */
+ public static ControlFlowGraph build(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ MethodTree tree, ClassTree classTree) {
+ return new CFGBuilder(false, false).run(root, env, tree, classTree);
+ }
+
+ /**
+ * Build the control flow graph of some code.
+ */
+ public ControlFlowGraph run(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST) {
+ declaredClasses.clear();
+ declaredLambdas.clear();
+
+ TreeBuilder builder = new TreeBuilder(env);
+ AnnotationProvider annotationProvider = new BasicAnnotationProvider();
+ PhaseOneResult phase1result = new CFGTranslationPhaseOne().process(
+ root, env, underlyingAST, exceptionalExitLabel, builder, annotationProvider);
+ ControlFlowGraph phase2result = new CFGTranslationPhaseTwo()
+ .process(phase1result);
+ ControlFlowGraph phase3result = CFGTranslationPhaseThree
+ .process(phase2result);
+ return phase3result;
+ }
+
+ /**
+ * Build the control flow graph of some code (method, initializer block, ...).
+ * bodyPath is the TreePath to the body of that code.
+ */
+ public ControlFlowGraph run(
+ TreePath bodyPath, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST) {
+ declaredClasses.clear();
+ TreeBuilder builder = new TreeBuilder(env);
+ AnnotationProvider annotationProvider = new BasicAnnotationProvider();
+ PhaseOneResult phase1result = new CFGTranslationPhaseOne().process(
+ bodyPath, env, underlyingAST, exceptionalExitLabel, builder, annotationProvider);
+ ControlFlowGraph phase2result = new CFGTranslationPhaseTwo()
+ .process(phase1result);
+ ControlFlowGraph phase3result = CFGTranslationPhaseThree
+ .process(phase2result);
+ return phase3result;
+ }
+
+ /**
+ * Build the control flow graph of a method.
+ */
+ public ControlFlowGraph run(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ MethodTree tree, ClassTree classTree) {
+ UnderlyingAST underlyingAST = new CFGMethod(tree, classTree);
+ return run(root, env, underlyingAST);
+ }
+
+ /* --------------------------------------------------------- */
+ /* Extended Node Types and Labels */
+ /* --------------------------------------------------------- */
+
+ /** Special label to identify the exceptional exit. */
+ protected final Label exceptionalExitLabel = new Label();
+
+ /** Special label to identify the regular exit. */
+ protected final Label regularExitLabel = new Label();
+
+ /**
+ * An extended node can be one of several things (depending on its
+ * {@code type}):
+ * <ul>
+ * <li><em>NODE</em>. An extended node of this type is just a wrapper for a
+ * {@link Node} (that cannot throw exceptions).</li>
+ * <li><em>EXCEPTION_NODE</em>. A wrapper for a {@link Node} which can throw
+ * exceptions. It contains a label for every possible exception type the
+ * node might throw.</li>
+ * <li><em>UNCONDITIONAL_JUMP</em>. An unconditional jump to a label.</li>
+ * <li><em>TWO_TARGET_CONDITIONAL_JUMP</em>. A conditional jump with two
+ * targets for both the 'then' and 'else' branch.</li>
+ * </ul>
+ */
+ protected static abstract class ExtendedNode {
+
+ /**
+ * The basic block this extended node belongs to (as determined in phase
+ * two).
+ */
+ protected BlockImpl block;
+
+ /** Type of this node. */
+ protected ExtendedNodeType type;
+
+ /** Does this node terminate the execution? (e.g., "System.exit()") */
+ protected boolean terminatesExecution = false;
+
+ public ExtendedNode(ExtendedNodeType type) {
+ this.type = type;
+ }
+
+ /** Extended node types (description see above). */
+ public enum ExtendedNodeType {
+ NODE, EXCEPTION_NODE, UNCONDITIONAL_JUMP, CONDITIONAL_JUMP
+ }
+
+ public ExtendedNodeType getType() {
+ return type;
+ }
+
+ public boolean getTerminatesExecution() {
+ return terminatesExecution;
+ }
+
+ public void setTerminatesExecution(boolean terminatesExecution) {
+ this.terminatesExecution = terminatesExecution;
+ }
+
+ /**
+ * @return the node contained in this extended node (only applicable if
+ * the type is {@code NODE} or {@code EXCEPTION_NODE}).
+ */
+ public Node getNode() {
+ assert false;
+ return null;
+ }
+
+ /**
+ * @return the label associated with this extended node (only applicable
+ * if type is {@link ExtendedNodeType#CONDITIONAL_JUMP} or
+ * {@link ExtendedNodeType#UNCONDITIONAL_JUMP}).
+ */
+ public Label getLabel() {
+ assert false;
+ return null;
+ }
+
+ public BlockImpl getBlock() {
+ return block;
+ }
+
+ public void setBlock(BlockImpl b) {
+ this.block = b;
+ }
+
+ @Override
+ public String toString() {
+ return "ExtendedNode(" + type + ")";
+ }
+ }
+
+ /**
+ * An extended node of type {@code NODE}.
+ */
+ protected static class NodeHolder extends ExtendedNode {
+
+ protected Node node;
+
+ public NodeHolder(Node node) {
+ super(ExtendedNodeType.NODE);
+ this.node = node;
+ }
+
+ @Override
+ public Node getNode() {
+ return node;
+ }
+
+ @Override
+ public String toString() {
+ return "NodeHolder(" + node + ")";
+ }
+
+ }
+
+ /**
+ * An extended node of type {@code EXCEPTION_NODE}.
+ */
+ protected static class NodeWithExceptionsHolder extends ExtendedNode {
+
+ protected Node node;
+ /**
+ * Map from exception type to labels of successors that may
+ * be reached as a result of that exception.
+ */
+ protected Map<TypeMirror, Set<Label>> exceptions;
+
+ public NodeWithExceptionsHolder(Node node,
+ Map<TypeMirror, Set<Label>> exceptions) {
+ super(ExtendedNodeType.EXCEPTION_NODE);
+ this.node = node;
+ this.exceptions = exceptions;
+ }
+
+ public Map<TypeMirror, Set<Label>> getExceptions() {
+ return exceptions;
+ }
+
+ @Override
+ public Node getNode() {
+ return node;
+ }
+
+ @Override
+ public String toString() {
+ return "NodeWithExceptionsHolder(" + node + ")";
+ }
+
+ }
+
+ /**
+ * An extended node of type {@link ExtendedNodeType#CONDITIONAL_JUMP}.
+ *
+ * <p>
+ *
+ * <em>Important:</em> In the list of extended nodes, there should not be
+ * any labels that point to a conditional jump. Furthermore, the node
+ * directly ahead of any conditional jump has to be a
+ * {@link NodeWithExceptionsHolder} or {@link NodeHolder}, and the node held
+ * by that extended node is required to be of boolean type.
+ */
+ protected static class ConditionalJump extends ExtendedNode {
+
+ protected Label trueSucc;
+ protected Label falseSucc;
+
+ protected Store.FlowRule trueFlowRule;
+ protected Store.FlowRule falseFlowRule;
+
+ public ConditionalJump(Label trueSucc, Label falseSucc) {
+ super(ExtendedNodeType.CONDITIONAL_JUMP);
+ this.trueSucc = trueSucc;
+ this.falseSucc = falseSucc;
+ }
+
+ public Label getThenLabel() {
+ return trueSucc;
+ }
+
+ public Label getElseLabel() {
+ return falseSucc;
+ }
+
+ public Store.FlowRule getTrueFlowRule() {
+ return trueFlowRule;
+ }
+
+ public Store.FlowRule getFalseFlowRule() {
+ return falseFlowRule;
+ }
+
+ public void setTrueFlowRule(Store.FlowRule rule) {
+ trueFlowRule = rule;
+ }
+
+ public void setFalseFlowRule(Store.FlowRule rule) {
+ falseFlowRule = rule;
+ }
+
+ @Override
+ public String toString() {
+ return "TwoTargetConditionalJump(" + getThenLabel() + ","
+ + getElseLabel() + ")";
+ }
+ }
+
+ /**
+ * An extended node of type {@link ExtendedNodeType#UNCONDITIONAL_JUMP}.
+ */
+ protected static class UnconditionalJump extends ExtendedNode {
+
+ protected Label jumpTarget;
+
+ public UnconditionalJump(Label jumpTarget) {
+ super(ExtendedNodeType.UNCONDITIONAL_JUMP);
+ this.jumpTarget = jumpTarget;
+ }
+
+ @Override
+ public Label getLabel() {
+ return jumpTarget;
+ }
+
+ @Override
+ public String toString() {
+ return "JumpMarker(" + getLabel() + ")";
+ }
+ }
+
+ /**
+ * A label is used to refer to other extended nodes using a mapping from
+ * labels to extended nodes. Labels get their names either from labeled
+ * statements in the source code or from internally generated unique names.
+ */
+ protected static class Label {
+ private static int uid = 0;
+
+ protected String name;
+
+ public Label(String name) {
+ this.name = name;
+ }
+
+ public Label() {
+ this.name = uniqueName();
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * Return a new unique label name that cannot be confused with a Java
+ * source code label.
+ *
+ * @return a new unique label name
+ */
+ private static String uniqueName() {
+ return "%L" + uid++;
+ }
+ }
+
+ /**
+ * A TryFrame takes a thrown exception type and maps it to a set
+ * of possible control-flow successors.
+ */
+ protected static interface TryFrame {
+ /**
+ * Given a type of thrown exception, add the set of possible control
+ * flow successor {@link Label}s to the argument set. Return true
+ * if the exception is known to be caught by one of those labels and
+ * false if it may propagate still further.
+ */
+ public boolean possibleLabels(TypeMirror thrown, Set<Label> labels);
+ }
+
+ /**
+ * A TryCatchFrame contains an ordered list of catch labels that apply
+ * to exceptions with specific types.
+ */
+ protected static class TryCatchFrame implements TryFrame {
+ protected Types types;
+
+ /** An ordered list of pairs because catch blocks are ordered. */
+ protected List<Pair<TypeMirror, Label>> catchLabels;
+
+ public TryCatchFrame(Types types, List<Pair<TypeMirror, Label>> catchLabels) {
+ this.types = types;
+ this.catchLabels = catchLabels;
+ }
+
+ /**
+ * Given a type of thrown exception, add the set of possible control
+ * flow successor {@link Label}s to the argument set. Return true
+ * if the exception is known to be caught by one of those labels and
+ * false if it may propagate still further.
+ */
+ @Override
+ public boolean possibleLabels(TypeMirror thrown, Set<Label> labels) {
+ // A conservative approach would be to say that every catch block
+ // might execute for any thrown exception, but we try to do better.
+ //
+ // We rely on several assumptions that seem to hold as of Java 7.
+ // 1) An exception parameter in a catch block must be either
+ // a declared type or a union composed of declared types,
+ // all of which are subtypes of Throwable.
+ // 2) A thrown type must either be a declared type or a variable
+ // that extends a declared type, which is a subtype of Throwable.
+ //
+ // Under those assumptions, if the thrown type (or its bound) is
+ // a subtype of the caught type (or one of its alternatives), then
+ // the catch block must apply and none of the later ones can apply.
+ // Otherwise, if the thrown type (or its bound) is a supertype
+ // of the caught type (or one of its alternatives), then the catch
+ // block may apply, but so may later ones.
+ // Otherwise, the thrown type and the caught type are unrelated
+ // declared types, so they do not overlap on any non-null value.
+
+ while (!(thrown instanceof DeclaredType)) {
+ assert thrown instanceof TypeVariable :
+ "thrown type must be a variable or a declared type";
+ thrown = ((TypeVariable)thrown).getUpperBound();
+ }
+ DeclaredType declaredThrown = (DeclaredType)thrown;
+ assert thrown != null : "thrown type must be bounded by a declared type";
+
+ for (Pair<TypeMirror, Label> pair : catchLabels) {
+ TypeMirror caught = pair.first;
+ boolean canApply = false;
+
+ if (caught instanceof DeclaredType) {
+ DeclaredType declaredCaught = (DeclaredType)caught;
+ if (types.isSubtype(declaredThrown, declaredCaught)) {
+ // No later catch blocks can apply.
+ labels.add(pair.second);
+ return true;
+ } else if (types.isSubtype(declaredCaught, declaredThrown)) {
+ canApply = true;
+ }
+ } else {
+ assert caught instanceof UnionType :
+ "caught type must be a union or a declared type";
+ UnionType caughtUnion = (UnionType)caught;
+ for (TypeMirror alternative : caughtUnion.getAlternatives()) {
+ assert alternative instanceof DeclaredType :
+ "alternatives of an caught union type must be declared types";
+ DeclaredType declaredAlt = (DeclaredType)alternative;
+ if (types.isSubtype(declaredThrown, declaredAlt)) {
+ // No later catch blocks can apply.
+ labels.add(pair.second);
+ return true;
+ } else if (types.isSubtype(declaredAlt, declaredThrown)) {
+ canApply = true;
+ }
+ }
+ }
+
+ if (canApply) {
+ labels.add(pair.second);
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * A TryFinallyFrame applies to exceptions of any type
+ */
+ protected class TryFinallyFrame implements TryFrame {
+ protected Label finallyLabel;
+
+ public TryFinallyFrame(Label finallyLabel) {
+ this.finallyLabel = finallyLabel;
+ }
+
+ @Override
+ public boolean possibleLabels(TypeMirror thrown, Set<Label> labels) {
+ labels.add(finallyLabel);
+ return true;
+ }
+ }
+
+ /**
+ * An exception stack represents the set of all try-catch blocks
+ * in effect at a given point in a program. It maps an exception
+ * type to a set of Labels and it maps a block exit (via return or
+ * fall-through) to a single Label.
+ */
+ protected static class TryStack {
+ protected Label exitLabel;
+ protected LinkedList<TryFrame> frames;
+
+ public TryStack(Label exitLabel) {
+ this.exitLabel = exitLabel;
+ this.frames = new LinkedList<>();
+ }
+
+ public void pushFrame(TryFrame frame) {
+ frames.addFirst(frame);
+ }
+
+ public void popFrame() {
+ frames.removeFirst();
+ }
+
+ /**
+ * Returns the set of possible {@link Label}s where control may
+ * transfer when an exception of the given type is thrown.
+ */
+ public Set<Label> possibleLabels(TypeMirror thrown) {
+ // Work up from the innermost frame until the exception is known to
+ // be caught.
+ Set<Label> labels = new MostlySingleton<>();
+ for (TryFrame frame : frames) {
+ if (frame.possibleLabels(thrown, labels)) {
+ return labels;
+ }
+ }
+ labels.add(exitLabel);
+ return labels;
+ }
+ }
+
+ /* --------------------------------------------------------- */
+ /* Phase Three */
+ /* --------------------------------------------------------- */
+
+ /**
+ * Class that performs phase three of the translation process. In
+ * particular, the following degenerate cases of basic blocks are removed:
+ *
+ * <ol>
+ * <li>Empty regular basic blocks: These blocks will be removed and their
+ * predecessors linked directly to the successor.</li>
+ * <li>Conditional basic blocks that have the same basic block as the 'then'
+ * and 'else' successor: The conditional basic block will be removed in this
+ * case.</li>
+ * <li>Two consecutive, non-empty, regular basic blocks where the second
+ * block has exactly one predecessor (namely the other of the two blocks):
+ * In this case, the two blocks are merged.</li>
+ * <li>Some basic blocks might not be reachable from the entryBlock. These
+ * basic blocks are removed, and the list of predecessors (in the
+ * doubly-linked structure of basic blocks) are adapted correctly.</li>
+ * </ol>
+ *
+ * Eliminating the second type of degenerate cases might introduce cases of
+ * the third problem. These are also removed.
+ */
+ public static class CFGTranslationPhaseThree {
+
+ /**
+ * A simple wrapper object that holds a basic block and allows to set
+ * one of its successors.
+ */
+ protected interface PredecessorHolder {
+ void setSuccessor(BlockImpl b);
+
+ BlockImpl getBlock();
+ }
+
+ /**
+ * Perform phase three on the control flow graph {@code cfg}.
+ *
+ * @param cfg
+ * The control flow graph. Ownership is transfered to this
+ * method and the caller is not allowed to read or modify
+ * {@code cfg} after the call to {@code process} any more.
+ * @return the resulting control flow graph
+ */
+ public static ControlFlowGraph process(ControlFlowGraph cfg) {
+ Set<Block> worklist = cfg.getAllBlocks();
+ Set<Block> dontVisit = new HashSet<>();
+
+ // note: this method has to be careful when relinking basic blocks
+ // to not forget to adjust the predecessors, too
+
+ // fix predecessor lists by removing any unreachable predecessors
+ for (Block c : worklist) {
+ BlockImpl cur = (BlockImpl) c;
+ for (BlockImpl pred : new HashSet<>(cur.getPredecessors())) {
+ if (!worklist.contains(pred)) {
+ cur.removePredecessor(pred);
+ }
+ }
+ }
+
+ // remove empty blocks
+ for (Block cur : worklist) {
+ if (dontVisit.contains(cur)) {
+ continue;
+ }
+
+ if (cur.getType() == BlockType.REGULAR_BLOCK) {
+ RegularBlockImpl b = (RegularBlockImpl) cur;
+ if (b.isEmpty()) {
+ Set<RegularBlockImpl> empty = new HashSet<>();
+ Set<PredecessorHolder> predecessors = new HashSet<>();
+ BlockImpl succ = computeNeighborhoodOfEmptyBlock(b,
+ empty, predecessors);
+ for (RegularBlockImpl e : empty) {
+ succ.removePredecessor(e);
+ dontVisit.add(e);
+ }
+ for (PredecessorHolder p : predecessors) {
+ BlockImpl block = p.getBlock();
+ dontVisit.add(block);
+ succ.removePredecessor(block);
+ p.setSuccessor(succ);
+ }
+ }
+ }
+ }
+
+ // remove useless conditional blocks
+ worklist = cfg.getAllBlocks();
+ for (Block c : worklist) {
+ BlockImpl cur = (BlockImpl) c;
+
+ if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
+ ConditionalBlockImpl cb = (ConditionalBlockImpl) cur;
+ assert cb.getPredecessors().size() == 1;
+ if (cb.getThenSuccessor() == cb.getElseSuccessor()) {
+ BlockImpl pred = cb.getPredecessors().iterator().next();
+ PredecessorHolder predecessorHolder = getPredecessorHolder(
+ pred, cb);
+ BlockImpl succ = (BlockImpl) cb.getThenSuccessor();
+ succ.removePredecessor(cb);
+ predecessorHolder.setSuccessor(succ);
+ }
+ }
+ }
+
+ // merge consecutive basic blocks if possible
+ worklist = cfg.getAllBlocks();
+ for (Block cur : worklist) {
+ if (cur.getType() == BlockType.REGULAR_BLOCK) {
+ RegularBlockImpl b = (RegularBlockImpl) cur;
+ Block succ = b.getRegularSuccessor();
+ if (succ.getType() == BlockType.REGULAR_BLOCK) {
+ RegularBlockImpl rs = (RegularBlockImpl) succ;
+ if (rs.getPredecessors().size() == 1) {
+ b.setSuccessor(rs.getRegularSuccessor());
+ b.addNodes(rs.getContents());
+ rs.getRegularSuccessor().removePredecessor(rs);
+ }
+ }
+ }
+ }
+
+ return cfg;
+ }
+
+ /**
+ * Compute the set of empty regular basic blocks {@code empty}, starting
+ * at {@code start} and going both forward and backwards. Furthermore,
+ * compute the predecessors of these empty blocks ({@code predecessors}
+ * ), and their single successor (return value).
+ *
+ * @param start
+ * The starting point of the search (an empty, regular basic
+ * block).
+ * @param empty
+ * An empty set to be filled by this method with all empty
+ * basic blocks found (including {@code start}).
+ * @param predecessors
+ * An empty set to be filled by this method with all
+ * predecessors.
+ * @return the single successor of the set of the empty basic blocks
+ */
+ protected static BlockImpl computeNeighborhoodOfEmptyBlock(
+ RegularBlockImpl start, Set<RegularBlockImpl> empty,
+ Set<PredecessorHolder> predecessors) {
+
+ // get empty neighborhood that come before 'start'
+ computeNeighborhoodOfEmptyBlockBackwards(start, empty, predecessors);
+
+ // go forward
+ BlockImpl succ = (BlockImpl) start.getSuccessor();
+ while (succ.getType() == BlockType.REGULAR_BLOCK) {
+ RegularBlockImpl cur = (RegularBlockImpl) succ;
+ if (cur.isEmpty()) {
+ computeNeighborhoodOfEmptyBlockBackwards(cur, empty,
+ predecessors);
+ assert empty.contains(cur) : "cur ought to be in empty";
+ succ = (BlockImpl) cur.getSuccessor();
+ if (succ == cur) {
+ // An infinite loop, making exit block unreachable
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return succ;
+ }
+
+ /**
+ * Compute the set of empty regular basic blocks {@code empty}, starting
+ * at {@code start} and looking only backwards in the control flow
+ * graph. Furthermore, compute the predecessors of these empty blocks (
+ * {@code predecessors}).
+ *
+ * @param start
+ * The starting point of the search (an empty, regular basic
+ * block).
+ * @param empty
+ * A set to be filled by this method with all empty basic
+ * blocks found (including {@code start}).
+ * @param predecessors
+ * A set to be filled by this method with all predecessors.
+ */
+ protected static void computeNeighborhoodOfEmptyBlockBackwards(
+ RegularBlockImpl start, Set<RegularBlockImpl> empty,
+ Set<PredecessorHolder> predecessors) {
+
+ RegularBlockImpl cur = start;
+ empty.add(cur);
+ for (final BlockImpl pred : cur.getPredecessors()) {
+ switch (pred.getType()) {
+ case SPECIAL_BLOCK:
+ // add pred correctly to predecessor list
+ predecessors.add(getPredecessorHolder(pred, cur));
+ break;
+ case CONDITIONAL_BLOCK:
+ // add pred correctly to predecessor list
+ predecessors.add(getPredecessorHolder(pred, cur));
+ break;
+ case EXCEPTION_BLOCK:
+ // add pred correctly to predecessor list
+ predecessors.add(getPredecessorHolder(pred, cur));
+ break;
+ case REGULAR_BLOCK:
+ RegularBlockImpl r = (RegularBlockImpl) pred;
+ if (r.isEmpty()) {
+ // recursively look backwards
+ if (!empty.contains(r)) {
+ computeNeighborhoodOfEmptyBlockBackwards(r, empty,
+ predecessors);
+ }
+ } else {
+ // add pred correctly to predecessor list
+ predecessors.add(getPredecessorHolder(pred, cur));
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Return a predecessor holder that can be used to set the successor of
+ * {@code pred} in the place where previously the edge pointed to
+ * {@code cur}. Additionally, the predecessor holder also takes care of
+ * unlinking (i.e., removing the {@code pred} from {@code cur's}
+ * predecessors).
+ */
+ protected static PredecessorHolder getPredecessorHolder(
+ final BlockImpl pred, final BlockImpl cur) {
+ switch (pred.getType()) {
+ case SPECIAL_BLOCK:
+ SingleSuccessorBlockImpl s = (SingleSuccessorBlockImpl) pred;
+ return singleSuccessorHolder(s, cur);
+ case CONDITIONAL_BLOCK:
+ // add pred correctly to predecessor list
+ final ConditionalBlockImpl c = (ConditionalBlockImpl) pred;
+ if (c.getThenSuccessor() == cur) {
+ return new PredecessorHolder() {
+ @Override
+ public void setSuccessor(BlockImpl b) {
+ c.setThenSuccessor(b);
+ cur.removePredecessor(pred);
+ }
+
+ @Override
+ public BlockImpl getBlock() {
+ return c;
+ }
+ };
+ } else {
+ assert c.getElseSuccessor() == cur;
+ return new PredecessorHolder() {
+ @Override
+ public void setSuccessor(BlockImpl b) {
+ c.setElseSuccessor(b);
+ cur.removePredecessor(pred);
+ }
+
+ @Override
+ public BlockImpl getBlock() {
+ return c;
+ }
+ };
+ }
+ case EXCEPTION_BLOCK:
+ // add pred correctly to predecessor list
+ final ExceptionBlockImpl e = (ExceptionBlockImpl) pred;
+ if (e.getSuccessor() == cur) {
+ return singleSuccessorHolder(e, cur);
+ } else {
+ Set<Entry<TypeMirror, Set<Block>>> entrySet = e
+ .getExceptionalSuccessors().entrySet();
+ for (final Entry<TypeMirror, Set<Block>> entry : entrySet) {
+ if (entry.getValue().contains(cur)) {
+ return new PredecessorHolder() {
+ @Override
+ public void setSuccessor(BlockImpl b) {
+ e.addExceptionalSuccessor(b, entry.getKey());
+ cur.removePredecessor(pred);
+ }
+
+ @Override
+ public BlockImpl getBlock() {
+ return e;
+ }
+ };
+ }
+ }
+ }
+ assert false;
+ break;
+ case REGULAR_BLOCK:
+ RegularBlockImpl r = (RegularBlockImpl) pred;
+ return singleSuccessorHolder(r, cur);
+ }
+ return null;
+ }
+
+ /**
+ * @return a {@link PredecessorHolder} that sets the successor of a
+ * single successor block {@code s}.
+ */
+ protected static PredecessorHolder singleSuccessorHolder(
+ final SingleSuccessorBlockImpl s, final BlockImpl old) {
+ return new PredecessorHolder() {
+ @Override
+ public void setSuccessor(BlockImpl b) {
+ s.setSuccessor(b);
+ old.removePredecessor(s);
+ }
+
+ @Override
+ public BlockImpl getBlock() {
+ return s;
+ }
+ };
+ }
+ }
+
+ /* --------------------------------------------------------- */
+ /* Phase Two */
+ /* --------------------------------------------------------- */
+
+ /** Tuple class with up to three members. */
+ protected static class Tuple<A, B, C> {
+ public A a;
+ public B b;
+ public C c;
+
+ public Tuple(A a, B b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ public Tuple(A a, B b, C c) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }
+ }
+
+ /**
+ * Class that performs phase two of the translation process.
+ */
+ public class CFGTranslationPhaseTwo {
+
+ public CFGTranslationPhaseTwo() {
+ }
+
+ /**
+ * Perform phase two of the translation.
+ *
+ * @param in
+ * The result of phase one.
+ * @return a control flow graph that might still contain degenerate
+ * basic block (such as empty regular basic blocks or
+ * conditional blocks with the same block as 'then' and 'else'
+ * sucessor)
+ */
+ public ControlFlowGraph process(PhaseOneResult in) {
+
+ Map<Label, Integer> bindings = in.bindings;
+ ArrayList<ExtendedNode> nodeList = in.nodeList;
+ Set<Integer> leaders = in.leaders;
+
+ assert in.nodeList.size() > 0;
+
+ // exit blocks
+ SpecialBlockImpl regularExitBlock = new SpecialBlockImpl(
+ SpecialBlockType.EXIT);
+ SpecialBlockImpl exceptionalExitBlock = new SpecialBlockImpl(
+ SpecialBlockType.EXCEPTIONAL_EXIT);
+
+ // record missing edges that will be added later
+ Set<Tuple<? extends SingleSuccessorBlockImpl, Integer, ?>> missingEdges = new MostlySingleton<>();
+
+ // missing exceptional edges
+ Set<Tuple<ExceptionBlockImpl, Integer, TypeMirror>> missingExceptionalEdges = new HashSet<>();
+
+ // create start block
+ SpecialBlockImpl startBlock = new SpecialBlockImpl(
+ SpecialBlockType.ENTRY);
+ missingEdges.add(new Tuple<>(startBlock, 0));
+
+ // loop through all 'leaders' (while dynamically detecting the
+ // leaders)
+ RegularBlockImpl block = new RegularBlockImpl();
+ int i = 0;
+ for (ExtendedNode node : nodeList) {
+ switch (node.getType()) {
+ case NODE:
+ if (leaders.contains(i)) {
+ RegularBlockImpl b = new RegularBlockImpl();
+ block.setSuccessor(b);
+ block = b;
+ }
+ block.addNode(node.getNode());
+ node.setBlock(block);
+
+ // does this node end the execution (modeled as an edge to
+ // the exceptional exit block)
+ boolean terminatesExecution = node.getTerminatesExecution();
+ if (terminatesExecution) {
+ block.setSuccessor(exceptionalExitBlock);
+ block = new RegularBlockImpl();
+ }
+ break;
+ case CONDITIONAL_JUMP: {
+ ConditionalJump cj = (ConditionalJump) node;
+ // Exception nodes may fall through to conditional jumps,
+ // so we set the block which is required for the insertion
+ // of missing edges.
+ node.setBlock(block);
+ assert block != null;
+ final ConditionalBlockImpl cb = new ConditionalBlockImpl();
+ if (cj.getTrueFlowRule() != null) {
+ cb.setThenFlowRule(cj.getTrueFlowRule());
+ }
+ if (cj.getFalseFlowRule() != null) {
+ cb.setElseFlowRule(cj.getFalseFlowRule());
+ }
+ block.setSuccessor(cb);
+ block = new RegularBlockImpl();
+ // use two anonymous SingleSuccessorBlockImpl that set the
+ // 'then' and 'else' successor of the conditional block
+ final Label thenLabel = cj.getThenLabel();
+ final Label elseLabel = cj.getElseLabel();
+ missingEdges.add(new Tuple<>(
+ new SingleSuccessorBlockImpl() {
+ @Override
+ public void setSuccessor(BlockImpl successor) {
+ cb.setThenSuccessor(successor);
+ }
+ }, bindings.get(thenLabel)));
+ missingEdges.add(new Tuple<>(
+ new SingleSuccessorBlockImpl() {
+ @Override
+ public void setSuccessor(BlockImpl successor) {
+ cb.setElseSuccessor(successor);
+ }
+ }, bindings.get(elseLabel)));
+ break;
+ }
+ case UNCONDITIONAL_JUMP:
+ if (leaders.contains(i)) {
+ RegularBlockImpl b = new RegularBlockImpl();
+ block.setSuccessor(b);
+ block = b;
+ }
+ node.setBlock(block);
+ if (node.getLabel() == regularExitLabel) {
+ block.setSuccessor(regularExitBlock);
+ } else if (node.getLabel() == exceptionalExitLabel) {
+ block.setSuccessor(exceptionalExitBlock);
+ } else {
+ missingEdges.add(new Tuple<>(block, bindings.get(node
+ .getLabel())));
+ }
+ block = new RegularBlockImpl();
+ break;
+ case EXCEPTION_NODE:
+ NodeWithExceptionsHolder en = (NodeWithExceptionsHolder) node;
+ // create new exception block and link with previous block
+ ExceptionBlockImpl e = new ExceptionBlockImpl();
+ Node nn = en.getNode();
+ e.setNode(nn);
+ node.setBlock(e);
+ block.setSuccessor(e);
+ block = new RegularBlockImpl();
+
+ // ensure linking between e and next block (normal edge)
+ // Note: do not link to the next block for throw statements
+ // (these throw exceptions for sure)
+ if (!node.getTerminatesExecution()) {
+ missingEdges.add(new Tuple<>(e, i + 1));
+ }
+
+ // exceptional edges
+ for (Entry<TypeMirror, Set<Label>> entry : en.getExceptions()
+ .entrySet()) {
+ TypeMirror cause = entry.getKey();
+ for (Label label : entry.getValue()) {
+ Integer target = bindings.get(label);
+ missingExceptionalEdges
+ .add(new Tuple<ExceptionBlockImpl, Integer, TypeMirror>(
+ e, target, cause));
+ }
+ }
+ break;
+ }
+ i++;
+ }
+
+ // add missing edges
+ for (Tuple<? extends SingleSuccessorBlockImpl, Integer, ?> p : missingEdges) {
+ Integer index = p.b;
+ ExtendedNode extendedNode = nodeList.get(index);
+ BlockImpl target = extendedNode.getBlock();
+ SingleSuccessorBlockImpl source = p.a;
+ source.setSuccessor(target);
+ }
+
+ // add missing exceptional edges
+ for (Tuple<ExceptionBlockImpl, Integer, ?> p : missingExceptionalEdges) {
+ Integer index = p.b;
+ TypeMirror cause = (TypeMirror) p.c;
+ ExceptionBlockImpl source = p.a;
+ if (index == null) {
+ // edge to exceptional exit
+ source.addExceptionalSuccessor(exceptionalExitBlock, cause);
+ } else {
+ // edge to specific target
+ ExtendedNode extendedNode = nodeList.get(index);
+ BlockImpl target = extendedNode.getBlock();
+ source.addExceptionalSuccessor(target, cause);
+ }
+ }
+
+ return new ControlFlowGraph(startBlock, regularExitBlock, exceptionalExitBlock, in.underlyingAST,
+ in.treeLookupMap, in.convertedTreeLookupMap, in.returnNodes);
+ }
+ }
+
+ /* --------------------------------------------------------- */
+ /* Phase One */
+ /* --------------------------------------------------------- */
+
+ /**
+ * A wrapper object to pass around the result of phase one. For a
+ * documentation of the fields see {@link CFGTranslationPhaseOne}.
+ */
+ protected static class PhaseOneResult {
+
+ private final IdentityHashMap<Tree, Node> treeLookupMap;
+ private final IdentityHashMap<Tree, Node> convertedTreeLookupMap;
+ private final UnderlyingAST underlyingAST;
+ private final Map<Label, Integer> bindings;
+ private final ArrayList<ExtendedNode> nodeList;
+ private final Set<Integer> leaders;
+ private final List<ReturnNode> returnNodes;
+
+ public PhaseOneResult(UnderlyingAST underlyingAST,
+ IdentityHashMap<Tree, Node> treeLookupMap,
+ IdentityHashMap<Tree, Node> convertedTreeLookupMap,
+ ArrayList<ExtendedNode> nodeList, Map<Label, Integer> bindings,
+ Set<Integer> leaders, List<ReturnNode> 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:
+ * <ul>
+ * <li>A sequence of extended nodes.</li>
+ * <li>A set of bindings from {@link Label}s to positions in the node
+ * sequence.</li>
+ * <li>A set of leader nodes that give rise to basic blocks in phase two.</li>
+ * <li>A lookup map that gives the mapping from AST tree nodes to
+ * {@link Node}s.</li>
+ * </ul>
+ *
+ * <p>
+ *
+ * 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).
+ *
+ * <p>
+ *
+ * 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<Node, Void> {
+
+ 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<Name, Label> 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<Name, Label> 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<Tree, Node> treeLookupMap;
+
+ /** Map from AST {@link Tree}s to post-conversion {@link Node}s. */
+ protected IdentityHashMap<Tree, Node> convertedTreeLookupMap;
+
+ /** The list of extended nodes. */
+ protected ArrayList<ExtendedNode> nodeList;
+
+ /**
+ * The bindings of labels to positions (i.e., indices) in the
+ * {@code nodeList}.
+ */
+ protected Map<Label, Integer> bindings;
+
+ /** The set of leaders (represented as indices into {@code nodeList}). */
+ protected Set<Integer> leaders;
+
+ /**
+ * All return nodes (if any) encountered. Only includes return
+ * statements that actually return something
+ */
+ private List<ReturnNode> 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 extends Node> 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<TypeMirror> causes) {
+ addToLookupMap(node);
+ Map<TypeMirror, Set<Label>> 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 extends Node> 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<TypeMirror> causes, Node pred) {
+ addToLookupMap(node);
+ Map<TypeMirror, Set<Label>> 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<Label, Integer> e : bindings.entrySet()) {
+ if (e.getValue() >= index+1) {
+ bindings.put(e.getKey(), e.getValue() + 1);
+ }
+ }
+ // update leaders
+ Set<Integer> 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.<Node>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<Node> convertCallArguments(ExecutableElement method,
+ List<? extends ExpressionTree> 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<? extends VariableElement> formals = method.getParameters();
+
+ ArrayList<Node> convertedNodes = new ArrayList<Node>();
+
+ 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<Node> dimensions = new ArrayList<>();
+ List<Node> 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);
+ // 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<? extends ExpressionTree> 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<Node> 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<TypeMirror> thrownSet = new HashSet<>();
+ // Add exceptions explicitly mentioned in the throws clause.
+ List<? extends TypeMirror> 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.
+ * <p>
+ * 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.<Node>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.<Node>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.<Node>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<? extends ExpressionTree> dimensions = tree.getDimensions();
+ List<? extends ExpressionTree> initializers = tree
+ .getInitializers();
+
+ List<Node> dimensionNodes = new ArrayList<Node>();
+ if (dimensions != null) {
+ for (ExpressionTree dim : dimensions) {
+ dimensionNodes.add(unaryNumericPromotion(scan(dim, p)));
+ }
+ }
+
+ List<Node> initializerNodes = new ArrayList<Node>();
+ 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<? extends ExpressionTree> actualExprs = tree.getArguments();
+
+ List<Node> arguments = convertCallArguments(constructor,
+ actualExprs);
+
+ Node constructorNode = scan(tree.getIdentifier(), p);
+
+ Node node = new ObjectCreationNode(tree, constructorNode, arguments);
+
+ Set<TypeMirror> thrownSet = new HashSet<>();
+ // Add exceptions explicitly mentioned in the throws clause.
+ List<? extends TypeMirror> 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<Tree, ParenthesizedTree> 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<Kind>(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<? extends CatchTree> 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<? extends Tree> resources = tree.getResources();
+ for (Tree resource : resources) {
+ scan(resource, p);
+ }
+
+ List<Pair<TypeMirror, Label>> 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> 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<Block> 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<TypeMirror, Set<Block>> 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;
+ }
+ }
+ }
+ }
+}