diff options
author | Liam Miller-Cushon <cushon@google.com> | 2017-10-15 23:31:56 -0700 |
---|---|---|
committer | Damien Martin-Guillerez <dmarting@google.com> | 2017-10-16 14:16:39 +0200 |
commit | 6bf3f268f4a01963a2ee13f60178664bb056a802 (patch) | |
tree | 32a870dc293e07af88f52b241c75d9cbd462f63f /third_party/checker_framework_dataflow | |
parent | 80a34dc97799961201e6dce20fd58dd08022c032 (diff) |
Update checker framework dataflow and javacutils to 2.1.14
Change-Id: I62ad827fc4bbd54d022097003af63e351e44b98c
Diffstat (limited to 'third_party/checker_framework_dataflow')
119 files changed, 4862 insertions, 5424 deletions
diff --git a/third_party/checker_framework_dataflow/dataflow-1.8.10.jar b/third_party/checker_framework_dataflow/dataflow-1.8.10.jar Binary files differdeleted file mode 100644 index 4cbe8f1f3d..0000000000 --- a/third_party/checker_framework_dataflow/dataflow-1.8.10.jar +++ /dev/null diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AbstractValue.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AbstractValue.java index 2dbcbd4b03..25b78075da 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AbstractValue.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AbstractValue.java @@ -4,23 +4,21 @@ package org.checkerframework.dataflow.analysis; * An abstract value used in the org.checkerframework.dataflow analysis. * * @author Stefan Heule - * */ public interface AbstractValue<V extends AbstractValue<V>> { /** * Compute the least upper bound of two stores. * - * <p> + * <p><em>Important</em>: This method must fulfill the following contract: * - * <em>Important</em>: This method must fulfill the following contract: * <ul> - * <li>Does not change {@code this}.</li> - * <li>Does not change {@code other}.</li> - * <li>Returns a fresh object which is not aliased yet.</li> - * <li>Returns an object of the same (dynamic) type as {@code this}, even if - * the signature is more permissive.</li> - * <li>Is commutative.</li> + * <li>Does not change {@code this}. + * <li>Does not change {@code other}. + * <li>Returns a fresh object which is not aliased yet. + * <li>Returns an object of the same (dynamic) type as {@code this}, even if the signature is + * more permissive. + * <li>Is commutative. * </ul> */ V leastUpperBound(V other); diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Analysis.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Analysis.java index fa07247e68..2e81b4af4a 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Analysis.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Analysis.java @@ -4,7 +4,25 @@ package org.checkerframework.dataflow.analysis; import org.checkerframework.checker.nullness.qual.Nullable; */ +import com.sun.source.tree.ClassTree; import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.PriorityQueue; +import java.util.Set; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; import org.checkerframework.dataflow.cfg.ControlFlowGraph; import org.checkerframework.dataflow.cfg.UnderlyingAST; import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGLambda; @@ -19,46 +37,20 @@ import org.checkerframework.dataflow.cfg.node.AssignmentNode; import org.checkerframework.dataflow.cfg.node.LocalVariableNode; import org.checkerframework.dataflow.cfg.node.Node; import org.checkerframework.dataflow.cfg.node.ReturnNode; - import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.Pair; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.PriorityQueue; -import java.util.Set; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Types; - -import com.sun.source.tree.ClassTree; -import com.sun.source.tree.MethodTree; -import com.sun.source.tree.Tree; -import com.sun.source.tree.VariableTree; - /** * An implementation of an iterative algorithm to solve a org.checkerframework.dataflow problem, * given a control flow graph and a transfer function. * * @author Stefan Heule - * - * @param <A> - * The abstract value type to be tracked by the analysis. - * @param <S> - * The store type used in the analysis. - * @param <T> - * The transfer function type that is used to approximated runtime - * behavior. + * @param <A> the abstract value type to be tracked by the analysis + * @param <S> the store type used in the analysis + * @param <T> the transfer function type that is used to approximated runtime behavior */ -public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> { +public class Analysis< + A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> { /** Is the analysis currently running? */ protected boolean isRunning = false; @@ -75,27 +67,30 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends /** Instance of the types utility. */ protected final Types types; - /** - * Then stores before every basic block (assumed to be 'no information' if - * not present). - */ + /** Then stores before every basic block (assumed to be 'no information' if not present). */ protected IdentityHashMap<Block, S> thenStores; + /** Else stores before every basic block (assumed to be 'no information' if not present). */ + protected IdentityHashMap<Block, S> elseStores; + /** - * Else stores before every basic block (assumed to be 'no information' if - * not present). + * Number of times every block has been analyzed since the last time widening was applied. Null, + * if maxCountBeforeWidening is -1 which implies widening isn't used for this analysis. */ - protected IdentityHashMap<Block, S> elseStores; + protected IdentityHashMap<Block, Integer> blockCount; /** - * The transfer inputs before every basic block (assumed to be 'no information' if - * not present). + * Number of times a block can be analyzed before widening. -1 implies that widening shouldn't + * be used. */ - protected IdentityHashMap<Block, TransferInput<A, S>> inputs; + protected final int maxCountBeforeWidening; /** - * The stores after every return statement. + * The transfer inputs before every basic block (assumed to be 'no information' if not present). */ + protected IdentityHashMap<Block, TransferInput<A, S>> inputs; + + /** The stores after every return statement. */ protected IdentityHashMap<ReturnNode, TransferResult<A, S>> storesAtReturnStatements; /** The worklist used for the fix-point iteration. */ @@ -108,25 +103,22 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends public HashMap<Element, A> finalLocalValues; /** - * The node that is currently handled in the analysis (if it is running). - * The following invariant holds: + * The node that is currently handled in the analysis (if it is running). The following + * invariant holds: * * <pre> - * !isRunning ==> (currentNode == null) + * !isRunning ⇒ (currentNode == null) * </pre> */ protected Node currentNode; /** - * The tree that is currently being looked at. The transfer function can set - * this tree to make sure that calls to {@code getValue} will not return - * information for this given tree. + * The tree that is currently being looked at. The transfer function can set this tree to make + * sure that calls to {@code getValue} will not return information for this given tree. */ protected Tree currentTree; - /** - * The current transfer input when the analysis is running. - */ + /** The current transfer input when the analysis is running. */ protected TransferInput<A, S> currentInput; public Tree getCurrentTree() { @@ -139,12 +131,10 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends /** * Construct an object that can perform a org.checkerframework.dataflow analysis over a control - * flow graph. The transfer function is set later using - * {@code setTransferFunction}. + * flow graph. The transfer function is set later using {@code setTransferFunction}. */ public Analysis(ProcessingEnvironment env) { - this.env = env; - types = env.getTypeUtils(); + this(env, null, -1); } /** @@ -152,7 +142,18 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends * flow graph, given a transfer function. */ public Analysis(ProcessingEnvironment env, T transfer) { - this(env); + this(env, transfer, -1); + } + + /** + * Construct an object that can perform a org.checkerframework.dataflow analysis over a control + * flow graph, given a transfer function. + */ + public Analysis(ProcessingEnvironment env, T transfer, int maxCountBeforeWidening) { + this.env = env; + types = env.getTypeUtils(); + this.transferFunction = transfer; + this.maxCountBeforeWidening = maxCountBeforeWidening; this.transferFunction = transfer; } @@ -173,10 +174,7 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } /** - * Perform the actual analysis. Should only be called once after the object - * has been created. - * - * @param cfg + * Perform the actual analysis. Should only be called once after the object has been created. */ public void performAnalysis(ControlFlowGraph cfg) { assert isRunning == false; @@ -188,101 +186,116 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends Block b = worklist.poll(); switch (b.getType()) { - case REGULAR_BLOCK: { - RegularBlock rb = (RegularBlock) b; - - // apply transfer function to contents - TransferInput<A, S> inputBefore = getInputBefore(rb); - currentInput = inputBefore.copy(); - TransferResult<A, S> transferResult = null; - Node lastNode = null; - boolean addToWorklistAgain = false; - for (Node n : rb.getContents()) { - transferResult = callTransferFunction(n, currentInput); - addToWorklistAgain |= updateNodeValues(n, transferResult); - currentInput = new TransferInput<>(n, this, transferResult); - lastNode = n; - } - // loop will run at least one, making transferResult non-null - - // propagate store to successors - Block succ = rb.getSuccessor(); - assert succ != null : "regular basic block without non-exceptional successor unexpected"; - propagateStoresTo(succ, lastNode, currentInput, rb.getFlowRule(), addToWorklistAgain); - break; - } - - case EXCEPTION_BLOCK: { - ExceptionBlock eb = (ExceptionBlock) b; - - // apply transfer function to content - TransferInput<A, S> inputBefore = getInputBefore(eb); - currentInput = inputBefore.copy(); - Node node = eb.getNode(); - TransferResult<A, S> transferResult = callTransferFunction( - node, currentInput); - boolean addToWorklistAgain = updateNodeValues(node, transferResult); - - // propagate store to successor - Block succ = eb.getSuccessor(); - if (succ != null) { - currentInput = new TransferInput<>(node, this, transferResult); - // TODO? Variable wasn't used. - // Store.FlowRule storeFlow = eb.getFlowRule(); - propagateStoresTo(succ, node, currentInput, eb.getFlowRule(), addToWorklistAgain); - } + case REGULAR_BLOCK: + { + RegularBlock rb = (RegularBlock) b; + + // apply transfer function to contents + TransferInput<A, S> inputBefore = getInputBefore(rb); + currentInput = inputBefore.copy(); + TransferResult<A, S> transferResult = null; + Node lastNode = null; + boolean addToWorklistAgain = false; + for (Node n : rb.getContents()) { + transferResult = callTransferFunction(n, currentInput); + addToWorklistAgain |= updateNodeValues(n, transferResult); + currentInput = new TransferInput<>(n, this, transferResult); + lastNode = n; + } + // loop will run at least one, making transferResult non-null + + // propagate store to successors + Block succ = rb.getSuccessor(); + assert succ != null + : "regular basic block without non-exceptional successor unexpected"; + propagateStoresTo( + succ, lastNode, currentInput, rb.getFlowRule(), addToWorklistAgain); + break; + } - // propagate store to exceptional successors - for (Entry<TypeMirror, Set<Block>> e : eb.getExceptionalSuccessors() - .entrySet()) { - TypeMirror cause = e.getKey(); - S exceptionalStore = transferResult - .getExceptionalStore(cause); - if (exceptionalStore != null) { - for (Block exceptionSucc : e.getValue()) { - addStoreBefore(exceptionSucc, node, exceptionalStore, Store.Kind.BOTH, - addToWorklistAgain); + case EXCEPTION_BLOCK: + { + ExceptionBlock eb = (ExceptionBlock) b; + + // apply transfer function to content + TransferInput<A, S> inputBefore = getInputBefore(eb); + currentInput = inputBefore.copy(); + Node node = eb.getNode(); + TransferResult<A, S> transferResult = + callTransferFunction(node, currentInput); + boolean addToWorklistAgain = updateNodeValues(node, transferResult); + + // propagate store to successor + Block succ = eb.getSuccessor(); + if (succ != null) { + currentInput = new TransferInput<>(node, this, transferResult); + // TODO? Variable wasn't used. + // Store.FlowRule storeFlow = eb.getFlowRule(); + propagateStoresTo( + succ, node, currentInput, eb.getFlowRule(), addToWorklistAgain); } - } else { - for (Block exceptionSucc : e.getValue()) { - addStoreBefore(exceptionSucc, node, inputBefore.copy().getRegularStore(), - Store.Kind.BOTH, addToWorklistAgain); + + // propagate store to exceptional successors + for (Entry<TypeMirror, Set<Block>> e : + eb.getExceptionalSuccessors().entrySet()) { + TypeMirror cause = e.getKey(); + S exceptionalStore = transferResult.getExceptionalStore(cause); + if (exceptionalStore != null) { + for (Block exceptionSucc : e.getValue()) { + addStoreBefore( + exceptionSucc, + node, + exceptionalStore, + Store.Kind.BOTH, + addToWorklistAgain); + } + } else { + for (Block exceptionSucc : e.getValue()) { + addStoreBefore( + exceptionSucc, + node, + inputBefore.copy().getRegularStore(), + Store.Kind.BOTH, + addToWorklistAgain); + } + } } + break; } - } - break; - } - case CONDITIONAL_BLOCK: { - ConditionalBlock cb = (ConditionalBlock) b; + case CONDITIONAL_BLOCK: + { + ConditionalBlock cb = (ConditionalBlock) b; - // get store before - TransferInput<A, S> inputBefore = getInputBefore(cb); - TransferInput<A, S> input = inputBefore.copy(); + // get store before + TransferInput<A, S> inputBefore = getInputBefore(cb); + TransferInput<A, S> input = inputBefore.copy(); - // propagate store to successor - Block thenSucc = cb.getThenSuccessor(); - Block elseSucc = cb.getElseSuccessor(); + // propagate store to successor + Block thenSucc = cb.getThenSuccessor(); + Block elseSucc = cb.getElseSuccessor(); - propagateStoresTo(thenSucc, null, input, cb.getThenFlowRule(), false); - propagateStoresTo(elseSucc, null, input, cb.getElseFlowRule(), false); - break; - } + propagateStoresTo(thenSucc, null, input, cb.getThenFlowRule(), false); + propagateStoresTo(elseSucc, null, input, cb.getElseFlowRule(), false); + break; + } - case SPECIAL_BLOCK: { - // special basic blocks are empty and cannot throw exceptions, - // thus there is no need to perform any analysis. - SpecialBlock sb = (SpecialBlock) b; - Block succ = sb.getSuccessor(); - if (succ != null) { - propagateStoresTo(succ, null, getInputBefore(b), sb.getFlowRule(), false); - } - break; - } + case SPECIAL_BLOCK: + { + // special basic blocks are empty and cannot throw exceptions, + // thus there is no need to perform any analysis. + SpecialBlock sb = (SpecialBlock) b; + Block succ = sb.getSuccessor(); + if (succ != null) { + propagateStoresTo( + succ, null, getInputBefore(b), sb.getFlowRule(), false); + } + break; + } - default: - assert false; - break; + default: + assert false; + break; } } @@ -291,78 +304,104 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } /** - * Propagate the stores in currentInput to the successor block, succ, according to the - * flowRule. + * Propagate the stores in currentInput to the successor block, succ, according to the flowRule. */ - protected void propagateStoresTo(Block succ, Node node, TransferInput<A, S> currentInput, - Store.FlowRule flowRule, boolean addToWorklistAgain) { + protected void propagateStoresTo( + Block succ, + Node node, + TransferInput<A, S> currentInput, + Store.FlowRule flowRule, + boolean addToWorklistAgain) { switch (flowRule) { - case EACH_TO_EACH: - if (currentInput.containsTwoStores()) { - addStoreBefore(succ, node, currentInput.getThenStore(), Store.Kind.THEN, + case EACH_TO_EACH: + if (currentInput.containsTwoStores()) { + addStoreBefore( + succ, + node, + currentInput.getThenStore(), + Store.Kind.THEN, + addToWorklistAgain); + addStoreBefore( + succ, + node, + currentInput.getElseStore(), + Store.Kind.ELSE, + addToWorklistAgain); + } else { + addStoreBefore( + succ, + node, + currentInput.getRegularStore(), + Store.Kind.BOTH, + addToWorklistAgain); + } + break; + case THEN_TO_BOTH: + addStoreBefore( + succ, + node, + currentInput.getThenStore(), + Store.Kind.BOTH, addToWorklistAgain); - addStoreBefore(succ, node, currentInput.getElseStore(), Store.Kind.ELSE, + break; + case ELSE_TO_BOTH: + addStoreBefore( + succ, + node, + currentInput.getElseStore(), + Store.Kind.BOTH, addToWorklistAgain); - } else { - addStoreBefore(succ, node, currentInput.getRegularStore(), Store.Kind.BOTH, + break; + case THEN_TO_THEN: + addStoreBefore( + succ, + node, + currentInput.getThenStore(), + Store.Kind.THEN, addToWorklistAgain); - } - break; - case THEN_TO_BOTH: - addStoreBefore(succ, node, currentInput.getThenStore(), Store.Kind.BOTH, - addToWorklistAgain); - break; - case ELSE_TO_BOTH: - addStoreBefore(succ, node, currentInput.getElseStore(), Store.Kind.BOTH, - addToWorklistAgain); - break; - case THEN_TO_THEN: - addStoreBefore(succ, node, currentInput.getThenStore(), Store.Kind.THEN, - addToWorklistAgain); - break; - case ELSE_TO_ELSE: - addStoreBefore(succ, node, currentInput.getElseStore(), Store.Kind.ELSE, - addToWorklistAgain); - break; + break; + case ELSE_TO_ELSE: + addStoreBefore( + succ, + node, + currentInput.getElseStore(), + Store.Kind.ELSE, + addToWorklistAgain); + break; } } /** - * Updates the value of node {@code node} to the value of the - * {@code transferResult}. Returns true if the node's value changed, or a - * store was updated. + * Updates the value of node {@code node} to the value of the {@code transferResult}. Returns + * true if the node's value changed, or a store was updated. */ protected boolean updateNodeValues(Node node, TransferResult<A, S> transferResult) { - A newVal = transferResult.getResultValue(); - boolean nodeValueChanged = false; + A newVal = transferResult.getResultValue(); + boolean nodeValueChanged = false; - if (newVal != null) { - A oldVal = nodeValues.get(node); - nodeValues.put(node, newVal); - nodeValueChanged = !Objects.equals(oldVal, newVal); - } + if (newVal != null) { + A oldVal = nodeValues.get(node); + nodeValues.put(node, newVal); + nodeValueChanged = !Objects.equals(oldVal, newVal); + } - return nodeValueChanged || transferResult.storeChanged(); + return nodeValueChanged || transferResult.storeChanged(); } /** - * Call the transfer function for node {@code node}, and set that node as - * current node first. + * Call the transfer function for node {@code node}, and set that node as current node first. */ - protected TransferResult<A, S> callTransferFunction(Node node, - TransferInput<A, S> store) { + protected TransferResult<A, S> callTransferFunction(Node node, TransferInput<A, S> store) { if (node.isLValue()) { // TODO: should the default behavior be to return either a regular // transfer result or a conditional transfer result (depending on // store.hasTwoStores()), or is the following correct? - return new RegularTransferResult<A, S>(null, - store.getRegularStore()); + return new RegularTransferResult<A, S>(null, store.getRegularStore()); } store.node = node; currentNode = node; - TransferResult<A, S> transferResult = node.accept(transferFunction, - store); + TransferResult<A, S> transferResult = node.accept(transferFunction, store); currentNode = null; if (node instanceof ReturnNode) { // save a copy of the store to later check if some property held at @@ -389,6 +428,7 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends this.cfg = cfg; thenStores = new IdentityHashMap<>(); elseStores = new IdentityHashMap<>(); + blockCount = maxCountBeforeWidening == -1 ? null : new IdentityHashMap<Block, Integer>(); inputs = new IdentityHashMap<>(); storesAtReturnStatements = new IdentityHashMap<>(); worklist = new Worklist(cfg); @@ -428,8 +468,7 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } /** - * Add a basic block to the worklist. If <code>b</code> is already present, - * the method does nothing. + * Add a basic block to the worklist. If {@code b} is already present, the method does nothing. */ protected void addToWorklist(Block b) { // TODO: use a more efficient way to check if b is already present @@ -439,74 +478,84 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } /** - * Add a store before the basic block <code>b</code> by merging with the - * existing stores for that location. + * Add a store before the basic block {@code b} by merging with the existing stores for that + * location. */ - protected void addStoreBefore(Block b, Node node, S s, Store.Kind kind, - boolean addBlockToWorklist) { + protected void addStoreBefore( + Block b, Node node, S s, Store.Kind kind, boolean addBlockToWorklist) { S thenStore = getStoreBefore(b, Store.Kind.THEN); S elseStore = getStoreBefore(b, Store.Kind.ELSE); - - switch (kind) { - case THEN: { - // Update the then store - S newThenStore = (thenStore != null) ? - thenStore.leastUpperBound(s) : s; - if (!newThenStore.equals(thenStore)) { - thenStores.put(b, newThenStore); - if (elseStore != null) { - inputs.put(b, new TransferInput<>(node, this, newThenStore, elseStore)); - addBlockToWorklist = true; - } + boolean shouldWiden = false; + if (blockCount != null) { + Integer count = blockCount.get(b); + if (count == null) { + count = 0; } - break; - } - case ELSE: { - // Update the else store - S newElseStore = (elseStore != null) ? - elseStore.leastUpperBound(s) : s; - if (!newElseStore.equals(elseStore)) { - elseStores.put(b, newElseStore); - if (thenStore != null) { - inputs.put(b, new TransferInput<>(node, this, thenStore, newElseStore)); - addBlockToWorklist = true; - } + shouldWiden = count >= maxCountBeforeWidening; + if (shouldWiden) { + blockCount.put(b, 0); + } else { + blockCount.put(b, count + 1); } - break; } - case BOTH: - if (thenStore == elseStore) { - // Currently there is only one regular store - S newStore = (thenStore != null) ? - thenStore.leastUpperBound(s) : s; - if (!newStore.equals(thenStore)) { - thenStores.put(b, newStore); - elseStores.put(b, newStore); - inputs.put(b, new TransferInput<>(node, this, newStore)); - addBlockToWorklist = true; - } - } else { - boolean storeChanged = false; - S newThenStore = (thenStore != null) ? - thenStore.leastUpperBound(s) : s; - if (!newThenStore.equals(thenStore)) { - thenStores.put(b, newThenStore); - storeChanged = true; + switch (kind) { + case THEN: + { + // Update the then store + S newThenStore = mergeStores(s, thenStore, shouldWiden); + if (!newThenStore.equals(thenStore)) { + thenStores.put(b, newThenStore); + if (elseStore != null) { + inputs.put(b, new TransferInput<>(node, this, newThenStore, elseStore)); + addBlockToWorklist = true; + } + } + break; } - - S newElseStore = (elseStore != null) ? - elseStore.leastUpperBound(s) : s; - if (!newElseStore.equals(elseStore)) { - elseStores.put(b, newElseStore); - storeChanged = true; + case ELSE: + { + // Update the else store + S newElseStore = mergeStores(s, elseStore, shouldWiden); + if (!newElseStore.equals(elseStore)) { + elseStores.put(b, newElseStore); + if (thenStore != null) { + inputs.put(b, new TransferInput<>(node, this, thenStore, newElseStore)); + addBlockToWorklist = true; + } + } + break; } + case BOTH: + if (thenStore == elseStore) { + // Currently there is only one regular store + S newStore = mergeStores(s, thenStore, shouldWiden); + if (!newStore.equals(thenStore)) { + thenStores.put(b, newStore); + elseStores.put(b, newStore); + inputs.put(b, new TransferInput<>(node, this, newStore)); + addBlockToWorklist = true; + } + } else { + boolean storeChanged = false; + + S newThenStore = mergeStores(s, thenStore, shouldWiden); + if (!newThenStore.equals(thenStore)) { + thenStores.put(b, newThenStore); + storeChanged = true; + } + + S newElseStore = mergeStores(s, elseStore, shouldWiden); + if (!newElseStore.equals(elseStore)) { + elseStores.put(b, newElseStore); + storeChanged = true; + } - if (storeChanged) { - inputs.put(b, new TransferInput<>(node, this, newThenStore, newElseStore)); - addBlockToWorklist = true; + if (storeChanged) { + inputs.put(b, new TransferInput<>(node, this, newThenStore, newElseStore)); + addBlockToWorklist = true; + } } - } } if (addBlockToWorklist) { @@ -514,17 +563,26 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } } + private S mergeStores(S newStore, S previousStore, boolean shouldWiden) { + if (previousStore == null) { + return newStore; + } else if (shouldWiden) { + return newStore.widenedUpperBound(previousStore); + } else { + return newStore.leastUpperBound(previousStore); + } + } + /** - * A worklist is a priority queue of blocks in which the order is given - * by depth-first ordering to place non-loop predecessors ahead of successors. + * A worklist is a priority queue of blocks in which the order is given by depth-first ordering + * to place non-loop predecessors ahead of successors. */ protected static class Worklist { /** Map all blocks in the CFG to their depth-first order. */ protected IdentityHashMap<Block, Integer> depthFirstOrder; - /** Comparator to allow priority queue to order blocks by their depth-first - order. */ + /** Comparator to allow priority queue to order blocks by their depth-first order. */ public class DFOComparator implements Comparator<Block> { @Override public int compare(Block b1, Block b2) { @@ -535,7 +593,6 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends /** The backing priority queue. */ protected PriorityQueue<Block> queue; - public Worklist(ControlFlowGraph cfg) { depthFirstOrder = new IdentityHashMap<>(); int count = 1; @@ -569,43 +626,39 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } /** - * Read the {@link TransferInput} for a particular basic block (or {@code null} if - * none exists yet). + * Read the {@link TransferInput} for a particular basic block (or {@code null} if none exists + * yet). */ public /*@Nullable*/ TransferInput<A, S> getInput(Block b) { return getInputBefore(b); } /** - * @return The transfer input corresponding to the location right before the basic - * block <code>b</code>. + * @return the transfer input corresponding to the location right before the basic block {@code + * b}. */ protected /*@Nullable*/ TransferInput<A, S> getInputBefore(Block b) { return inputs.get(b); } - /** - * @return The store corresponding to the location right before the basic - * block <code>b</code>. - */ + /** @return the store corresponding to the location right before the basic block {@code b}. */ protected /*@Nullable*/ S getStoreBefore(Block b, Store.Kind kind) { switch (kind) { - case THEN: - return readFromStore(thenStores, b); - case ELSE: - return readFromStore(elseStores, b); - default: - assert false; - return null; + case THEN: + return readFromStore(thenStores, b); + case ELSE: + return readFromStore(elseStores, b); + default: + assert false; + return null; } } /** - * Read the {@link Store} for a particular basic block from a map of stores - * (or {@code null} if none exists yet). + * Read the {@link Store} for a particular basic block from a map of stores (or {@code null} if + * none exists yet). */ - protected static <S> /*@Nullable*/ S readFromStore(Map<Block, S> stores, - Block b) { + protected static <S> /*@Nullable*/ S readFromStore(Map<Block, S> stores, Block b) { return stores.get(b); } @@ -615,24 +668,24 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } /** - * @return The abstract value for {@link Node} {@code n}, or {@code null} if - * no information is available. Note that if the analysis has not - * finished yet, this value might not represent the final value for - * this node. + * @return the abstract value for {@link Node} {@code n}, or {@code null} if no information is + * available. Note that if the analysis has not finished yet, this value might not represent + * the final value for this node. */ public /*@Nullable*/ A getValue(Node n) { if (isRunning) { // we do not yet have a org.checkerframework.dataflow fact about the current node - if (currentNode == n + if (currentNode == null + || currentNode == n || (currentTree != null && currentTree == n.getTree())) { return null; } // check that 'n' is a subnode of 'node'. Check immediate operands // first for efficiency. - assert currentNode != null; assert !n.isLValue() : "Did not expect an lvalue, but got " + n; - if (!(currentNode != n && (currentNode.getOperands().contains(n) || currentNode - .getTransitiveOperands().contains(n)))) { + if (!(currentNode != n + && (currentNode.getOperands().contains(n) + || currentNode.getTransitiveOperands().contains(n)))) { return null; } return nodeValues.get(n); @@ -641,10 +694,9 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } /** - * @return The abstract value for {@link Tree} {@code t}, or {@code null} if - * no information is available. Note that if the analysis has not - * finished yet, this value might not represent the final value for - * this node. + * @return the abstract value for {@link Tree} {@code t}, or {@code null} if no information is + * available. Note that if the analysis has not finished yet, this value might not represent + * the final value for this node. */ public /*@Nullable*/ A getValue(Tree t) { // we do not yet have a org.checkerframework.dataflow fact about the current node @@ -658,24 +710,22 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends return getValue(nodeCorrespondingToTree); } - /** - * Get the {@link Node} for a given {@link Tree}. - */ + /** Get the {@link Node} for a given {@link Tree}. */ public Node getNodeForTree(Tree t) { return cfg.getNodeCorrespondingToTree(t); } /** - * Get the {@link MethodTree} of the current CFG if the argument {@link Tree} maps - * to a {@link Node} in the CFG or null otherwise. + * Get the {@link MethodTree} of the current CFG if the argument {@link Tree} maps to a {@link + * Node} in the CFG or null otherwise. */ public /*@Nullable*/ MethodTree getContainingMethod(Tree t) { return cfg.getContainingMethod(t); } /** - * Get the {@link ClassTree} of the current CFG if the argument {@link Tree} maps - * to a {@link Node} in the CFG or null otherwise. + * Get the {@link ClassTree} of the current CFG if the argument {@link Tree} maps to a {@link + * Node} in the CFG or null otherwise. */ public /*@Nullable*/ ClassTree getContainingClass(Tree t) { return cfg.getContainingClass(t); @@ -684,8 +734,7 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends public List<Pair<ReturnNode, TransferResult<A, S>>> getReturnStatementStores() { List<Pair<ReturnNode, TransferResult<A, S>>> result = new ArrayList<>(); for (ReturnNode returnNode : cfg.getReturnNodes()) { - TransferResult<A, S> store = storesAtReturnStatements - .get(returnNode); + TransferResult<A, S> store = storesAtReturnStatements.get(returnNode); result.add(Pair.of(returnNode, store)); } return result; @@ -698,9 +747,8 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } /** - * @return The regular exit store, or {@code null}, if there is no such - * store (because the method cannot exit through the regular exit - * block). + * @return the regular exit store, or {@code null}, if there is no such store (because the + * method cannot exit through the regular exit block). */ public /*@Nullable*/ S getRegularExitStore() { SpecialBlock regularExitBlock = cfg.getRegularExitBlock(); @@ -713,8 +761,7 @@ public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends } public S getExceptionalExitStore() { - S exceptionalExitStore = inputs.get(cfg.getExceptionalExitBlock()) - .getRegularStore(); + S exceptionalExitStore = inputs.get(cfg.getExceptionalExitBlock()).getRegularStore(); return exceptionalExitStore; } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AnalysisResult.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AnalysisResult.java index 42b8d2f2ba..48f376dbea 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AnalysisResult.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AnalysisResult.java @@ -4,29 +4,24 @@ package org.checkerframework.dataflow.analysis; import org.checkerframework.checker.nullness.qual.Nullable; */ -import org.checkerframework.dataflow.cfg.block.Block; -import org.checkerframework.dataflow.cfg.block.ExceptionBlock; -import org.checkerframework.dataflow.cfg.block.RegularBlock; -import org.checkerframework.dataflow.cfg.node.Node; - +import com.sun.source.tree.Tree; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.Map.Entry; - import javax.lang.model.element.Element; - -import com.sun.source.tree.Tree; +import org.checkerframework.dataflow.cfg.block.Block; +import org.checkerframework.dataflow.cfg.block.ExceptionBlock; +import org.checkerframework.dataflow.cfg.block.RegularBlock; +import org.checkerframework.dataflow.cfg.node.Node; /** * An {@link AnalysisResult} represents the result of a org.checkerframework.dataflow analysis by - * providing the abstract values given a node or a tree. Note that it does not - * keep track of custom results computed by some analysis. + * providing the abstract values given a node or a tree. Note that it does not keep track of custom + * results computed by some analysis. * * @author Stefan Heule - * - * @param <A> - * type of the abstract value that is tracked. + * @param <A> type of the abstract value that is tracked */ public class AnalysisResult<A extends AbstractValue<A>, S extends Store<S>> { @@ -39,26 +34,22 @@ public class AnalysisResult<A extends AbstractValue<A>, S extends Store<S>> { /** Map from (effectively final) local variable elements to their abstract value. */ protected final HashMap<Element, A> finalLocalValues; - /** - * The stores before every method call. - */ + /** The stores before every method call. */ protected final IdentityHashMap<Block, TransferInput<A, S>> stores; - /** - * Initialize with a given node-value mapping. - */ - public AnalysisResult(Map<Node, A> nodeValues, + /** Initialize with a given node-value mapping. */ + public AnalysisResult( + Map<Node, A> nodeValues, IdentityHashMap<Block, TransferInput<A, S>> stores, - IdentityHashMap<Tree, Node> treeLookup, HashMap<Element, A> finalLocalValues) { + IdentityHashMap<Tree, Node> treeLookup, + HashMap<Element, A> finalLocalValues) { this.nodeValues = new IdentityHashMap<>(nodeValues); this.treeLookup = new IdentityHashMap<>(treeLookup); this.stores = stores; this.finalLocalValues = finalLocalValues; } - /** - * Initialize empty result. - */ + /** Initialize empty result. */ public AnalysisResult() { nodeValues = new IdentityHashMap<>(); treeLookup = new IdentityHashMap<>(); @@ -66,9 +57,7 @@ public class AnalysisResult<A extends AbstractValue<A>, S extends Store<S>> { finalLocalValues = new HashMap<>(); } - /** - * Combine with another analysis result. - */ + /** Combine with another analysis result. */ public void combine(AnalysisResult<A, S> other) { for (Entry<Node, A> e : other.nodeValues.entrySet()) { nodeValues.put(e.getKey(), e.getValue()); @@ -84,68 +73,68 @@ public class AnalysisResult<A extends AbstractValue<A>, S extends Store<S>> { } } - /** - * @return The value of effectively final local variables. - */ + /** @return the value of effectively final local variables */ public HashMap<Element, A> getFinalLocalValues() { return finalLocalValues; } /** - * @return The abstract value for {@link Node} {@code n}, or {@code null} if - * no information is available. + * @return the abstract value for {@link Node} {@code n}, or {@code null} if no information is + * available. */ public /*@Nullable*/ A getValue(Node n) { return nodeValues.get(n); } /** - * @return The abstract value for {@link Tree} {@code t}, or {@code null} if - * no information is available. + * @return the abstract value for {@link Tree} {@code t}, or {@code null} if no information is + * available. */ public /*@Nullable*/ A getValue(Tree t) { A val = getValue(treeLookup.get(t)); return val; } - /** - * @return The {@link Node} for a given {@link Tree}. - */ + /** @return the {@link Node} for a given {@link Tree}. */ public /*@Nullable*/ Node getNodeForTree(Tree tree) { return treeLookup.get(tree); } - /** - * @return The store immediately before a given {@link Tree}. - */ + /** @return the store immediately before a given {@link Tree}. */ public S getStoreBefore(Tree tree) { Node node = getNodeForTree(tree); if (node == null) { return null; } + return getStoreBefore(node); + } + + /** @return the store immediately before a given {@link Node}. */ + public S getStoreBefore(Node node) { return runAnalysisFor(node, true); } - /** - * @return The store immediately after a given {@link Tree}. - */ + /** @return the store immediately after a given {@link Tree}. */ public S getStoreAfter(Tree tree) { Node node = getNodeForTree(tree); if (node == null) { return null; } + return getStoreAfter(node); + } + + /** @return the store immediately after a given {@link Node}. */ + public S getStoreAfter(Node node) { return runAnalysisFor(node, false); } /** - * Runs the analysis again within the block of {@code node} and returns the - * store at the location of {@code node}. If {@code before} is true, then - * the store immediately before the {@link Node} {@code node} is returned. - * Otherwise, the store after {@code node} is returned. + * Runs the analysis again within the block of {@code node} and returns the store at the + * location of {@code node}. If {@code before} is true, then the store immediately before the + * {@link Node} {@code node} is returned. Otherwise, the store after {@code node} is returned. * - * <p> - * If the given {@link Node} cannot be reached (in the control flow graph), - * then {@code null} is returned. + * <p>If the given {@link Node} cannot be reached (in the control flow graph), then {@code null} + * is returned. */ protected S runAnalysisFor(Node node, boolean before) { Block block = node.getBlock(); @@ -157,10 +146,9 @@ public class AnalysisResult<A extends AbstractValue<A>, S extends Store<S>> { } /** - * Runs the analysis again within the block of {@code node} and returns the - * store at the location of {@code node}. If {@code before} is true, then - * the store immediately before the {@link Node} {@code node} is returned. - * Otherwise, the store after {@code node} is returned. + * Runs the analysis again within the block of {@code node} and returns the store at the + * location of {@code node}. If {@code before} is true, then the store immediately before the + * {@link Node} {@code node} is returned. Otherwise, the store after {@code node} is returned. */ public static <A extends AbstractValue<A>, S extends Store<S>> S runAnalysisFor( Node node, boolean before, TransferInput<A, S> transferInput) { @@ -176,49 +164,51 @@ public class AnalysisResult<A extends AbstractValue<A>, S extends Store<S>> { analysis.isRunning = true; try { switch (block.getType()) { - case REGULAR_BLOCK: { - RegularBlock rb = (RegularBlock) block; - - // Apply transfer function to contents until we found the node - // we - // are looking for. - TransferInput<A, S> store = transferInput; - TransferResult<A, S> transferResult = null; - for (Node n : rb.getContents()) { - analysis.currentNode = n; - if (n == node && before) { - return store.getRegularStore(); + case REGULAR_BLOCK: + { + RegularBlock rb = (RegularBlock) block; + + // Apply transfer function to contents until we found the node + // we + // are looking for. + TransferInput<A, S> store = transferInput; + TransferResult<A, S> transferResult = null; + for (Node n : rb.getContents()) { + analysis.currentNode = n; + if (n == node && before) { + return store.getRegularStore(); + } + transferResult = analysis.callTransferFunction(n, store); + if (n == node) { + return transferResult.getRegularStore(); + } + store = new TransferInput<>(n, analysis, transferResult); + } + // This point should never be reached. If the block of 'node' is + // 'block', then 'node' must be part of the contents of 'block'. + assert false; + return null; } - transferResult = analysis.callTransferFunction(n, store); - if (n == node) { + + case EXCEPTION_BLOCK: + { + ExceptionBlock eb = (ExceptionBlock) block; + + // apply transfer function to content + assert eb.getNode() == node; + if (before) { + return transferInput.getRegularStore(); + } + analysis.currentNode = node; + TransferResult<A, S> transferResult = + analysis.callTransferFunction(node, transferInput); return transferResult.getRegularStore(); } - store = new TransferInput<>(n, analysis, transferResult); - } - // This point should never be reached. If the block of 'node' is - // 'block', then 'node' must be part of the contents of 'block'. - assert false; - return null; - } - - case EXCEPTION_BLOCK: { - ExceptionBlock eb = (ExceptionBlock) block; - - // apply transfer function to content - assert eb.getNode() == node; - if (before) { - return transferInput.getRegularStore(); - } - analysis.currentNode = node; - TransferResult<A, S> transferResult = analysis - .callTransferFunction(node, transferInput); - return transferResult.getRegularStore(); - } - default: - // Only regular blocks and exceptional blocks can hold nodes. - assert false; - break; + default: + // Only regular blocks and exceptional blocks can hold nodes. + assert false; + break; } return null; diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/ConditionalTransferResult.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/ConditionalTransferResult.java index c49357a3ff..99a8a525ec 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/ConditionalTransferResult.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/ConditionalTransferResult.java @@ -1,19 +1,15 @@ package org.checkerframework.dataflow.analysis; import java.util.Map; - import javax.lang.model.type.TypeMirror; /** - * Implementation of a {@link TransferResult} with two non-exceptional store; - * one for the 'then' edge and one for 'else'. The result of - * {@code getRegularStore} will be the least upper bound of the two underlying - * stores. + * Implementation of a {@link TransferResult} with two non-exceptional store; one for the 'then' + * edge and one for 'else'. The result of {@code getRegularStore} will be the least upper bound of + * the two underlying stores. * * @author Stefan Heule - * - * @param <S> - * The {@link Store} used to keep track of intermediate results. + * @param <S> the {@link Store} used to keep track of intermediate results */ public class ConditionalTransferResult<A extends AbstractValue<A>, S extends Store<S>> extends TransferResult<A, S> { @@ -27,25 +23,21 @@ public class ConditionalTransferResult<A extends AbstractValue<A>, S extends Sto protected S elseStore; /** - * Create a {@code ConditionalTransferResult} with {@code thenStore} as the - * resulting store if the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} evaluates to - * {@code true} and {@code elseStore} otherwise. - * - * For the meaning of storeChanged, see - * {@link org.checkerframework.dataflow.analysis.TransferResult#storeChanged}. + * Create a {@code ConditionalTransferResult} with {@code thenStore} as the resulting store if + * the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} evaluates to {@code + * true} and {@code elseStore} otherwise. * - * <p> + * <p>For the meaning of storeChanged, see {@link + * org.checkerframework.dataflow.analysis.TransferResult#storeChanged}. * - * <em>Exceptions</em>: If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} throws an - * exception, then it is assumed that no special handling is necessary and - * the store before the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed along any - * exceptional edge. + * <p><em>Exceptions</em>: If the corresponding {@link + * org.checkerframework.dataflow.cfg.node.Node} throws an exception, then it is assumed that no + * special handling is necessary and the store before the corresponding {@link + * org.checkerframework.dataflow.cfg.node.Node} will be passed along any exceptional edge. * - * <p> - * - * <em>Aliasing</em>: {@code thenStore} and {@code elseStore} are not - * allowed to be used anywhere outside of this class (including use through - * aliases). Complete control over the objects is transfered to this class. + * <p><em>Aliasing</em>: {@code thenStore} and {@code elseStore} are not allowed to be used + * anywhere outside of this class (including use through aliases). Complete control over the + * objects is transfered to this class. */ public ConditionalTransferResult(A value, S thenStore, S elseStore, boolean storeChanged) { super(value); @@ -59,27 +51,27 @@ public class ConditionalTransferResult<A extends AbstractValue<A>, S extends Sto } /** - * Create a {@code ConditionalTransferResult} with {@code thenStore} as the - * resulting store if the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} evaluates to - * {@code true} and {@code elseStore} otherwise. - * - * <p> + * Create a {@code ConditionalTransferResult} with {@code thenStore} as the resulting store if + * the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} evaluates to {@code + * true} and {@code elseStore} otherwise. * - * <em>Exceptions</em>: If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} throws an - * exception, then the corresponding store in {@code exceptionalStores} is - * used. If no exception is found in {@code exceptionalStores}, then it is - * assumed that no special handling is necessary and the store before the - * corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed along any exceptional edge. + * <p><em>Exceptions</em>: If the corresponding {@link + * org.checkerframework.dataflow.cfg.node.Node} throws an exception, then the corresponding + * store in {@code exceptionalStores} is used. If no exception is found in {@code + * exceptionalStores}, then it is assumed that no special handling is necessary and the store + * before the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed + * along any exceptional edge. * - * <p> - * - * <em>Aliasing</em>: {@code thenStore}, {@code elseStore}, and any store in - * {@code exceptionalStores} are not allowed to be used anywhere outside of - * this class (including use through aliases). Complete control over the - * objects is transfered to this class. + * <p><em>Aliasing</em>: {@code thenStore}, {@code elseStore}, and any store in {@code + * exceptionalStores} are not allowed to be used anywhere outside of this class (including use + * through aliases). Complete control over the objects is transfered to this class. */ - public ConditionalTransferResult(A value, S thenStore, S elseStore, - Map<TypeMirror, S> exceptionalStores, boolean storeChanged) { + public ConditionalTransferResult( + A value, + S thenStore, + S elseStore, + Map<TypeMirror, S> exceptionalStores, + boolean storeChanged) { super(value); this.exceptionalStores = exceptionalStores; this.thenStore = thenStore; @@ -87,8 +79,8 @@ public class ConditionalTransferResult<A extends AbstractValue<A>, S extends Sto this.storeChanged = storeChanged; } - public ConditionalTransferResult(A value, S thenStore, S elseStore, - Map<TypeMirror, S> exceptionalStores) { + public ConditionalTransferResult( + A value, S thenStore, S elseStore, Map<TypeMirror, S> exceptionalStores) { this(value, thenStore, elseStore, exceptionalStores, false); } @@ -126,12 +118,9 @@ public class ConditionalTransferResult<A extends AbstractValue<A>, S extends Sto return result.toString(); } - /** - * @see org.checkerframework.dataflow.analysis.TransferResult#storeChanged() - */ + /** @see org.checkerframework.dataflow.analysis.TransferResult#storeChanged() */ @Override public boolean storeChanged() { - return storeChanged; + return storeChanged; } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java index 54828c7d31..85ad7b4b00 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java @@ -1,6 +1,25 @@ package org.checkerframework.dataflow.analysis; +import com.sun.source.tree.ArrayAccessTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.LiteralTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; import org.checkerframework.dataflow.cfg.node.ArrayAccessNode; +import org.checkerframework.dataflow.cfg.node.ArrayCreationNode; import org.checkerframework.dataflow.cfg.node.ClassNameNode; import org.checkerframework.dataflow.cfg.node.ExplicitThisLiteralNode; import org.checkerframework.dataflow.cfg.node.FieldAccessNode; @@ -15,41 +34,33 @@ import org.checkerframework.dataflow.cfg.node.ValueLiteralNode; import org.checkerframework.dataflow.cfg.node.WideningConversionNode; import org.checkerframework.dataflow.util.HashCodeUtils; import org.checkerframework.dataflow.util.PurityUtils; - import org.checkerframework.javacutil.AnnotationProvider; import org.checkerframework.javacutil.ElementUtils; +import org.checkerframework.javacutil.ErrorReporter; +import org.checkerframework.javacutil.InternalUtils; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TypeAnnotationUtils; import org.checkerframework.javacutil.TypesUtils; -import java.util.ArrayList; -import java.util.List; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; - /** - * Collection of classes and helper functions to represent Java expressions - * about which the org.checkerframework.dataflow analysis can possibly infer facts. Expressions - * include: + * Collection of classes and helper functions to represent Java expressions about which the + * org.checkerframework.dataflow analysis can possibly infer facts. Expressions include: + * * <ul> - * <li>Field accesses (e.g., <em>o.f</em>)</li> - * <li>Local variables (e.g., <em>l</em>)</li> - * <li>This reference (e.g., <em>this</em>)</li> - * <li>Pure method calls (e.g., <em>o.m()</em>)</li> - * <li>Unknown other expressions to mark that something else was present.</li> + * <li>Field accesses (e.g., <em>o.f</em>) + * <li>Local variables (e.g., <em>l</em>) + * <li>This reference (e.g., <em>this</em>) + * <li>Pure method calls (e.g., <em>o.m()</em>) + * <li>Unknown other expressions to mark that something else was present. * </ul> * * @author Stefan Heule - * */ public class FlowExpressions { /** - * @return The internal representation (as {@link FieldAccess}) of a - * {@link FieldAccessNode}. Can contain {@link Unknown} as receiver. + * @return the internal representation (as {@link FieldAccess}) of a {@link FieldAccessNode}. + * Can contain {@link Unknown} as receiver. */ public static FieldAccess internalReprOfFieldAccess( AnnotationProvider provider, FieldAccessNode node) { @@ -64,8 +75,8 @@ public class FlowExpressions { } /** - * @return The internal representation (as {@link FieldAccess}) of a - * {@link FieldAccessNode}. Can contain {@link Unknown} as receiver. + * @return the internal representation (as {@link FieldAccess}) of a {@link FieldAccessNode}. + * Can contain {@link Unknown} as receiver. */ public static ArrayAccess internalReprOfArrayAccess( AnnotationProvider provider, ArrayAccessNode node) { @@ -75,26 +86,25 @@ public class FlowExpressions { } /** - * We ignore operations such as widening and - * narrowing when computing the internal representation. + * We ignore operations such as widening and narrowing when computing the internal + * representation. * - * @return The internal representation (as {@link Receiver}) of any - * {@link Node}. Might contain {@link Unknown}. + * @return the internal representation (as {@link Receiver}) of any {@link Node}. Might contain + * {@link Unknown}. */ - public static Receiver internalReprOf(AnnotationProvider provider, - Node receiverNode) { + public static Receiver internalReprOf(AnnotationProvider provider, Node receiverNode) { return internalReprOf(provider, receiverNode, false); } /** - * We ignore operations such as widening and - * narrowing when computing the internal representation. + * We ignore operations such as widening and narrowing when computing the internal + * representation. * - * @return The internal representation (as {@link Receiver}) of any - * {@link Node}. Might contain {@link Unknown}. + * @return the internal representation (as {@link Receiver}) of any {@link Node}. Might contain + * {@link Unknown}. */ - public static Receiver internalReprOf(AnnotationProvider provider, - Node receiverNode, boolean allowNonDeterminitic) { + public static Receiver internalReprOf( + AnnotationProvider provider, Node receiverNode, boolean allowNonDeterministic) { Receiver receiver = null; if (receiverNode instanceof FieldAccessNode) { FieldAccessNode fan = (FieldAccessNode) receiverNode; @@ -103,6 +113,12 @@ public class FlowExpressions { // For some reason, "className.this" is considered a field access. // We right this wrong here. receiver = new ThisReference(fan.getReceiver().getType()); + } else if (fan.getFieldName().equals("class")) { + // "className.class" is considered a field access. This makes sense, + // since .class is similar to a field access which is the equivalent + // of a call to getClass(). However for the purposes of dataflow + // analysis, and value stores, this is the equivalent of a ClassNameNode. + receiver = new ClassName(fan.getReceiver().getType()); } else { receiver = internalReprOfFieldAccess(provider, fan); } @@ -120,26 +136,25 @@ public class FlowExpressions { receiver = internalReprOfArrayAccess(provider, a); } else if (receiverNode instanceof StringConversionNode) { // ignore string conversion - return internalReprOf(provider, - ((StringConversionNode) receiverNode).getOperand()); + return internalReprOf(provider, ((StringConversionNode) receiverNode).getOperand()); } else if (receiverNode instanceof WideningConversionNode) { // ignore widening - return internalReprOf(provider, - ((WideningConversionNode) receiverNode).getOperand()); + return internalReprOf(provider, ((WideningConversionNode) receiverNode).getOperand()); } else if (receiverNode instanceof NarrowingConversionNode) { // ignore narrowing - return internalReprOf(provider, - ((NarrowingConversionNode) receiverNode).getOperand()); + return internalReprOf(provider, ((NarrowingConversionNode) receiverNode).getOperand()); } else if (receiverNode instanceof ClassNameNode) { ClassNameNode cn = (ClassNameNode) receiverNode; receiver = new ClassName(cn.getType()); } else if (receiverNode instanceof ValueLiteralNode) { ValueLiteralNode vn = (ValueLiteralNode) receiverNode; receiver = new ValueLiteral(vn.getType(), vn); + } else if (receiverNode instanceof ArrayCreationNode) { + ArrayCreationNode an = (ArrayCreationNode) receiverNode; + receiver = new ArrayCreation(an.getType(), an.getDimensions(), an.getInitializers()); } else if (receiverNode instanceof MethodInvocationNode) { MethodInvocationNode mn = (MethodInvocationNode) receiverNode; - ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn - .getTree()); + ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn.getTree()); // check if this represents a boxing operation of a constant, in which // case we treat the method call as deterministic, because there is no way @@ -153,21 +168,20 @@ public class FlowExpressions { } } - if (PurityUtils.isDeterministic(provider, invokedMethod) || allowNonDeterminitic || considerDeterministic) { + if (PurityUtils.isDeterministic(provider, invokedMethod) + || allowNonDeterministic + || considerDeterministic) { List<Receiver> parameters = new ArrayList<>(); for (Node p : mn.getArguments()) { parameters.add(internalReprOf(provider, p)); } Receiver methodReceiver; if (ElementUtils.isStatic(invokedMethod)) { - methodReceiver = new ClassName(mn.getTarget().getReceiver() - .getType()); + methodReceiver = new ClassName(mn.getTarget().getReceiver().getType()); } else { - methodReceiver = internalReprOf(provider, mn.getTarget() - .getReceiver()); + methodReceiver = internalReprOf(provider, mn.getTarget().getReceiver()); } - receiver = new PureMethodCall(mn.getType(), invokedMethod, - methodReceiver, parameters); + receiver = new MethodCall(mn.getType(), invokedMethod, methodReceiver, parameters); } } @@ -177,7 +191,209 @@ public class FlowExpressions { return receiver; } - public static abstract class Receiver { + /** + * @return the internal representation (as {@link Receiver}) of any {@link ExpressionTree}. + * Might contain {@link Unknown}. + */ + public static Receiver internalReprOf( + AnnotationProvider provider, ExpressionTree receiverTree) { + return internalReprOf(provider, receiverTree, true); + } + /** + * We ignore operations such as widening and narrowing when computing the internal + * representation. + * + * @return the internal representation (as {@link Receiver}) of any {@link ExpressionTree}. + * Might contain {@link Unknown}. + */ + public static Receiver internalReprOf( + AnnotationProvider provider, + ExpressionTree receiverTree, + boolean allowNonDeterministic) { + Receiver receiver; + switch (receiverTree.getKind()) { + case ARRAY_ACCESS: + ArrayAccessTree a = (ArrayAccessTree) receiverTree; + Receiver arrayAccessExpression = internalReprOf(provider, a.getExpression()); + Receiver index = internalReprOf(provider, a.getIndex()); + receiver = new ArrayAccess(InternalUtils.typeOf(a), arrayAccessExpression, index); + break; + case BOOLEAN_LITERAL: + case CHAR_LITERAL: + case DOUBLE_LITERAL: + case FLOAT_LITERAL: + case INT_LITERAL: + case LONG_LITERAL: + case NULL_LITERAL: + case STRING_LITERAL: + LiteralTree vn = (LiteralTree) receiverTree; + receiver = new ValueLiteral(InternalUtils.typeOf(receiverTree), vn.getValue()); + break; + case NEW_ARRAY: + receiver = + new ArrayCreation( + InternalUtils.typeOf(receiverTree), + Collections.<Node>emptyList(), + Collections.<Node>emptyList()); + break; + case METHOD_INVOCATION: + MethodInvocationTree mn = (MethodInvocationTree) receiverTree; + ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn); + if (PurityUtils.isDeterministic(provider, invokedMethod) || allowNonDeterministic) { + List<Receiver> parameters = new ArrayList<>(); + for (ExpressionTree p : mn.getArguments()) { + parameters.add(internalReprOf(provider, p)); + } + Receiver methodReceiver; + if (ElementUtils.isStatic(invokedMethod)) { + methodReceiver = new ClassName(InternalUtils.typeOf(mn.getMethodSelect())); + } else { + methodReceiver = internalReprOf(provider, mn.getMethodSelect()); + } + TypeMirror type = InternalUtils.typeOf(mn); + receiver = new MethodCall(type, invokedMethod, methodReceiver, parameters); + } else { + receiver = null; + } + break; + case MEMBER_SELECT: + receiver = internalRepOfMemberSelect(provider, (MemberSelectTree) receiverTree); + break; + case IDENTIFIER: + IdentifierTree identifierTree = (IdentifierTree) receiverTree; + TypeMirror typeOfId = InternalUtils.typeOf(identifierTree); + if (identifierTree.getName().contentEquals("this") + || identifierTree.getName().contentEquals("super")) { + receiver = new ThisReference(typeOfId); + break; + } + Element ele = TreeUtils.elementFromUse(identifierTree); + switch (ele.getKind()) { + case LOCAL_VARIABLE: + case RESOURCE_VARIABLE: + case EXCEPTION_PARAMETER: + case PARAMETER: + receiver = new LocalVariable(ele); + break; + case FIELD: + // Implicit access expression, such as "this" or a class name + Receiver fieldAccessExpression; + TypeMirror enclosingType = ElementUtils.enclosingClass(ele).asType(); + if (ElementUtils.isStatic(ele)) { + fieldAccessExpression = new ClassName(enclosingType); + } else { + fieldAccessExpression = new ThisReference(enclosingType); + } + receiver = + new FieldAccess( + fieldAccessExpression, typeOfId, (VariableElement) ele); + break; + case CLASS: + case ENUM: + case ANNOTATION_TYPE: + case INTERFACE: + receiver = new ClassName(ele.asType()); + break; + default: + receiver = null; + } + break; + default: + receiver = null; + } + + if (receiver == null) { + receiver = new Unknown(InternalUtils.typeOf(receiverTree)); + } + return receiver; + } + + /** + * Returns the implicit receiver of ele. + * + * <p>Returns either a new ClassName or a new ThisReference depending on whether ele is static + * or not. The passed element must be a field, method, or class. + * + * @param ele field, method, or class + * @return either a new ClassName or a new ThisReference depending on whether ele is static or + * not + */ + public static Receiver internalRepOfImplicitReceiver(Element ele) { + TypeMirror enclosingType = ElementUtils.enclosingClass(ele).asType(); + if (ElementUtils.isStatic(ele)) { + return new ClassName(enclosingType); + } else { + return new ThisReference(enclosingType); + } + } + + /** + * Returns either a new ClassName or ThisReference Receiver object for the enclosingType + * + * <p>The Tree should be an expression or a statement that does not have a receiver or an + * implicit receiver. For example, a local variable declaration. + * + * @param path TreePath to tree + * @param enclosingType type of the enclosing type + * @return a new ClassName or ThisReference that is a Receiver object for the enclosingType + */ + public static Receiver internalRepOfPseudoReceiver(TreePath path, TypeMirror enclosingType) { + if (TreeUtils.isTreeInStaticScope(path)) { + return new ClassName(enclosingType); + } else { + return new ThisReference(enclosingType); + } + } + + private static Receiver internalRepOfMemberSelect( + AnnotationProvider provider, MemberSelectTree memberSelectTree) { + TypeMirror expressionType = InternalUtils.typeOf(memberSelectTree.getExpression()); + if (TreeUtils.isClassLiteral(memberSelectTree)) { + return new ClassName(expressionType); + } + Element ele = TreeUtils.elementFromUse(memberSelectTree); + switch (ele.getKind()) { + case METHOD: + case CONSTRUCTOR: + return internalReprOf(provider, memberSelectTree.getExpression()); + case CLASS: // o instanceof MyClass.InnerClass + case ENUM: + case INTERFACE: // o instanceof MyClass.InnerInterface + case ANNOTATION_TYPE: + return new ClassName(expressionType); + case ENUM_CONSTANT: + case FIELD: + Receiver r = internalReprOf(provider, memberSelectTree.getExpression()); + return new FieldAccess(r, ElementUtils.getType(ele), (VariableElement) ele); + default: + ErrorReporter.errorAbort( + "Unexpected element kind: %s element: %s", ele.getKind(), ele); + return null; + } + } + + /** + * Returns Receiver objects for the formal parameters of the method in which path is enclosed. + * + * @param annotationProvider annotationProvider + * @param path TreePath that is enclosed by the method + * @return list of Receiver objects for the formal parameters of the method in which path is + * enclosed + */ + public static List<Receiver> getParametersOfEnclosingMethod( + AnnotationProvider annotationProvider, TreePath path) { + MethodTree methodTree = TreeUtils.enclosingMethod(path); + if (methodTree == null) { + return null; + } + List<Receiver> internalArguments = new ArrayList<>(); + for (VariableTree arg : methodTree.getParameters()) { + internalArguments.add(internalReprOf(annotationProvider, new LocalVariableNode(arg))); + } + return internalArguments; + } + + public abstract static class Receiver { protected final TypeMirror type; public Receiver(TypeMirror type) { @@ -196,38 +412,32 @@ public class FlowExpressions { } /** - * Returns true if and only if the value this expression stands for - * cannot be changed by a method call. This is the case for local - * variables, the self reference as well as final field accesses for - * whose receiver {@link #isUnmodifiableByOtherCode} is true. + * Returns true if and only if the value this expression stands for cannot be changed by a + * method call. This is the case for local variables, the self reference as well as final + * field accesses for whose receiver {@link #isUnmodifiableByOtherCode} is true. */ public abstract boolean isUnmodifiableByOtherCode(); - /** - * @return True if and only if the two receiver are syntactically - * identical. - */ + /** @return true if and only if the two receiver are syntactically identical */ public boolean syntacticEquals(Receiver other) { return other == this; } /** - * @return True if and only if this receiver contains a receiver that is - * syntactically equal to {@code other}. + * @return true if and only if this receiver contains a receiver that is syntactically equal + * to {@code other}. */ public boolean containsSyntacticEqualReceiver(Receiver other) { return syntacticEquals(other); } /** - * Returns true if and only if {@code other} appear anywhere in this - * receiver or an expression appears in this receiver such that - * {@code other} might alias this expression, and that expression is - * modifiable. + * Returns true if and only if {@code other} appears anywhere in this receiver or an + * expression appears in this receiver such that {@code other} might alias this expression, + * and that expression is modifiable. * - * <p> - * This is always true, except for cases where the Java type information - * prevents aliasing and none of the subexpressions can alias 'other'. + * <p>This is always true, except for cases where the Java type information prevents + * aliasing and none of the subexpressions can alias 'other'. */ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) { return this.equals(other) || store.canAlias(this, other); @@ -252,8 +462,7 @@ public class FlowExpressions { this.field = node.getElement(); } - public FieldAccess(Receiver receiver, TypeMirror type, - VariableElement fieldElement) { + public FieldAccess(Receiver receiver, TypeMirror type, VariableElement fieldElement) { super(type); this.receiver = receiver; this.field = fieldElement; @@ -273,8 +482,7 @@ public class FlowExpressions { return false; } FieldAccess fa = (FieldAccess) obj; - return fa.getField().equals(getField()) - && fa.getReceiver().equals(getReceiver()); + return fa.getField().equals(getField()) && fa.getReceiver().equals(getReceiver()); } @Override @@ -290,8 +498,7 @@ public class FlowExpressions { @Override public boolean containsSyntacticEqualReceiver(Receiver other) { - return syntacticEquals(other) - || receiver.containsSyntacticEqualReceiver(other); + return syntacticEquals(other) || receiver.containsSyntacticEqualReceiver(other); } @Override @@ -301,13 +508,17 @@ public class FlowExpressions { } FieldAccess fa = (FieldAccess) other; return super.syntacticEquals(other) - || fa.getField().equals(getField()) - && fa.getReceiver().syntacticEquals(getReceiver()); + || (fa.getField().equals(getField()) + && fa.getReceiver().syntacticEquals(getReceiver())); } @Override public String toString() { - return receiver + "." + field; + if (receiver instanceof ClassName) { + return receiver.getType() + "." + field; + } else { + return receiver + "." + field; + } } @Override @@ -363,12 +574,10 @@ public class FlowExpressions { } /** - * A ClassName represents the occurrence of a class as part of a static - * field access or method invocation. + * A ClassName represents the occurrence of a class as part of a static field access or method + * invocation. */ public static class ClassName extends Receiver { - protected Element element; - public ClassName(TypeMirror type) { super(type); } @@ -389,7 +598,7 @@ public class FlowExpressions { @Override public String toString() { - return getType().toString(); + return getType().toString() + ".class"; } @Override @@ -447,7 +656,6 @@ public class FlowExpressions { public boolean isUnmodifiableByOtherCode() { return false; } - } public static class LocalVariable extends Receiver { @@ -469,7 +677,17 @@ public class FlowExpressions { return false; } LocalVariable other = (LocalVariable) obj; - return other.element.equals(element); + VarSymbol vs = (VarSymbol) element; + VarSymbol vsother = (VarSymbol) other.element; + // Use TypeAnnotationUtils.unannotatedType(type).toString().equals(...) instead of Types.isSameType(...) + // because Types requires a processing environment, and FlowExpressions is + // designed to be independent of processing environment. See also + // calls to getType().toString() in FlowExpressions. + return vsother.name.contentEquals(vs.name) + && TypeAnnotationUtils.unannotatedType(vsother.type) + .toString() + .equals(TypeAnnotationUtils.unannotatedType(vs.type).toString()) + && vsother.owner.toString().equals(vs.owner.toString()); } public Element getElement() { @@ -478,7 +696,11 @@ public class FlowExpressions { @Override public int hashCode() { - return HashCodeUtils.hash(element); + VarSymbol vs = (VarSymbol) element; + return HashCodeUtils.hash( + vs.name.toString(), + TypeAnnotationUtils.unannotatedType(vs.type).toString(), + vs.owner.toString()); } @Override @@ -497,7 +719,7 @@ public class FlowExpressions { return false; } LocalVariable l = (LocalVariable) other; - return l.getElement().equals(getElement()); + return l.equals(this); } @Override @@ -542,11 +764,9 @@ public class FlowExpressions { } ValueLiteral other = (ValueLiteral) obj; if (value == null) { - return type.toString().equals(other.type.toString()) - && other.value == null; + return type.toString().equals(other.type.toString()) && other.value == null; } - return type.toString().equals(other.type.toString()) - && value.equals(other.value); + return type.toString().equals(other.type.toString()) && value.equals(other.value); } @Override @@ -573,23 +793,24 @@ public class FlowExpressions { public boolean containsModifiableAliasOf(Store<?> store, Receiver other) { return false; // not modifiable } + + public Object getValue() { + return value; + } } - /** - * A method call, typically a deterministic one. However, this is not - * enforced and non-pure methods are also possible. It is the clients - * responsibility to ensure that using non-deterministic methods is done in - * a sound way. The CF allows non-deterministic methods to be used in - * postconditions such as EnsuresNonNull. - */ - public static class PureMethodCall extends Receiver { + /** A method call. */ + public static class MethodCall extends Receiver { protected final Receiver receiver; protected final List<Receiver> parameters; - protected final Element method; + protected final ExecutableElement method; - public PureMethodCall(TypeMirror type, Element method, - Receiver receiver, List<Receiver> parameters) { + public MethodCall( + TypeMirror type, + ExecutableElement method, + Receiver receiver, + List<Receiver> parameters) { super(type); this.receiver = receiver; this.parameters = parameters; @@ -612,6 +833,24 @@ public class FlowExpressions { return false; } + /** @return the method call receiver (for inspection only - do not modify) */ + public Receiver getReceiver() { + return receiver; + } + + /** + * @return the method call parameters (for inspection only - do not modify any of the + * parameters) + */ + public List<Receiver> getParameters() { + return Collections.unmodifiableList(parameters); + } + + /** @return the ExecutableElement for the method call */ + public ExecutableElement getElement() { + return method; + } + @Override public boolean isUnmodifiableByOtherCode() { return false; @@ -624,10 +863,10 @@ public class FlowExpressions { @Override public boolean syntacticEquals(Receiver other) { - if (!(other instanceof PureMethodCall)) { + if (!(other instanceof MethodCall)) { return false; } - PureMethodCall otherMethod = (PureMethodCall) other; + MethodCall otherMethod = (MethodCall) other; if (!receiver.syntacticEquals(otherMethod.receiver)) { return false; } @@ -668,10 +907,10 @@ public class FlowExpressions { @Override public boolean equals(Object obj) { - if (obj == null || !(obj instanceof PureMethodCall)) { + if (obj == null || !(obj instanceof MethodCall)) { return false; } - PureMethodCall other = (PureMethodCall) obj; + MethodCall other = (MethodCall) obj; int i = 0; for (Receiver p : parameters) { if (!p.equals(other.parameters.get(i))) { @@ -679,8 +918,7 @@ public class FlowExpressions { } i++; } - return receiver.equals(other.receiver) - && method.equals(other.method); + return receiver.equals(other.receiver) && method.equals(other.method); } @Override @@ -695,7 +933,11 @@ public class FlowExpressions { @Override public String toString() { StringBuilder result = new StringBuilder(); - result.append(receiver.toString()); + if (receiver instanceof ClassName) { + result.append(receiver.getType()); + } else { + result.append(receiver); + } result.append("."); String methodName = method.getSimpleName().toString(); result.append(methodName); @@ -713,9 +955,7 @@ public class FlowExpressions { } } - /** - * A deterministic method call. - */ + /** A deterministic method call. */ public static class ArrayAccess extends Receiver { protected final Receiver receiver; @@ -753,7 +993,8 @@ public class FlowExpressions { @Override public boolean containsSyntacticEqualReceiver(Receiver other) { - return syntacticEquals(other) || receiver.syntacticEquals(other) + return syntacticEquals(other) + || receiver.syntacticEquals(other) || index.syntacticEquals(other); } @@ -801,4 +1042,102 @@ public class FlowExpressions { return result.toString(); } } + + public static class ArrayCreation extends Receiver { + + protected List<Node> dimensions; + protected List<Node> initializers; + + public ArrayCreation(TypeMirror type, List<Node> dimensions, List<Node> initializers) { + super(type); + this.dimensions = dimensions; + this.initializers = initializers; + } + + public List<Node> getDimensions() { + return dimensions; + } + + public List<Node> getInitializers() { + return initializers; + } + + @Override + public boolean containsOfClass(Class<? extends Receiver> clazz) { + for (Node n : dimensions) { + if (n.getClass().equals(clazz)) return true; + } + for (Node n : initializers) { + if (n.getClass().equals(clazz)) return true; + } + return false; + } + + @Override + public boolean isUnmodifiableByOtherCode() { + return false; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((dimensions == null) ? 0 : dimensions.hashCode()); + result = prime * result + ((initializers == null) ? 0 : initializers.hashCode()); + result = prime * result + HashCodeUtils.hash(getType().toString()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof ArrayCreation)) { + return false; + } + ArrayCreation other = (ArrayCreation) obj; + return this.dimensions.equals(other.getDimensions()) + && this.initializers.equals(other.getInitializers()) + && getType().toString().equals(other.getType().toString()); + } + + @Override + public boolean syntacticEquals(Receiver other) { + return this.equals(other); + } + + @Override + public boolean containsSyntacticEqualReceiver(Receiver other) { + return syntacticEquals(other); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("new " + type); + if (!dimensions.isEmpty()) { + boolean needComma = false; + sb.append(" ("); + for (Node dim : dimensions) { + if (needComma) { + sb.append(", "); + } + sb.append(dim); + needComma = true; + } + sb.append(")"); + } + if (!initializers.isEmpty()) { + boolean needComma = false; + sb.append(" = {"); + for (Node init : initializers) { + if (needComma) { + sb.append(", "); + } + sb.append(init); + needComma = true; + } + sb.append("}"); + } + return sb.toString(); + } + } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/RegularTransferResult.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/RegularTransferResult.java index 8872e7f808..b4044fae83 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/RegularTransferResult.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/RegularTransferResult.java @@ -1,43 +1,36 @@ package org.checkerframework.dataflow.analysis; import java.util.Map; - import javax.lang.model.type.TypeMirror; /** - * Implementation of a {@link TransferResult} with just one non-exceptional - * store. The result of {@code getThenStore} and {@code getElseStore} is equal - * to the only underlying store. + * Implementation of a {@link TransferResult} with just one non-exceptional store. The result of + * {@code getThenStore} and {@code getElseStore} is equal to the only underlying store. * * @author Stefan Heule - * - * @param <S> - * The {@link Store} used to keep track of intermediate results. + * @param <S> the {@link Store} used to keep track of intermediate results */ public class RegularTransferResult<A extends AbstractValue<A>, S extends Store<S>> extends TransferResult<A, S> { /** The regular result store. */ protected S store; - final private boolean storeChanged; + + private final boolean storeChanged; /** - * Create a {@code TransferResult} with {@code resultStore} as the resulting - * store. If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} is a boolean node, then + * Create a {@code TransferResult} with {@code resultStore} as the resulting store. If the + * corresponding {@link org.checkerframework.dataflow.cfg.node.Node} is a boolean node, then * {@code resultStore} is used for both the 'then' and 'else' edge. * - * <p> - * - * <em>Exceptions</em>: If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} throws an - * exception, then it is assumed that no special handling is necessary and - * the store before the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed along any - * exceptional edge. - * - * <p> + * <p><em>Exceptions</em>: If the corresponding {@link + * org.checkerframework.dataflow.cfg.node.Node} throws an exception, then it is assumed that no + * special handling is necessary and the store before the corresponding {@link + * org.checkerframework.dataflow.cfg.node.Node} will be passed along any exceptional edge. * - * <em>Aliasing</em>: {@code resultStore} is not allowed to be used anywhere - * outside of this class (including use through aliases). Complete control - * over the object is transfered to this class. + * <p><em>Aliasing</em>: {@code resultStore} is not allowed to be used anywhere outside of this + * class (including use through aliases). Complete control over the object is transfered to this + * class. */ public RegularTransferResult(A value, S resultStore, boolean storeChanged) { super(value); @@ -50,38 +43,33 @@ public class RegularTransferResult<A extends AbstractValue<A>, S extends Store<S } /** - * Create a {@code TransferResult} with {@code resultStore} as the resulting - * store. If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} is a boolean node, then + * Create a {@code TransferResult} with {@code resultStore} as the resulting store. If the + * corresponding {@link org.checkerframework.dataflow.cfg.node.Node} is a boolean node, then * {@code resultStore} is used for both the 'then' and 'else' edge. * - * For the meaning of storeChanged, see - * {@link org.checkerframework.dataflow.analysis.TransferResult#storeChanged}. + * <p>For the meaning of storeChanged, see {@link + * org.checkerframework.dataflow.analysis.TransferResult#storeChanged}. * - * <p> + * <p><em>Exceptions</em>: If the corresponding {@link + * org.checkerframework.dataflow.cfg.node.Node} throws an exception, then the corresponding + * store in {@code exceptionalStores} is used. If no exception is found in {@code + * exceptionalStores}, then it is assumed that no special handling is necessary and the store + * before the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed + * along any exceptional edge. * - * <em>Exceptions</em>: If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} throws an - * exception, then the corresponding store in {@code exceptionalStores} is - * used. If no exception is found in {@code exceptionalStores}, then it is - * assumed that no special handling is necessary and the store before the - * corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed along any exceptional edge. - * - * <p> - * - * <em>Aliasing</em>: {@code resultStore} and any store in - * {@code exceptionalStores} are not allowed to be used anywhere outside of - * this class (including use through aliases). Complete control over the - * objects is transfered to this class. + * <p><em>Aliasing</em>: {@code resultStore} and any store in {@code exceptionalStores} are not + * allowed to be used anywhere outside of this class (including use through aliases). Complete + * control over the objects is transfered to this class. */ - public RegularTransferResult(A value, S resultStore, - Map<TypeMirror, S> exceptionalStores, boolean storeChanged) { + public RegularTransferResult( + A value, S resultStore, Map<TypeMirror, S> exceptionalStores, boolean storeChanged) { super(value); this.store = resultStore; this.storeChanged = storeChanged; this.exceptionalStores = exceptionalStores; } - public RegularTransferResult(A value, S resultStore, - Map<TypeMirror, S> exceptionalStores) { + public RegularTransferResult(A value, S resultStore, Map<TypeMirror, S> exceptionalStores) { this(value, resultStore, exceptionalStores, false); } @@ -120,11 +108,9 @@ public class RegularTransferResult<A extends AbstractValue<A>, S extends Store<S return result.toString(); } - /** - * @see org.checkerframework.dataflow.analysis.TransferResult#storeChanged() - */ + /** @see org.checkerframework.dataflow.analysis.TransferResult#storeChanged() */ @Override public boolean storeChanged() { - return storeChanged; + return storeChanged; } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Store.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Store.java index 90c7f20b9c..5d714da889 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Store.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Store.java @@ -1,15 +1,15 @@ package org.checkerframework.dataflow.analysis; +import org.checkerframework.dataflow.cfg.CFGVisualizer; + /** * A store is used to keep track of the information that the org.checkerframework.dataflow analysis * has accumulated at any given point in time. * * @author Stefan Heule - * - * @param <S> - * The type of the store returned by {@code copy} and that is used in - * {@code leastUpperBound}. Usually it is the implementing class - * itself, e.g. in {@code T extends Store<T>}. + * @param <S> the type of the store returned by {@code copy} and that is used in {@code + * leastUpperBound}. Usually it is the implementing class itself, e.g. in {@code T extends + * Store<T>}. */ public interface Store<S extends Store<S>> { @@ -24,47 +24,68 @@ public interface Store<S extends Store<S>> { BOTH } - // A flow rule describes how stores flow along one edge between basic blocks. + /** A flow rule describes how stores flow along one edge between basic blocks. */ public static enum FlowRule { - EACH_TO_EACH, // The normal case, then store flows to the then store - // and else store flows to the else store. - THEN_TO_BOTH, // Then store flows to both then and else of successor. - ELSE_TO_BOTH, // Else store flows to both then and else of successor. - THEN_TO_THEN, // Then store flows to the then of successor. Else store is ignored. - ELSE_TO_ELSE, // Else store flows to the else of successor. Then store is ignored. + EACH_TO_EACH, // The normal case, then store flows to the then store + // and else store flows to the else store. + THEN_TO_BOTH, // Then store flows to both then and else of successor. + ELSE_TO_BOTH, // Else store flows to both then and else of successor. + THEN_TO_THEN, // Then store flows to the then of successor. Else store is ignored. + ELSE_TO_ELSE, // Else store flows to the else of successor. Then store is ignored. } - /** @return An exact copy of this store. */ + /** @return an exact copy of this store. */ S copy(); /** * Compute the least upper bound of two stores. * - * <p> + * <p><em>Important</em>: This method must fulfill the following contract: * - * <em>Important</em>: This method must fulfill the following contract: * <ul> - * <li>Does not change {@code this}.</li> - * <li>Does not change {@code other}.</li> - * <li>Returns a fresh object which is not aliased yet.</li> - * <li>Returns an object of the same (dynamic) type as {@code this}, even if - * the signature is more permissive.</li> - * <li>Is commutative.</li> + * <li>Does not change {@code this}. + * <li>Does not change {@code other}. + * <li>Returns a fresh object which is not aliased yet. + * <li>Returns an object of the same (dynamic) type as {@code this}, even if the signature is + * more permissive. + * <li>Is commutative. * </ul> */ S leastUpperBound(S other); /** - * Can the objects {@code a} and {@code b} be aliases? Returns a - * conservative answer (i.e., returns {@code true} if not enough information - * is available to determine aliasing). + * Compute an upper bound of two stores that is wider than the least upper bound of the two + * stores. Used to jump to a higher abstraction to allow faster termination of the fixed point + * computations in {@link Analysis}. {@code previous} must be the previous store. + * + * <p>A particular analysis might not require widening and should implement this method by + * calling leastUpperBound. + * + * <p><em>Important</em>: This method must fulfill the following contract: + * + * <ul> + * <li>Does not change {@code this}. + * <li>Does not change {@code previous}. + * <li>Returns a fresh object which is not aliased yet. + * <li>Returns an object of the same (dynamic) type as {@code this}, even if the signature is + * more permissive. + * <li>Is commutative. + * </ul> + * + * @param previous must be the previous store */ - boolean canAlias(FlowExpressions.Receiver a, - FlowExpressions.Receiver b); + S widenedUpperBound(S previous); - /** @return Whether the Store supports DOT graph output. */ - boolean hasDOToutput(); + /** + * Can the objects {@code a} and {@code b} be aliases? Returns a conservative answer (i.e., + * returns {@code true} if not enough information is available to determine aliasing). + */ + boolean canAlias(FlowExpressions.Receiver a, FlowExpressions.Receiver b); - /** @return The store encoded as a DOT graph for visualization. */ - String toDOToutput(); + /** + * Delegate visualization responsibility to a visualizer. + * + * @param viz the visualizer to visualize this store + */ + void visualize(CFGVisualizer<?, S, ?> viz); } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferFunction.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferFunction.java index e21ab5646a..a10a766b33 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferFunction.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferFunction.java @@ -4,46 +4,38 @@ package org.checkerframework.dataflow.analysis; import org.checkerframework.checker.nullness.qual.Nullable; */ +import java.util.List; import org.checkerframework.dataflow.cfg.UnderlyingAST; import org.checkerframework.dataflow.cfg.node.LocalVariableNode; import org.checkerframework.dataflow.cfg.node.Node; import org.checkerframework.dataflow.cfg.node.NodeVisitor; -import java.util.List; - /** - * Interface of a transfer function for the abstract interpretation used for the - * flow analysis. + * Interface of a transfer function for the abstract interpretation used for the flow analysis. * - * <p> + * <p>A transfer function consists of the following components: * - * A transfer function consists of the following components: * <ul> - * <li>A method {@code initialStore} that determines which initial store should - * be used in the org.checkerframework.dataflow analysis.</li> - * <li>A function for every {@link Node} type that determines the behavior of - * the org.checkerframework.dataflow analysis in that case. This method takes a {@link Node} and an - * incoming store, and produces a {@link RegularTransferResult}.</li> + * <li>A method {@code initialStore} that determines which initial store should be used in the + * org.checkerframework.dataflow analysis. + * <li>A function for every {@link Node} type that determines the behavior of the + * org.checkerframework.dataflow analysis in that case. This method takes a {@link Node} and + * an incoming store, and produces a {@link RegularTransferResult}. * </ul> * - * <p> - * - * <em>Important</em>: The individual transfer functions ( {@code visit*}) are - * allowed to use (and modify) the stores contained in the argument passed; the - * ownership is transfered from the caller to that function. + * <p><em>Important</em>: The individual transfer functions ( {@code visit*}) are allowed to use + * (and modify) the stores contained in the argument passed; the ownership is transfered from the + * caller to that function. * * @author Stefan Heule - * - * @param <S> - * The {@link Store} used to keep track of intermediate results. + * @param <S> the {@link Store} used to keep track of intermediate results */ public interface TransferFunction<A extends AbstractValue<A>, S extends Store<S>> extends NodeVisitor<TransferResult<A, S>, TransferInput<A, S>> { /** - * @return The initial store to be used by the org.checkerframework.dataflow analysis. - * {@code parameters} is only set if the underlying AST is a method. + * @return the initial store to be used by the org.checkerframework.dataflow analysis. {@code + * parameters} is only set if the underlying AST is a method. */ - S initialStore(UnderlyingAST underlyingAST, - /*@Nullable*/ List<LocalVariableNode> parameters); + S initialStore(UnderlyingAST underlyingAST, /*@Nullable*/ List<LocalVariableNode> parameters); } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferInput.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferInput.java index 7314b35e13..f827ad302d 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferInput.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferInput.java @@ -8,79 +8,65 @@ import org.checkerframework.dataflow.cfg.node.Node; import org.checkerframework.dataflow.util.HashCodeUtils; /** - * {@code TransferInput} is used as the input type of the individual transfer - * functions of a {@link TransferFunction}. It also contains a reference to the - * node for which the transfer function will be applied. + * {@code TransferInput} is used as the input type of the individual transfer functions of a {@link + * TransferFunction}. It also contains a reference to the node for which the transfer function will + * be applied. * - * <p> - * - * A {@code TransferInput} contains one or two stores. If two stores are - * present, one belongs to 'then', and the other to 'else'. + * <p>A {@code TransferInput} contains one or two stores. If two stores are present, one belongs to + * 'then', and the other to 'else'. * * @author Stefan Heule - * - * @param <S> - * The {@link Store} used to keep track of intermediate results. + * @param <S> the {@link Store} used to keep track of intermediate results */ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { - /** - * The corresponding node. - */ + /** The corresponding node. */ protected Node node; /** - * The regular result store (or {@code null} if none is present). The - * following invariant is maintained: + * The regular result store (or {@code null} if none is present). The following invariant is + * maintained: * - * <pre> - * store == null <==> thenStore != null && elseStore != null - * </pre> + * <pre>{@code + * store == null ⇔ thenStore != null && elseStore != null + * }</pre> */ protected final /*@Nullable*/ S store; /** - * The 'then' result store (or {@code null} if none is present). The - * following invariant is maintained: + * The 'then' result store (or {@code null} if none is present). The following invariant is + * maintained: * - * <pre> - * store == null <==> thenStore != null && elseStore != null - * </pre> + * <pre>{@code + * store == null ⇔ thenStore != null && elseStore != null + * }</pre> */ protected final /*@Nullable*/ S thenStore; /** - * The 'else' result store (or {@code null} if none is present). The - * following invariant is maintained: + * The 'else' result store (or {@code null} if none is present). The following invariant is + * maintained: * - * <pre> - * store == null <==> thenStore != null && elseStore != null - * </pre> + * <pre>{@code + * store == null ⇔ thenStore != null && elseStore != null + * }</pre> */ protected final /*@Nullable*/ S elseStore; - /** - * The corresponding analysis class to get intermediate flow results. - */ + /** The corresponding analysis class to get intermediate flow results. */ protected final Analysis<A, S, ?> analysis; /** - * Create a {@link TransferInput}, given a {@link TransferResult} and a - * node-value mapping. - * - * <p> + * Create a {@link TransferInput}, given a {@link TransferResult} and a node-value mapping. * - * <em>Aliasing</em>: The stores returned by any methods of {@code to} will - * be stored internally and are not allowed to be used elsewhere. Full - * control of them is transfered to this object. + * <p><em>Aliasing</em>: The stores returned by any methods of {@code to} will be stored + * internally and are not allowed to be used elsewhere. Full control of them is transfered to + * this object. * - * <p> - * - * The node-value mapping {@code nodeValues} is provided by the analysis and - * is only read from within this {@link TransferInput}. + * <p>The node-value mapping {@code nodeValues} is provided by the analysis and is only read + * from within this {@link TransferInput}. */ - public TransferInput(Node n, Analysis<A, S, ?> analysis, - TransferResult<A, S> to) { + public TransferInput(Node n, Analysis<A, S, ?> analysis, TransferResult<A, S> to) { node = n; this.analysis = analysis; if (to.containsTwoStores()) { @@ -96,16 +82,11 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { /** * Create a {@link TransferInput}, given a store and a node-value mapping. * - * <p> - * - * <em>Aliasing</em>: The store {@code s} will be stored internally and is - * not allowed to be used elsewhere. Full control over {@code s} is - * transfered to this object. + * <p><em>Aliasing</em>: The store {@code s} will be stored internally and is not allowed to be + * used elsewhere. Full control over {@code s} is transfered to this object. * - * <p> - * - * The node-value mapping {@code nodeValues} is provided by the analysis and - * is only read from within this {@link TransferInput}. + * <p>The node-value mapping {@code nodeValues} is provided by the analysis and is only read + * from within this {@link TransferInput}. */ public TransferInput(Node n, Analysis<A, S, ?> analysis, S s) { node = n; @@ -115,14 +96,10 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { } /** - * Create a {@link TransferInput}, given two stores and a node-value - * mapping. - * - * <p> + * Create a {@link TransferInput}, given two stores and a node-value mapping. * - * <em>Aliasing</em>: The two stores {@code s1} and {@code s2} will be - * stored internally and are not allowed to be used elsewhere. Full control - * of them is transfered to this object. + * <p><em>Aliasing</em>: The two stores {@code s1} and {@code s2} will be stored internally and + * are not allowed to be used elsewhere. Full control of them is transfered to this object. */ public TransferInput(Node n, Analysis<A, S, ?> analysis, S s1, S s2) { node = n; @@ -132,9 +109,7 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { store = null; } - /** - * Copy constructor. - */ + /** Copy constructor. */ protected TransferInput(TransferInput<A, S> from) { this.node = from.node; this.analysis = from.analysis; @@ -148,27 +123,24 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { } } - /** - * @return The {@link Node} for this {@link TransferInput}. - */ + /** @return the {@link Node} for this {@link TransferInput}. */ public Node getNode() { return node; } /** - * @return The abstract value of {@link Node} {@code n}, which is required - * to be a 'sub-node' (that is, a direct or indirect child) of the - * node this transfer input is associated with. Furthermore, - * {@code n} cannot be a l-value node. Returns {@code null} if no - * value if available. + * @return the abstract value of {@link Node} {@code n}, which is required to be a 'sub-node' + * (that is, a direct or indirect child) of the node this transfer input is associated with. + * Furthermore, {@code n} cannot be a l-value node. Returns {@code null} if no value if + * available. */ public /*@Nullable*/ A getValueOfSubNode(Node n) { return analysis.getValue(n); } /** - * @return The regular result store produced if no exception is thrown by - * the {@link Node} corresponding to this transfer function result. + * @return the regular result store produced if no exception is thrown by the {@link Node} + * corresponding to this transfer function result */ public S getRegularStore() { if (store == null) { @@ -179,8 +151,8 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { } /** - * @return The result store produced if the {@link Node} this result belongs - * to evaluates to {@code true}. + * @return the result store produced if the {@link Node} this result belongs to evaluates to + * {@code true}. */ public S getThenStore() { if (store == null) { @@ -190,8 +162,8 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { } /** - * @return The result store produced if the {@link Node} this result belongs - * to evaluates to {@code false}. + * @return the result store produced if the {@link Node} this result belongs to evaluates to + * {@code false}. */ public S getElseStore() { if (store == null) { @@ -203,21 +175,19 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { } /** - * @return {@code true} if and only if this transfer input contains two - * stores that are potentially not equal. Note that the result - * {@code true} does not imply that {@code getRegularStore} cannot - * be called (or vice versa for {@code false}). Rather, it indicates - * that {@code getThenStore} or {@code getElseStore} can be used to - * give more precise results. Otherwise, if the result is - * {@code false}, then all three methods {@code getRegularStore}, - * {@code getThenStore}, and {@code getElseStore} return equivalent - * stores. + * @return {@code true} if and only if this transfer input contains two stores that are + * potentially not equal. Note that the result {@code true} does not imply that {@code + * getRegularStore} cannot be called (or vice versa for {@code false}). Rather, it indicates + * that {@code getThenStore} or {@code getElseStore} can be used to give more precise + * results. Otherwise, if the result is {@code false}, then all three methods {@code + * getRegularStore}, {@code getThenStore}, and {@code getElseStore} return equivalent + * stores. */ public boolean containsTwoStores() { return (thenStore != null && elseStore != null); } - /** @return An exact copy of this store. */ + /** @return an exact copy of this store. */ public TransferInput<A, S> copy() { return new TransferInput<>(this); } @@ -225,25 +195,22 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { /** * Compute the least upper bound of two stores. * - * <p> - * - * <em>Important</em>: This method must fulfill the same contract as - * {@code leastUpperBound} of {@link Store}. + * <p><em>Important</em>: This method must fulfill the same contract as {@code leastUpperBound} + * of {@link Store}. */ public TransferInput<A, S> leastUpperBound(TransferInput<A, S> other) { if (store == null) { S newThenStore = thenStore.leastUpperBound(other.getThenStore()); S newElseStore = elseStore.leastUpperBound(other.getElseStore()); - return new TransferInput<>(node, analysis, newThenStore, - newElseStore); + return new TransferInput<>(node, analysis, newThenStore, newElseStore); } else { if (other.store == null) { // make sure we do not lose precision and keep two stores if at // least one of the two TransferInput's has two stores. return other.leastUpperBound(this); } - return new TransferInput<>(node, analysis, - store.leastUpperBound(other.getRegularStore())); + return new TransferInput<>( + node, analysis, store.leastUpperBound(other.getRegularStore())); } } @@ -254,8 +221,8 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { TransferInput<A, S> other = (TransferInput<A, S>) o; if (containsTwoStores()) { if (other.containsTwoStores()) { - return getThenStore().equals(other.getThenStore()) && - getElseStore().equals(other.getElseStore()); + return getThenStore().equals(other.getThenStore()) + && getElseStore().equals(other.getElseStore()); } } else { if (!other.containsTwoStores()) { @@ -268,7 +235,8 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { @Override public int hashCode() { - return HashCodeUtils.hash(this.analysis, this.node, this.store, this.thenStore, this.elseStore); + return HashCodeUtils.hash( + this.analysis, this.node, this.store, this.thenStore, this.elseStore); } @Override @@ -279,23 +247,4 @@ public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> { return "[" + store + "]"; } } - - public boolean hasDOToutput() { - return true; - } - - public String toDOToutput() { - if (store == null) { - if (thenStore.hasDOToutput()) { - return "[then=" + thenStore.toDOToutput() + ", else=" + elseStore.toDOToutput() + "]"; - } - return "[then=" + thenStore + ", else=" + elseStore + "]"; - } else { - if (store.hasDOToutput()) { - return "[" + store.toDOToutput() + "]"; - } - return "[" + store + "]"; - } - } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferResult.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferResult.java index d083372917..7c178a13fc 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferResult.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferResult.java @@ -5,38 +5,32 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ import java.util.Map; - import javax.lang.model.type.TypeMirror; /** - * {@code TransferResult} is used as the result type of the individual transfer - * functions of a {@link TransferFunction}. It always belongs to the result of - * the individual transfer function for a particular {@link org.checkerframework.dataflow.cfg.node.Node}, even though - * that {@code org.checkerframework.dataflow.cfg.node.Node} is not explicitly store in {@code TransferResult}. - * - * <p> + * {@code TransferResult} is used as the result type of the individual transfer functions of a + * {@link TransferFunction}. It always belongs to the result of the individual transfer function for + * a particular {@link org.checkerframework.dataflow.cfg.node.Node}, even though that {@code + * org.checkerframework.dataflow.cfg.node.Node} is not explicitly store in {@code TransferResult}. * - * A {@code TransferResult} contains one or two stores (for 'then' and 'else'), - * and zero or more stores with a cause ({@link TypeMirror}). + * <p>A {@code TransferResult} contains one or two stores (for 'then' and 'else'), and zero or more + * stores with a cause ({@link TypeMirror}). * * @author Stefan Heule - * - * @param <S> - * The {@link Store} used to keep track of intermediate results. + * @param <S> the {@link Store} used to keep track of intermediate results */ -abstract public class TransferResult<A extends AbstractValue<A>, S extends Store<S>> { +public abstract class TransferResult<A extends AbstractValue<A>, S extends Store<S>> { /** - * The stores in case the basic block throws an exception (or {@code null} - * if the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} does not throw any exceptions). Does - * not necessarily contain a store for every exception, in which case the - * in-store will be used. + * The stores in case the basic block throws an exception (or {@code null} if the corresponding + * {@link org.checkerframework.dataflow.cfg.node.Node} does not throw any exceptions). Does not + * necessarily contain a store for every exception, in which case the in-store will be used. */ protected /*@Nullable*/ Map<TypeMirror, S> exceptionalStores; /** - * The abstract value of the {@link org.checkerframework.dataflow.cfg.node.Node} associated with this - * {@link TransferResult}, or {@code null} if no value has been produced. + * The abstract value of the {@link org.checkerframework.dataflow.cfg.node.Node} associated with + * this {@link TransferResult}, or {@code null} if no value has been produced. */ protected /*@Nullable*/ A resultValue; @@ -44,9 +38,7 @@ abstract public class TransferResult<A extends AbstractValue<A>, S extends Store this.resultValue = resultValue; } - /** - * @return The abstract value produced by the transfer function. - */ + /** @return the abstract value produced by the transfer function */ public A getResultValue() { return resultValue; } @@ -56,30 +48,29 @@ abstract public class TransferResult<A extends AbstractValue<A>, S extends Store } /** - * @return The regular result store produced if no exception is thrown by - * the {@link org.checkerframework.dataflow.cfg.node.Node} corresponding to this transfer function result. + * @return the regular result store produced if no exception is thrown by the {@link + * org.checkerframework.dataflow.cfg.node.Node} corresponding to this transfer function + * result. */ - abstract public S getRegularStore(); + public abstract S getRegularStore(); /** - * @return The result store produced if the {@link org.checkerframework.dataflow.cfg.node.Node} this result belongs - * to evaluates to {@code true}. + * @return the result store produced if the {@link org.checkerframework.dataflow.cfg.node.Node} + * this result belongs to evaluates to {@code true}. */ - abstract public S getThenStore(); + public abstract S getThenStore(); /** - * @return The result store produced if the {@link org.checkerframework.dataflow.cfg.node.Node} this result belongs - * to evaluates to {@code false}. + * @return the result store produced if the {@link org.checkerframework.dataflow.cfg.node.Node} + * this result belongs to evaluates to {@code false}. */ - abstract public S getElseStore(); + public abstract S getElseStore(); /** - * @return The store that flows along the outgoing exceptional edge labeled - * with {@code exception} (or {@code null} if no special handling is - * required for exceptional edges). + * @return the store that flows along the outgoing exceptional edge labeled with {@code + * exception} (or {@code null} if no special handling is required for exceptional edges). */ - public /*@Nullable*/ S getExceptionalStore( - TypeMirror exception) { + public /*@Nullable*/ S getExceptionalStore(TypeMirror exception) { if (exceptionalStores == null) { return null; } @@ -87,8 +78,7 @@ abstract public class TransferResult<A extends AbstractValue<A>, S extends Store } /** - * @return Returns a Map of {@link TypeMirror} to {@link Store}. - * + * @return a Map of {@link TypeMirror} to {@link Store} * @see TransferResult#getExceptionalStore(TypeMirror) */ public Map<TypeMirror, S> getExceptionalStores() { @@ -96,21 +86,19 @@ abstract public class TransferResult<A extends AbstractValue<A>, S extends Store } /** - * @return {@code true} if and only if this transfer result contains two - * stores that are potentially not equal. Note that the result - * {@code true} does not imply that {@code getRegularStore} cannot - * be called (or vice versa for {@code false}). Rather, it indicates - * that {@code getThenStore} or {@code getElseStore} can be used to - * give more precise results. Otherwise, if the result is - * {@code false}, then all three methods {@code getRegularStore}, - * {@code getThenStore}, and {@code getElseStore} return equivalent - * stores. + * @return {@code true} if and only if this transfer result contains two stores that are + * potentially not equal. Note that the result {@code true} does not imply that {@code + * getRegularStore} cannot be called (or vice versa for {@code false}). Rather, it indicates + * that {@code getThenStore} or {@code getElseStore} can be used to give more precise + * results. Otherwise, if the result is {@code false}, then all three methods {@code + * getRegularStore}, {@code getThenStore}, and {@code getElseStore} return equivalent + * stores. */ - abstract public boolean containsTwoStores(); + public abstract boolean containsTwoStores(); /** - * @return {@code true} if and only if the transfer function returning this - * transfer result changed the regularStore, elseStore, or thenStore. + * @return {@code true} if and only if the transfer function returning this transfer result + * changed the regularStore, elseStore, or thenStore. */ - abstract public boolean storeChanged(); + public abstract boolean storeChanged(); } 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 index 7b6804a080..55daff24ff 100644 --- 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 @@ -4,10 +4,95 @@ package org.checkerframework.dataflow.cfg; import org.checkerframework.checker.nullness.qual.Nullable; */ +import com.sun.source.tree.AnnotatedTypeTree; +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.ArrayAccessTree; +import com.sun.source.tree.ArrayTypeTree; +import com.sun.source.tree.AssertTree; +import com.sun.source.tree.AssignmentTree; +import com.sun.source.tree.BinaryTree; +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.BreakTree; +import com.sun.source.tree.CaseTree; +import com.sun.source.tree.CatchTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.CompoundAssignmentTree; +import com.sun.source.tree.ConditionalExpressionTree; +import com.sun.source.tree.ContinueTree; +import com.sun.source.tree.DoWhileLoopTree; +import com.sun.source.tree.EmptyStatementTree; +import com.sun.source.tree.EnhancedForLoopTree; +import com.sun.source.tree.ErroneousTree; +import com.sun.source.tree.ExpressionStatementTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.ForLoopTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.IfTree; +import com.sun.source.tree.ImportTree; +import com.sun.source.tree.InstanceOfTree; +import com.sun.source.tree.LabeledStatementTree; +import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.LiteralTree; +import com.sun.source.tree.MemberReferenceTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.ModifiersTree; +import com.sun.source.tree.NewArrayTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.ParameterizedTypeTree; +import com.sun.source.tree.ParenthesizedTree; +import com.sun.source.tree.PrimitiveTypeTree; +import com.sun.source.tree.ReturnTree; +import com.sun.source.tree.StatementTree; +import com.sun.source.tree.SwitchTree; +import com.sun.source.tree.SynchronizedTree; +import com.sun.source.tree.ThrowTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.TryTree; +import com.sun.source.tree.TypeCastTree; +import com.sun.source.tree.TypeParameterTree; +import com.sun.source.tree.UnaryTree; +import com.sun.source.tree.UnionTypeTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.tree.WhileLoopTree; +import com.sun.source.tree.WildcardTree; +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; +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 org.checkerframework.dataflow.analysis.Store; import org.checkerframework.dataflow.cfg.CFGBuilder.ExtendedNode.ExtendedNodeType; import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGMethod; @@ -91,7 +176,6 @@ import org.checkerframework.dataflow.cfg.node.VariableDeclarationNode; import org.checkerframework.dataflow.cfg.node.WideningConversionNode; 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; @@ -101,122 +185,29 @@ 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.AnnotatedTypeTree; -import com.sun.source.tree.AnnotationTree; -import com.sun.source.tree.ArrayAccessTree; -import com.sun.source.tree.ArrayTypeTree; -import com.sun.source.tree.AssertTree; -import com.sun.source.tree.AssignmentTree; -import com.sun.source.tree.BinaryTree; -import com.sun.source.tree.BlockTree; -import com.sun.source.tree.BreakTree; -import com.sun.source.tree.CaseTree; -import com.sun.source.tree.CatchTree; -import com.sun.source.tree.ClassTree; -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.CompoundAssignmentTree; -import com.sun.source.tree.ConditionalExpressionTree; -import com.sun.source.tree.ContinueTree; -import com.sun.source.tree.DoWhileLoopTree; -import com.sun.source.tree.EmptyStatementTree; -import com.sun.source.tree.EnhancedForLoopTree; -import com.sun.source.tree.ErroneousTree; -import com.sun.source.tree.ExpressionStatementTree; -import com.sun.source.tree.ExpressionTree; -import com.sun.source.tree.ForLoopTree; -import com.sun.source.tree.IdentifierTree; -import com.sun.source.tree.IfTree; -import com.sun.source.tree.ImportTree; -import com.sun.source.tree.InstanceOfTree; -import com.sun.source.tree.LabeledStatementTree; -import com.sun.source.tree.LambdaExpressionTree; -import com.sun.source.tree.LiteralTree; -import com.sun.source.tree.MemberReferenceTree; -import com.sun.source.tree.MemberSelectTree; -import com.sun.source.tree.MethodInvocationTree; -import com.sun.source.tree.MethodTree; -import com.sun.source.tree.ModifiersTree; -import com.sun.source.tree.NewArrayTree; -import com.sun.source.tree.NewClassTree; -import com.sun.source.tree.ParameterizedTypeTree; -import com.sun.source.tree.ParenthesizedTree; -import com.sun.source.tree.PrimitiveTypeTree; -import com.sun.source.tree.ReturnTree; -import com.sun.source.tree.StatementTree; -import com.sun.source.tree.SwitchTree; -import com.sun.source.tree.SynchronizedTree; -import com.sun.source.tree.ThrowTree; -import com.sun.source.tree.Tree; -import com.sun.source.tree.Tree.Kind; -import com.sun.source.tree.TryTree; -import com.sun.source.tree.TypeCastTree; -import com.sun.source.tree.TypeParameterTree; -import com.sun.source.tree.UnaryTree; -import com.sun.source.tree.UnionTypeTree; -import com.sun.source.tree.VariableTree; -import com.sun.source.tree.WhileLoopTree; -import com.sun.source.tree.WildcardTree; -import com.sun.source.util.TreePath; -import com.sun.source.util.TreePathScanner; -import com.sun.source.util.Trees; - /** - * Builds the control flow graph of some Java code (either a method, or an - * arbitrary statement). + * Builds the control flow graph of some Java code (either a method, or an arbitrary statement). * - * <p> + * <p>The translation of the AST to the CFG is split into three phases: * - * 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> + * <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><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><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. * </ol> * * @author Stefan Heule - * */ public class CFGBuilder { @@ -233,116 +224,128 @@ public class CFGBuilder { } /** - * Class declarations that have been encountered when building the - * control-flow graph for a method. + * Class declarations that have been encountered when building the control-flow graph for a + * method. */ - protected List<ClassTree> declaredClasses; + 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. + * Lambdas encountered when building the control-flow graph for a method, variable initializer, + * or initializer. */ - protected List<LambdaExpressionTree> declaredLambdas; + protected final List<LambdaExpressionTree> declaredLambdas = new LinkedList<>(); public List<LambdaExpressionTree> getDeclaredLambdas() { return declaredLambdas; } - /** - * Build the control flow graph of some code. - */ + /** 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); + 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. + * 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); + 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. - */ + /** 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); + 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. - */ + /** Build the control flow graph of some code. */ public static ControlFlowGraph build( - CompilationUnitTree root, ProcessingEnvironment env, - UnderlyingAST underlyingAST) { + CompilationUnitTree root, ProcessingEnvironment env, UnderlyingAST underlyingAST) { return new CFGBuilder(false, false).run(root, env, underlyingAST); } - /** - * Build the control flow graph of a method. - */ + /** Build the control flow graph of a method. */ public static ControlFlowGraph build( - CompilationUnitTree root, ProcessingEnvironment env, - MethodTree tree, ClassTree classTree) { + 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. - */ + /** Build the control flow graph of some code. */ public ControlFlowGraph run( - CompilationUnitTree root, ProcessingEnvironment env, - UnderlyingAST underlyingAST) { - declaredClasses = new LinkedList<>(); - declaredLambdas = new LinkedList<>(); + 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); + 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. + * 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 = new LinkedList<>(); + 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); + 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. - */ + /** Build the control flow graph of a method. */ public ControlFlowGraph run( - CompilationUnitTree root, ProcessingEnvironment env, - MethodTree tree, ClassTree classTree) { + CompilationUnitTree root, + ProcessingEnvironment env, + MethodTree tree, + ClassTree classTree) { UnderlyingAST underlyingAST = new CFGMethod(tree, classTree); return run(root, env, underlyingAST); } @@ -358,25 +361,21 @@ public class CFGBuilder { protected final Label regularExitLabel = new Label(); /** - * An extended node can be one of several things (depending on its - * {@code type}): + * 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> + * <li><em>NODE</em>. An extended node of this type is just a wrapper for a {@link Node} (that + * cannot throw exceptions). + * <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><em>UNCONDITIONAL_JUMP</em>. An unconditional jump to a label. + * <li><em>TWO_TARGET_CONDITIONAL_JUMP</em>. A conditional jump with two targets for both the + * 'then' and 'else' branch. * </ul> */ - protected static abstract class ExtendedNode { + protected abstract static class ExtendedNode { - /** - * The basic block this extended node belongs to (as determined in phase - * two). - */ + /** The basic block this extended node belongs to (as determined in phase two). */ protected BlockImpl block; /** Type of this node. */ @@ -391,7 +390,10 @@ public class CFGBuilder { /** Extended node types (description see above). */ public enum ExtendedNodeType { - NODE, EXCEPTION_NODE, UNCONDITIONAL_JUMP, CONDITIONAL_JUMP + NODE, + EXCEPTION_NODE, + UNCONDITIONAL_JUMP, + CONDITIONAL_JUMP } public ExtendedNodeType getType() { @@ -407,8 +409,8 @@ public class CFGBuilder { } /** - * @return The node contained in this extended node (only applicable if - * the type is {@code NODE} or {@code EXCEPTION_NODE}). + * @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; @@ -416,9 +418,8 @@ public class CFGBuilder { } /** - * @return The label associated with this extended node (only applicable - * if type is {@link ExtendedNodeType#CONDITIONAL_JUMP} or - * {@link ExtendedNodeType#UNCONDITIONAL_JUMP}). + * @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; @@ -439,9 +440,7 @@ public class CFGBuilder { } } - /** - * An extended node of type {@code NODE}. - */ + /** An extended node of type {@code NODE}. */ protected static class NodeHolder extends ExtendedNode { protected Node node; @@ -460,21 +459,19 @@ public class CFGBuilder { public String toString() { return "NodeHolder(" + node + ")"; } - } - /** - * An extended node of type {@code EXCEPTION_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. + /** + * 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) { + public NodeWithExceptionsHolder(Node node, Map<TypeMirror, Set<Label>> exceptions) { super(ExtendedNodeType.EXCEPTION_NODE); this.node = node; this.exceptions = exceptions; @@ -493,19 +490,15 @@ public class CFGBuilder { 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. + * <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 { @@ -547,14 +540,11 @@ public class CFGBuilder { @Override public String toString() { - return "TwoTargetConditionalJump(" + getThenLabel() + "," - + getElseLabel() + ")"; + return "TwoTargetConditionalJump(" + getThenLabel() + "," + getElseLabel() + ")"; } } - /** - * An extended node of type {@link ExtendedNodeType#UNCONDITIONAL_JUMP}. - */ + /** An extended node of type {@link ExtendedNodeType#UNCONDITIONAL_JUMP}. */ protected static class UnconditionalJump extends ExtendedNode { protected Label jumpTarget; @@ -576,9 +566,9 @@ public class CFGBuilder { } /** - * 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. + * 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; @@ -599,8 +589,7 @@ public class CFGBuilder { } /** - * Return a new unique label name that cannot be confused with a Java - * source code label. + * Return a new unique label name that cannot be confused with a Java source code label. * * @return a new unique label name */ @@ -610,27 +599,26 @@ public class CFGBuilder { } /** - * A TryFrame takes a thrown exception type and maps it to a set - * of possible control-flow successors. + * 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. + * 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. + * 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. + /** 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) { @@ -639,10 +627,9 @@ public class CFGBuilder { } /** - * 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. + * 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) { @@ -666,11 +653,11 @@ public class CFGBuilder { // 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(); + assert thrown instanceof TypeVariable + : "thrown type must be a variable or a declared type"; + thrown = ((TypeVariable) thrown).getUpperBound(); } - DeclaredType declaredThrown = (DeclaredType)thrown; + DeclaredType declaredThrown = (DeclaredType) thrown; assert thrown != null : "thrown type must be bounded by a declared type"; for (Pair<TypeMirror, Label> pair : catchLabels) { @@ -678,7 +665,7 @@ public class CFGBuilder { boolean canApply = false; if (caught instanceof DeclaredType) { - DeclaredType declaredCaught = (DeclaredType)caught; + DeclaredType declaredCaught = (DeclaredType) caught; if (types.isSubtype(declaredThrown, declaredCaught)) { // No later catch blocks can apply. labels.add(pair.second); @@ -687,13 +674,13 @@ public class CFGBuilder { canApply = true; } } else { - assert caught instanceof UnionType : - "caught type must be a union or a declared type"; - UnionType caughtUnion = (UnionType)caught; + 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; + 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); @@ -713,10 +700,8 @@ public class CFGBuilder { } } - /** - * A TryFinallyFrame applies to exceptions of any type - */ - protected class TryFinallyFrame implements TryFrame { + /** A TryFinallyFrame applies to exceptions of any type */ + protected static class TryFinallyFrame implements TryFrame { protected Label finallyLabel; public TryFinallyFrame(Label finallyLabel) { @@ -731,9 +716,8 @@ public class CFGBuilder { } /** - * 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 + * 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 { @@ -754,8 +738,8 @@ public class CFGBuilder { } /** - * Returns the set of possible {@link Label}s where control may - * transfer when an exception of the given type is thrown. + * 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 @@ -776,31 +760,29 @@ public class CFGBuilder { /* --------------------------------------------------------- */ /** - * Class that performs phase three of the translation process. In - * particular, the following degenerate cases of basic blocks are removed: + * 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> + * <li>Empty regular basic blocks: These blocks will be removed and their predecessors linked + * directly to the successor. + * <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>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>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. * </ol> * - * Eliminating the second type of degenerate cases might introduce cases of - * the third problem. These are also removed. + * 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. + * A simple wrapper object that holds a basic block and allows to set one of its successors. */ protected interface PredecessorHolder { void setSuccessor(BlockImpl b); @@ -811,11 +793,10 @@ public class CFGBuilder { /** * 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. + * @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(); @@ -845,8 +826,7 @@ public class CFGBuilder { if (b.isEmpty()) { Set<RegularBlockImpl> empty = new HashSet<>(); Set<PredecessorHolder> predecessors = new HashSet<>(); - BlockImpl succ = computeNeighborhoodOfEmptyBlock(b, - empty, predecessors); + BlockImpl succ = computeNeighborhoodOfEmptyBlock(b, empty, predecessors); for (RegularBlockImpl e : empty) { succ.removePredecessor(e); dontVisit.add(e); @@ -871,8 +851,7 @@ public class CFGBuilder { assert cb.getPredecessors().size() == 1; if (cb.getThenSuccessor() == cb.getElseSuccessor()) { BlockImpl pred = cb.getPredecessors().iterator().next(); - PredecessorHolder predecessorHolder = getPredecessorHolder( - pred, cb); + PredecessorHolder predecessorHolder = getPredecessorHolder(pred, cb); BlockImpl succ = (BlockImpl) cb.getThenSuccessor(); succ.removePredecessor(cb); predecessorHolder.setSuccessor(succ); @@ -901,24 +880,19 @@ public class CFGBuilder { } /** - * 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). + * 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. + * @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, + RegularBlockImpl start, + Set<RegularBlockImpl> empty, Set<PredecessorHolder> predecessors) { // get empty neighborhood that come before 'start' @@ -929,8 +903,7 @@ public class CFGBuilder { while (succ.getType() == BlockType.REGULAR_BLOCK) { RegularBlockImpl cur = (RegularBlockImpl) succ; if (cur.isEmpty()) { - computeNeighborhoodOfEmptyBlockBackwards(cur, empty, - predecessors); + computeNeighborhoodOfEmptyBlockBackwards(cur, empty, predecessors); assert empty.contains(cur) : "cur ought to be in empty"; succ = (BlockImpl) cur.getSuccessor(); if (succ == cur) { @@ -945,138 +918,132 @@ public class CFGBuilder { } /** - * 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}). + * 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. + * @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, + 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 { + case SPECIAL_BLOCK: // add pred correctly to predecessor list predecessors.add(getPredecessorHolder(pred, cur)); - } - break; + 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} + * 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; - } - }; + 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); + 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}. + * @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) { @@ -1117,23 +1084,18 @@ public class CFGBuilder { } } - /** - * Class that performs phase two of the translation process. - */ + /** Class that performs phase two of the translation process. */ public class CFGTranslationPhaseTwo { - public 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). + * @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) { @@ -1144,20 +1106,20 @@ public class CFGBuilder { assert in.nodeList.size() > 0; // exit blocks - SpecialBlockImpl regularExitBlock = new SpecialBlockImpl( - SpecialBlockType.EXIT); - SpecialBlockImpl exceptionalExitBlock = new SpecialBlockImpl( - SpecialBlockType.EXCEPTIONAL_EXIT); + 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<>(); + Set<Tuple<? extends SingleSuccessorBlockImpl, Integer, ?>> missingEdges = + new MostlySingleton<>(); // missing exceptional edges - Set<Tuple<ExceptionBlockImpl, Integer, TypeMirror>> missingExceptionalEdges = new HashSet<>(); + Set<Tuple<ExceptionBlockImpl, Integer, TypeMirror>> missingExceptionalEdges = + new HashSet<>(); // create start block - SpecialBlockImpl startBlock = new SpecialBlockImpl( - SpecialBlockType.ENTRY); + SpecialBlockImpl startBlock = new SpecialBlockImpl(SpecialBlockType.ENTRY); missingEdges.add(new Tuple<>(startBlock, 0)); // loop through all 'leaders' (while dynamically detecting the @@ -1166,104 +1128,108 @@ public class CFGBuilder { 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); + 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 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; + 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)); } - } - break; + + // 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++; } @@ -1293,8 +1259,14 @@ public class CFGBuilder { } } - return new ControlFlowGraph(startBlock, regularExitBlock, exceptionalExitBlock, in.underlyingAST, - in.treeLookupMap, in.convertedTreeLookupMap, in.returnNodes); + return new ControlFlowGraph( + startBlock, + regularExitBlock, + exceptionalExitBlock, + in.underlyingAST, + in.treeLookupMap, + in.convertedTreeLookupMap, + in.returnNodes); } } @@ -1303,8 +1275,8 @@ public class CFGBuilder { /* --------------------------------------------------------- */ /** - * A wrapper object to pass around the result of phase one. For a - * documentation of the fields see {@link CFGTranslationPhaseOne}. + * A wrapper object to pass around the result of phase one. For a documentation of the fields + * see {@link CFGTranslationPhaseOne}. */ protected static class PhaseOneResult { @@ -1316,11 +1288,14 @@ public class CFGBuilder { private final Set<Integer> leaders; private final List<ReturnNode> returnNodes; - public PhaseOneResult(UnderlyingAST underlyingAST, + 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) { + ArrayList<ExtendedNode> nodeList, + Map<Label, Integer> bindings, + Set<Integer> leaders, + List<ReturnNode> returnNodes) { this.underlyingAST = underlyingAST; this.treeLookupMap = treeLookupMap; this.convertedTreeLookupMap = convertedTreeLookupMap; @@ -1344,8 +1319,10 @@ public class CFGBuilder { if (n.getType() == ExtendedNodeType.CONDITIONAL_JUMP) { ConditionalJump t = (ConditionalJump) n; return "TwoTargetConditionalJump(" - + resolveLabel(t.getThenLabel()) + "," - + resolveLabel(t.getElseLabel()) + ")"; + + resolveLabel(t.getThenLabel()) + + "," + + resolveLabel(t.getElseLabel()) + + ")"; } else if (n.getType() == ExtendedNodeType.UNCONDITIONAL_JUMP) { return "UnconditionalJump(" + resolveLabel(n.getLabel()) + ")"; } else { @@ -1360,45 +1337,35 @@ public class CFGBuilder { } return nodeToString(nodeList.get(index)); } - } /** - * Class that performs phase one of the translation process. It generates - * the following information: + * 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> + * <li>A sequence of extended nodes. + * <li>A set of bindings from {@link Label}s to positions in the node sequence. + * <li>A set of leader nodes that give rise to basic blocks in phase two. + * <li>A lookup map that gives the mapping from AST tree nodes to {@link Node}s. * </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> + * <p>The return type of this scanner is {@link Node}. For expressions, the corresponding node + * is returned to allow linking between different nodes. * - * Every {@code visit*} method is assumed to add at least one extended node - * to the list of nodes (which might only be a jump). + * <p>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() { - } + public CFGTranslationPhaseOne() {} - /** - * Annotation processing environment and its associated type and tree - * utilities. - */ + /** Annotation processing environment and its associated type and tree utilities. */ protected ProcessingEnvironment env; + protected Elements elements; protected Types types; protected Trees trees; @@ -1406,44 +1373,35 @@ public class CFGBuilder { protected AnnotationProvider annotationProvider; /** - * Current {@link Label} to which a break statement with no label should - * jump, or null if there is no valid destination. + * 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. + * 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. + * 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. + * 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; /** - * Node yielding the value for the lexically enclosing synchronized statement, - * or null if there is no such statement. - */ - protected Node synchronizedExpr; - - /** - * 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. + * 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; @@ -1453,49 +1411,39 @@ public class CFGBuilder { /** The list of extended nodes. */ protected ArrayList<ExtendedNode> nodeList; - /** - * The bindings of labels to positions (i.e., indices) in the - * {@code 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 + * 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. - */ + /** 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. + * @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) { + TreePath bodyPath, + ProcessingEnvironment env, + UnderlyingAST underlyingAST, + Label exceptionalExitLabel, + TreeBuilder treeBuilder, + AnnotationProvider annotationProvider) { this.env = env; this.tryStack = new TryStack(exceptionalExitLabel); this.treeBuilder = treeBuilder; @@ -1524,25 +1472,39 @@ public class CFGBuilder { // removed in a later phase. nodeList.add(new UnconditionalJump(regularExitLabel)); - return new PhaseOneResult(underlyingAST, treeLookupMap, - convertedTreeLookupMap, nodeList, - bindings, leaders, returnNodes); + 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) { + 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); + 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. + * 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 + * @param tree the newly created Tree */ public void handleArtificialTree(Tree tree) {} @@ -1553,8 +1515,7 @@ public class CFGBuilder { /** * Add a node to the lookup map if it not already present. * - * @param node - * The node to add to the lookup map. + * @param node the node to add to the lookup map */ protected void addToLookupMap(Node node) { Tree tree = node.getTree(); @@ -1573,13 +1534,11 @@ public class CFGBuilder { } /** - * 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. + * 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. + * @param node the node to add to the lookup map */ protected void addToConvertedLookupMap(Node node) { Tree tree = node.getTree(); @@ -1587,15 +1546,12 @@ public class CFGBuilder { } /** - * 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. + * 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. + * @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; @@ -1606,9 +1562,8 @@ public class CFGBuilder { /** * Extend the list of extended nodes with a node. * - * @param node - * The node to add. - * @return The same node (for convenience). + * @param node the node to add + * @return the same node (for convenience) */ protected <T extends Node> T extendWithNode(T node) { addToLookupMap(node); @@ -1617,54 +1572,46 @@ public class CFGBuilder { } /** - * Extend the list of extended nodes with a node, where - * <code>node</code> might throw the exception <code>cause</code>. + * 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. + * @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) { + 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</code> might throw any of the exception in - * <code>causes</code>. + * 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. + * @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) { + 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); + NodeWithExceptionsHolder exNode = new NodeWithExceptionsHolder(node, exceptions); extendWithExtendedNode(exNode); return exNode; } /** - * Insert <code>node</code> after <code>pred</code> in - * the list of extended nodes, or append to the list if - * <code>pred</code> is not present. + * 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. + * @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); @@ -1673,28 +1620,22 @@ public class CFGBuilder { } /** - * Insert a <code>node</code> that might throw the exception - * <code>cause</code> after <code>pred</code> in the list of - * extended nodes, or append to the list if <code>pred</code> - * is not present. + * 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. + * @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) { + 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); + NodeWithExceptionsHolder exNode = new NodeWithExceptionsHolder(node, exceptions); insertExtendedNodeAfter(exNode, pred); return exNode; } @@ -1702,29 +1643,24 @@ public class CFGBuilder { /** * Extend the list of extended nodes with an extended node. * - * @param n - * The extended node. + * @param n the extended node */ protected void extendWithExtendedNode(ExtendedNode n) { nodeList.add(n); } /** - * Insert <code>n</code> after the node <code>pred</code> in the - * list of extended nodes, or append <code>n</code> if <code>pred</code> - * is not present. + * 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. + * @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 instanceof NodeHolder || inList instanceof NodeWithExceptionsHolder) { if (inList.getNode() == pred) { index = i; break; @@ -1733,14 +1669,29 @@ public class CFGBuilder { } 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. + * 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()); @@ -1752,28 +1703,26 @@ public class CFGBuilder { /* --------------------------------------------------------- */ 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. + * 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 + * @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)); + PrimitiveType primitive = types.getPrimitiveType(node.getType().getKind()); + TypeMirror boxedType = types.getDeclaredType(types.boxedClass(primitive)); - TypeElement boxedElement = (TypeElement)((DeclaredType)boxedType).asElement(); + TypeElement boxedElement = (TypeElement) ((DeclaredType) boxedType).asElement(); IdentifierTree classTree = treeBuilder.buildClassUse(boxedElement); handleArtificialTree(classTree); ClassNameNode className = new ClassNameNode(classTree); @@ -1787,18 +1736,21 @@ public class CFGBuilder { insertNodeAfter(valueOfAccess, className); MethodInvocationTree valueOfCall = - treeBuilder.buildMethodInvocation(valueOfSelect, (ExpressionTree)node.getTree()); + treeBuilder.buildMethodInvocation( + valueOfSelect, (ExpressionTree) node.getTree()); handleArtificialTree(valueOfCall); - Node boxed = new MethodInvocationNode(valueOfCall, valueOfAccess, - Collections.singletonList(node), - getCurrentPath()); + 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"); + TypeElement throwableElement = elements.getTypeElement("java.lang.Throwable"); addToConvertedLookupMap(node.getTree(), boxed); - insertNodeWithExceptionsAfter(boxed, - Collections.singleton(throwableElement.asType()), valueOfAccess); + insertNodeWithExceptionsAfter( + boxed, Collections.singleton(throwableElement.asType()), valueOfAccess); return boxed; } else { return node; @@ -1806,42 +1758,41 @@ public class CFGBuilder { } /** - * If the input node is a boxed type, unbox it, otherwise leave it - * alone. + * 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 + * @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()); + 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); + TypeElement npeElement = elements.getTypeElement("java.lang.NullPointerException"); + insertNodeWithExceptionsAfter( + primValueAccess, Collections.singleton(npeElement.asType()), node); MethodInvocationTree primValueCall = - treeBuilder.buildMethodInvocation(primValueSelect); + treeBuilder.buildMethodInvocation(primValueSelect); handleArtificialTree(primValueCall); - Node unboxed = new MethodInvocationNode(primValueCall, primValueAccess, - Collections.<Node>emptyList(), - getCurrentPath()); + 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"); + TypeElement throwableElement = elements.getTypeElement("java.lang.Throwable"); addToConvertedLookupMap(node.getTree(), unboxed); - insertNodeWithExceptionsAfter(unboxed, - Collections.singleton(throwableElement.asType()), primValueAccess); + insertNodeWithExceptionsAfter( + unboxed, Collections.singleton(throwableElement.asType()), primValueAccess); return unboxed; } else { return node; @@ -1879,9 +1830,7 @@ public class CFGBuilder { }; } - /** - * @return the unboxed tree if necessary, as described in JLS 5.1.8 - */ + /** @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; } @@ -1889,18 +1838,15 @@ public class CFGBuilder { /** * 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 + * @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"); + TypeElement stringElement = elements.getTypeElement("java.lang.String"); if (!TypesUtils.isString(node.getType())) { - Node converted = new StringConversionNode(node.getTree(), node, - stringElement.asType()); + Node converted = + new StringConversionNode(node.getTree(), node, stringElement.asType()); addToConvertedLookupMap(converted); insertNodeAfter(converted, node); return converted; @@ -1912,37 +1858,36 @@ public class CFGBuilder { /** * 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 + * @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; + 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. + * 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)) { @@ -1952,12 +1897,12 @@ public class CFGBuilder { } /** - * 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. + * 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 + * @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) { @@ -1972,24 +1917,18 @@ public class CFGBuilder { } /** - * Perform binary numeric promotion on the input node to make it match - * the expression type. + * 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 + * @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); + Node widened = new WideningConversionNode(node.getTree(), node, exprType); addToConvertedLookupMap(widened); insertNodeAfter(widened, node); return widened; @@ -1999,24 +1938,20 @@ public class CFGBuilder { } /** - * Perform widening primitive conversion on the input node to make it - * match the destination type. + * 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 + * @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"; + 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); + Node widened = new WideningConversionNode(node.getTree(), node, destType); addToConvertedLookupMap(widened); insertNodeAfter(widened, node); return widened; @@ -2026,24 +1961,19 @@ public class CFGBuilder { } /** - * Perform narrowing conversion on the input node to make it match the - * destination type. + * 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 + * @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"; + 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); + Node narrowed = new NarrowingConversionNode(node.getTree(), node, destType); addToConvertedLookupMap(narrowed); insertNodeAfter(narrowed, node); return narrowed; @@ -2053,15 +1983,13 @@ public class CFGBuilder { } /** - * Perform narrowing conversion and optionally boxing conversion on the - * input node to make it match the destination type. + * 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 + * @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)) { @@ -2071,47 +1999,41 @@ public class CFGBuilder { } } - /** - * Return whether a conversion from the type of the node to varType - * requires narrowing. + * 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 + * @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; + 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 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. + * 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 + * @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) { + protected Node commonConvert( + Node node, TypeMirror varType, boolean contextAllowsNarrowing) { // For assignment conversion, see JLS 5.2 // For method invocation conversion, see JLS 5.3 @@ -2150,8 +2072,7 @@ public class CFGBuilder { node = unbox(node); nodeType = node.getType(); - if (types.isSubtype(nodeType, varType) - && !types.isSameType(nodeType, varType)) { + if (types.isSubtype(nodeType, varType) && !types.isSameType(nodeType, varType)) { node = widen(node, varType); nodeType = node.getType(); } @@ -2169,50 +2090,42 @@ public class CFGBuilder { } /** - * Perform assignment conversion so that it can be assigned to a - * variable of the given type. + * 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 + * @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. + * 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 + * @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. + * 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. + * @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) { + 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, @@ -2238,48 +2151,46 @@ public class CFGBuilder { // 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())); + convertedNodes.add( + methodInvocationConvert(actualVal, formals.get(i).asType())); } - Node lastArgument = new ArrayCreationNode(null, - lastParamType, dimensions, initializers); + 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)) { + 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())); + convertedNodes.add( + methodInvocationConvert(actualVal, formals.get(i).asType())); } } else { - assert lastParamType instanceof ArrayType : - "variable argument formal must be an array"; + 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())); + convertedNodes.add( + methodInvocationConvert(actualVal, formals.get(i).asType())); } - TypeMirror elemType = - ((ArrayType)lastParamType).getComponentType(); + 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); + Node lastArgument = + new ArrayCreationNode( + null, lastParamType, dimensions, initializers); extendWithNode(lastArgument); convertedNodes.add(lastArgument); } @@ -2287,8 +2198,7 @@ public class CFGBuilder { } else { for (int i = 0; i < numActuals; i++) { Node actualVal = scan(actualExprs.get(i), null); - convertedNodes.add(methodInvocationConvert(actualVal, - formals.get(i).asType())); + convertedNodes.add(methodInvocationConvert(actualVal, formals.get(i).asType())); } } @@ -2296,16 +2206,11 @@ public class CFGBuilder { } /** - * Convert an operand of a conditional expression to the type of the - * whole expression. + * 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 + * @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, @@ -2320,23 +2225,19 @@ public class CFGBuilder { // If the operand is a primitive and the whole expression is // boxed, then apply boxing. - if (TypesUtils.isPrimitive(nodeType) && - TypesUtils.isBoxedPrimitive(destType)) { + 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 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) { + 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); } @@ -2346,8 +2247,9 @@ public class CFGBuilder { // 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 (destKind == TypeKind.BYTE + || destKind == TypeKind.CHAR + || destKind == TypeKind.SHORT) { if (isBoxedPrimitive) { return unbox(node); } else if (nodeType.getKind() == TypeKind.INT) { @@ -2359,10 +2261,10 @@ public class CFGBuilder { } // 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)) { + if (TypesUtils.isPrimitive(nodeType) + && (destType.getKind() == TypeKind.DECLARED + || destType.getKind() == TypeKind.UNION + || destType.getKind() == TypeKind.INTERSECTION)) { return box(node); } @@ -2370,8 +2272,8 @@ public class CFGBuilder { } /** - * Returns the label {@link Name} of the leaf in the argument path, or - * null if the leaf is not a labeled statement. + * 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) { @@ -2413,29 +2315,31 @@ public class CFGBuilder { // Fifth, if the method is synchronized, lock the receiving // object or class (15.12.4.5) ExecutableElement method = TreeUtils.elementFromUse(tree); + if (method == null) { + // The method wasn't found, e.g. because of a compilation error. + return null; + } + // TODO? Variable wasn't used. // boolean isBooleanMethod = TypesUtils.isBooleanType(method.getReturnType()); ExpressionTree methodSelect = tree.getMethodSelect(); - assert TreeUtils.isMethodAccess(methodSelect) : "Expected a method access, but got: " + methodSelect; + 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())); + Node receiver = getReceiver(methodSelect, TreeUtils.enclosingClass(getCurrentPath())); - MethodAccessNode target = new MethodAccessNode(methodSelect, - receiver); + MethodAccessNode target = new MethodAccessNode(methodSelect, receiver); ExecutableElement element = TreeUtils.elementFromUse(tree); - if (ElementUtils.isStatic(element) || - receiver instanceof ThisLiteralNode) { + 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"); + TypeElement npeElement = elements.getTypeElement("java.lang.NullPointerException"); extendWithNodeWithException(target, npeElement.asType()); } @@ -2454,23 +2358,24 @@ public class CFGBuilder { // TODO: lock the receiver for synchronized methods - MethodInvocationNode node = new MethodInvocationNode(tree, target, arguments, getCurrentPath()); + 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"); + 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; + boolean terminatesExecution = + annotationProvider.getDeclAnnotation(methodElement, TerminatesExecution.class) + != null; if (terminatesExecution) { extendedNode.setTerminatesExecution(true); } @@ -2483,11 +2388,6 @@ public class CFGBuilder { // see JLS 14.10 - // If assertions are disabled, then nothing is executed. - if (assumeAssertionsDisabled) { - return null; - } - // If assertions are enabled, then we can just translate the // assertion. if (assumeAssertionsEnabled || assumeAssertionsEnabledFor(tree)) { @@ -2495,6 +2395,11 @@ public class CFGBuilder { 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. @@ -2505,8 +2410,7 @@ public class CFGBuilder { Label assertionDisabled = new Label(); extendWithNode(new LocalVariableNode(ea)); - extendWithExtendedNode(new ConditionalJump(assertionEnabled, - assertionDisabled)); + extendWithExtendedNode(new ConditionalJump(assertionEnabled, assertionDisabled)); // 'then' branch (i.e. check the assertion) addLabelForNextNode(assertionEnabled); @@ -2520,28 +2424,24 @@ public class CFGBuilder { } /** - * Should assertions be assumed to be executed for a given - * {@link AssertTree}? False by default. + * 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. - */ + /** 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. + * 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()); + MethodTree enclosingMethod = TreeUtils.enclosingMethod(getCurrentPath()); Element owner; if (enclosingMethod != null) { owner = TreeUtils.elementFromDeclaration(enclosingMethod); @@ -2550,16 +2450,16 @@ public class CFGBuilder { owner = TreeUtils.elementFromDeclaration(enclosingClass); } ExpressionTree initializer = null; - ea = treeBuilder.buildVariableDecl( - types.getPrimitiveType(TypeKind.BOOLEAN), name, owner, - initializer); + 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. + * Translates an assertion statement to the correct CFG nodes. The translation assumes that + * assertions are enabled. */ protected void translateAssertWithAssertionsEnabled(AssertTree tree) { @@ -2578,13 +2478,14 @@ public class CFGBuilder { 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()); + 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()); + NodeWithExceptionsHolder exNode = + extendWithNodeWithException( + new ThrowNode(null, assertNode, env.getTypeUtils()), + assertException.asType()); exNode.setTerminatesExecution(true); // then branch (nothing happens) @@ -2603,8 +2504,7 @@ public class CFGBuilder { // case 1: field access if (TreeUtils.isFieldAccess(variable)) { // visit receiver - Node receiver = getReceiver(variable, - TreeUtils.enclosingClass(getCurrentPath())); + Node receiver = getReceiver(variable, TreeUtils.enclosingClass(getCurrentPath())); // visit expression Node expression = scan(tree.getExpression(), p); @@ -2615,19 +2515,17 @@ public class CFGBuilder { target.setLValue(); Element element = TreeUtils.elementFromUse(variable); - if (ElementUtils.isStatic(element) || - receiver instanceof ThisLiteralNode) { + 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"); + TypeElement npeElement = + elements.getTypeElement("java.lang.NullPointerException"); extendWithNodeWithException(target, npeElement.asType()); } // add assignment node - assignmentNode = new AssignmentNode(tree, - target, expression); + assignmentNode = new AssignmentNode(tree, target, expression); extendWithNode(assignmentNode); } @@ -2636,100 +2534,93 @@ public class CFGBuilder { Node target = scan(variable, p); target.setLValue(); - assignmentNode = translateAssignment(tree, target, - tree.getExpression()); + assignmentNode = translateAssignment(tree, target, tree.getExpression()); } return assignmentNode; } - /** - * Translate an assignment. - */ - protected AssignmentNode translateAssignment(Tree tree, Node target, - ExpressionTree rhs) { + /** 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; + /** 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); + AssignmentNode assignmentNode = new AssignmentNode(tree, target, expression); extendWithNode(assignmentNode); return assignmentNode; } /** - * Note 1: Requires <code>tree</code> to be a field or method access - * tree. - * <p> - * Note 2: Visits the receiver and adds all necessary blocks to the CFG. + * 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. + * @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); + private Node getReceiver(ExpressionTree 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; + Element ele = TreeUtils.elementFromUse(tree); + TypeElement declaringClass = ElementUtils.enclosingClass(ele); + TypeMirror type = ElementUtils.getType(declaringClass); + if (ElementUtils.isStatic(ele)) { + Node node = new ClassNameNode(type, declaringClass); + extendWithNode(node); + return node; + } else { + Node node = new ImplicitThisLiteralNode(type); + extendWithNode(node); + return node; + } } } /** - * Map an operation with assignment to the corresponding operation - * without assignment. + * Map an operation with assignment to the corresponding operation without assignment. * - * @param kind a Tree.Kind representing an operation with 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; + 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 @@ -2739,84 +2630,193 @@ public class CFGBuilder { 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); + 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); + 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; - } + 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 + 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; + } + } - Node targetLHS = scan(tree.getVariable(), p); - Node value = scan(tree.getExpression(), p); + 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); - TypeMirror leftType = InternalUtils.typeOf(tree.getVariable()); - TypeMirror rightType = InternalUtils.typeOf(tree.getExpression()); + TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree); + handleArtificialTree(castTree); + TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType); + castNode.setInSource(false); + extendWithNode(castNode); - 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); + AssignmentNode assignNode = new AssignmentNode(tree, targetLHS, castNode); + extendWithNode(assignNode); + return assignNode; + } - BinaryTree operTree = treeBuilder.buildBinary(promotedType, withoutAssignment(kind), - tree.getVariable(), tree.getExpression()); + 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.PLUS_ASSIGNMENT) { - operNode = new NumericalAdditionNode(operTree, targetRHS, value); + 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.MINUS_ASSIGNMENT; - operNode = new NumericalSubtractionNode(operTree, targetRHS, value); + assert kind == Kind.XOR_ASSIGNMENT; + operNode = new BitwiseXorNode(operTree, targetRHS, value); } extendWithNode(operNode); @@ -2826,101 +2826,12 @@ public class CFGBuilder { 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; + default: + assert false : "unexpected compound assignment type"; + break; } assert false : "unexpected compound assignment type"; return null; @@ -2938,235 +2849,246 @@ public class CFGBuilder { 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); + 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; } - } - 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); + 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; } - } - 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 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 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()); - } + 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); + } - 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); + TypeMirror rightType = InternalUtils.typeOf(rightTree); + if (TypesUtils.isBoxedPrimitive(rightType)) { + rightType = types.unboxedType(rightType); + } - return node; - } + 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); + } - 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)); - } + extendWithNode(node); - 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); - } + return node; + } - extendWithNode(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()); + } - return node; - } + 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); - case CONDITIONAL_AND: - case CONDITIONAL_OR: { - // see JLS 15.23 and 15.24 + return node; + } - // all necessary labels - Label rightStartL = new Label(); - Label shortCircuitL = new Label(); + 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)); + } - // left-hand side - Node left = scan(leftTree, 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); + } - 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); + extendWithNode(node); - // right-hand side - addLabelForNextNode(rightStartL); - Node right = scan(rightTree, p); + return node; + } - // 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; + 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); @@ -3190,8 +3112,7 @@ public class CFGBuilder { } else { assert breakLabels.containsKey(label); - extendWithExtendedNode(new UnconditionalJump( - breakLabels.get(label))); + extendWithExtendedNode(new UnconditionalJump(breakLabels.get(label))); } return null; @@ -3205,9 +3126,9 @@ public class CFGBuilder { } private class SwitchBuilder { - final private SwitchTree switchTree; - final private Label[] caseBodyLabels; - final private Void p; + private final SwitchTree switchTree; + private final Label[] caseBodyLabels; + private final Void p; private Node switchExpr; private SwitchBuilder(SwitchTree tree, Void p) { @@ -3219,17 +3140,19 @@ public class CFGBuilder { public void build() { Label oldBreakTargetL = breakTargetL; breakTargetL = new Label(); - int cases = caseBodyLabels.length-1; - for(int i=0; i<cases; ++i) { + 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())); + extendWithNode( + new MarkerNode( + switchTree, "start of switch statement", env.getTypeUtils())); Integer defaultIndex = null; - for(int i=0; i<cases; ++i) { + for (int i = 0; i < cases; ++i) { CaseTree caseTree = switchTree.getCases().get(i); if (caseTree.getExpression() == null) { defaultIndex = i; @@ -3250,7 +3173,7 @@ public class CFGBuilder { private void buildCase(CaseTree tree, int index) { final Label thisBodyL = caseBodyLabels[index]; - final Label nextBodyL = caseBodyLabels[index+1]; + final Label nextBodyL = caseBodyLabels[index + 1]; final Label nextCaseL = new Label(); ExpressionTree exprTree = tree.getExpression(); @@ -3288,8 +3211,7 @@ public class CFGBuilder { } @Override - public Node visitConditionalExpression(ConditionalExpressionTree tree, - Void p) { + public Node visitConditionalExpression(ConditionalExpressionTree tree, Void p) { // see JLS 15.25 TypeMirror exprType = InternalUtils.typeOf(tree); @@ -3327,8 +3249,7 @@ public class CFGBuilder { } else { assert continueLabels.containsKey(label); - extendWithExtendedNode(new UnconditionalJump( - continueLabels.get(label))); + extendWithExtendedNode(new UnconditionalJump(continueLabels.get(label))); } return null; @@ -3387,329 +3308,322 @@ public class CFGBuilder { } @Override - public Node visitExpressionStatement(ExpressionStatementTree tree, - Void p) { + 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); - - TypeMirror iteratorType = InternalUtils.typeOf(iteratorCall); - - // Declare and initialize a new, unique iterator variable - VariableTree iteratorVariable = - treeBuilder.buildVariableDecl(iteratorType, // annotatedIteratorTypeTree, - uniqueName("iter"), - variableElement.getEnclosingElement(), - iteratorCall); - 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. - - TypeMirror arrayType = InternalUtils.typeOf(expression); - - // Declare and initialize a temporary array variable - VariableTree arrayVariable = - treeBuilder.buildVariableDecl(arrayType, - uniqueName("array"), - variableElement.getEnclosingElement(), - expression); - 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; + // 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(Integer.valueOf(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 @@ -3776,38 +3690,38 @@ public class CFGBuilder { public Node visitIdentifier(IdentifierTree tree, Void p) { Node node; if (TreeUtils.isFieldAccess(tree)) { - Node receiver = getReceiver(tree, - TreeUtils.enclosingClass(getCurrentPath())); + 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()); + 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); @@ -3887,33 +3801,33 @@ public class CFGBuilder { 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; + 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); @@ -3936,12 +3850,11 @@ public class CFGBuilder { public Node visitNewArray(NewArrayTree tree, Void p) { // see JLS 15.10 - ArrayType type = (ArrayType)InternalUtils.typeOf(tree); + ArrayType type = (ArrayType) InternalUtils.typeOf(tree); TypeMirror elemType = type.getComponentType(); List<? extends ExpressionTree> dimensions = tree.getDimensions(); - List<? extends ExpressionTree> initializers = tree - .getInitializers(); + List<? extends ExpressionTree> initializers = tree.getInitializers(); List<Node> dimensionNodes = new ArrayList<Node>(); if (dimensions != null) { @@ -3957,8 +3870,7 @@ public class CFGBuilder { } } - Node node = new ArrayCreationNode(tree, type, dimensionNodes, - initializerNodes); + Node node = new ArrayCreationNode(tree, type, dimensionNodes, initializerNodes); return extendWithNode(node); } @@ -3973,15 +3885,19 @@ public class CFGBuilder { // We ignore any class body because its methods should // be visited separately. + // TODO: For anonymous classes we want to propagate the current store + // to the anonymous class. + // See Issues 266, 811. // Convert constructor arguments ExecutableElement constructor = TreeUtils.elementFromUse(tree); List<? extends ExpressionTree> actualExprs = tree.getArguments(); - List<Node> arguments = convertCallArguments(constructor, - actualExprs); + List<Node> arguments = convertCallArguments(constructor, actualExprs); + // TODO: for anonymous classes, don't use the identifier alone. + // See Issue 890. Node constructorNode = scan(tree.getIdentifier(), p); Node node = new ObjectCreationNode(tree, constructorNode, arguments); @@ -3991,8 +3907,7 @@ public class CFGBuilder { List<? extends TypeMirror> thrownTypes = constructor.getThrownTypes(); thrownSet.addAll(thrownTypes); // Add Throwable to account for unchecked exceptions - TypeElement throwableElement = elements - .getTypeElement("java.lang.Throwable"); + TypeElement throwableElement = elements.getTypeElement("java.lang.Throwable"); thrownSet.add(throwableElement.asType()); extendWithNodeWithExceptions(node, thrownSet); @@ -4001,14 +3916,14 @@ public class CFGBuilder { } /** - * Maps a <code>Tree</code> its directly enclosing <code>ParenthesizedTree</code> if one exists. + * 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</code> with the dataflow <code>Node</code> that was used - * during inference. This map is necessary because dataflow does - * not create a <code>Node</code> for a <code>ParenthesizedTree.</code> + * <p>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 Map<Tree, ParenthesizedTree> parenMapping = new HashMap<>(); + private final Map<Tree, ParenthesizedTree> parenMapping = new HashMap<>(); @Override public Node visitParenthesized(ParenthesizedTree tree, Void p) { @@ -4023,17 +3938,30 @@ public class CFGBuilder { 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))); + 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); + 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); + result = new ReturnNode(tree, node, env.getTypeUtils(), (MethodTree) enclosing); } returnNodes.add(result); extendWithNode(result); @@ -4051,18 +3979,18 @@ public class CFGBuilder { 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; + 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; } @@ -4070,14 +3998,13 @@ public class CFGBuilder { Node node = new FieldAccessNode(tree, expr); Element element = TreeUtils.elementFromUse(tree); - if (ElementUtils.isStatic(element) || - expr instanceof ImplicitThisLiteralNode || - expr instanceof ExplicitThisLiteralNode) { + 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"); + TypeElement npeElement = elements.getTypeElement("java.lang.NullPointerException"); extendWithNodeWithException(node, npeElement.asType()); } @@ -4093,11 +4020,13 @@ public class CFGBuilder { public Node visitSynchronized(SynchronizedTree tree, Void p) { // see JLS 14.19 - synchronizedExpr = scan(tree.getExpression(), p); - SynchronizedNode synchronizedStartNode = new SynchronizedNode(tree, synchronizedExpr, true, env.getTypeUtils()); + 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()); + SynchronizedNode synchronizedEndNode = + new SynchronizedNode(tree, synchronizedExpr, false, env.getTypeUtils()); extendWithNode(synchronizedEndNode); return null; @@ -4108,8 +4037,7 @@ public class CFGBuilder { Node expression = scan(tree.getExpression(), p); TypeMirror exception = expression.getType(); ThrowNode throwsNode = new ThrowNode(tree, expression, env.getTypeUtils()); - NodeWithExceptionsHolder exNode = extendWithNodeWithException( - throwsNode, exception); + NodeWithExceptionsHolder exNode = extendWithNodeWithException(throwsNode, exception); exNode.setTerminatesExecution(true); return throwsNode; } @@ -4161,7 +4089,8 @@ public class CFGBuilder { addLabelForNextNode(catchLabels.get(catchIndex).second); scan(c, p); catchIndex++; - extendWithExtendedNode(new UnconditionalJump(firstNonNull(finallyLabel, doneLabel))); + extendWithExtendedNode( + new UnconditionalJump(firstNonNull(finallyLabel, doneLabel))); } if (finallyLabel != null) { @@ -4169,10 +4098,10 @@ public class CFGBuilder { addLabelForNextNode(finallyLabel); scan(finallyBlock, p); - TypeMirror throwableType = - elements.getTypeElement("java.lang.Throwable").asType(); - extendWithNodeWithException(new MarkerNode(tree, "end of finally block", env.getTypeUtils()), - throwableType); + TypeMirror throwableType = elements.getTypeElement("java.lang.Throwable").asType(); + extendWithNodeWithException( + new MarkerNode(tree, "end of finally block", env.getTypeUtils()), + throwableType); } addLabelForNextNode(doneLabel); @@ -4222,8 +4151,7 @@ public class CFGBuilder { 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); + InstanceOfNode node = new InstanceOfNode(tree, operand, refType, types); extendWithNode(node); return node; } @@ -4233,130 +4161,141 @@ public class CFGBuilder { 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); + { + // 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; + } - LiteralTree oneTree = treeBuilder.buildLiteral(Integer.valueOf(1)); - handleArtificialTree(oneTree); + case LOGICAL_COMPLEMENT: + { + // see JLS 15.15.6 + Node expr = scan(tree.getExpression(), p); + result = extendWithNode(new ConditionalNotNode(tree, unbox(expr))); + break; + } - 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); + 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; - } + 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; - } + 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; + assert false : "Unknown kind (" + kind + ") of unary expression: " + tree; } return result; @@ -4367,22 +4306,24 @@ public class CFGBuilder { // see JLS 14.4 - boolean isField = TreeUtils.enclosingOfKind(getCurrentPath(), Kind.BLOCK) == null; + boolean isField = + getCurrentPath().getParentPath() != null + && getCurrentPath().getParentPath().getLeaf().getKind() == Kind.CLASS; Node node = null; - ClassTree enclosingClass = TreeUtils - .enclosingClass(getCurrentPath()); - TypeElement classElem = TreeUtils - .elementFromDeclaration(enclosingClass); + 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); + node = + translateAssignment( + tree, + new FieldAccessNode( + tree, TreeUtils.elementFromDeclaration(tree), receiver), + initializer); } else { // local variable definition VariableDeclarationNode decl = new VariableDeclarationNode(tree); @@ -4392,8 +4333,9 @@ public class CFGBuilder { ExpressionTree initializer = tree.getInitializer(); if (initializer != null) { - node = translateAssignment(tree, new LocalVariableNode(tree, receiver), - initializer); + node = + translateAssignment( + tree, new LocalVariableNode(tree, receiver), initializer); } } @@ -4481,13 +4423,14 @@ public class CFGBuilder { } } - /** - * A tuple with 4 named elements. - */ + /** A tuple with 4 named elements. */ private interface TreeInfo { boolean isBoxed(); + boolean isNumeric(); + boolean isBoolean(); + TypeMirror unboxedType(); } @@ -4506,40 +4449,43 @@ public class CFGBuilder { /* --------------------------------------------------------- */ /** - * Print a set of {@link Block}s and the edges between them. This is useful - * for examining the results of phase two. + * 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; - } + 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; + } } } } } - diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGDOTVisualizer.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGDOTVisualizer.java deleted file mode 100644 index abd482122d..0000000000 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGDOTVisualizer.java +++ /dev/null @@ -1,304 +0,0 @@ -package org.checkerframework.dataflow.cfg; - -/*>>> -import org.checkerframework.checker.nullness.qual.Nullable; -*/ - -import org.checkerframework.dataflow.analysis.AbstractValue; -import org.checkerframework.dataflow.analysis.Analysis; -import org.checkerframework.dataflow.analysis.Store; -import org.checkerframework.dataflow.analysis.TransferFunction; -import org.checkerframework.dataflow.analysis.TransferInput; -import org.checkerframework.dataflow.cfg.block.Block; -import org.checkerframework.dataflow.cfg.block.Block.BlockType; -import org.checkerframework.dataflow.cfg.block.ConditionalBlock; -import org.checkerframework.dataflow.cfg.block.ExceptionBlock; -import org.checkerframework.dataflow.cfg.block.RegularBlock; -import org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock; -import org.checkerframework.dataflow.cfg.block.SpecialBlock; -import org.checkerframework.dataflow.cfg.node.Node; - -import javax.lang.model.type.TypeMirror; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; -import java.util.Queue; -import java.util.Set; - -/** - * Generate a graph description in the DOT language of a control graph. - * - * @author Stefan Heule - * - */ -public class CFGDOTVisualizer { - - /** - * Output a graph description in the DOT language, representing the control - * flow graph starting at <code>entry</code>. Does not output verbose information - * or stores at the beginning of basic blocks. - * - * @see #visualize(ControlFlowGraph, Block, Analysis, boolean) - */ - public static String visualize(ControlFlowGraph cfg, Block entry) { - return visualize(cfg, entry, null, false); - } - - /** - * Output a graph description in the DOT language, representing the control - * flow graph starting at <code>entry</code>. - * - * @param entry - * The entry node of the control flow graph to be represented. - * @param analysis - * An analysis containing information about the program - * represented by the CFG. The information includes {@link Store} - * s that are valid at the beginning of basic blocks reachable - * from <code>entry</code> and per-node information for value - * producing {@link Node}s. Can also be <code>null</code> to - * indicate that this information should not be output. - * @param verbose - * Add more output to the CFG description. - * @return String representation of the graph in the DOT language. - */ - public static <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> String visualize( - ControlFlowGraph cfg, - Block entry, - /*@Nullable*/ Analysis<A, S, T> analysis, - boolean verbose) { - StringBuilder sb1 = new StringBuilder(); - StringBuilder sb2 = new StringBuilder(); - Set<Block> visited = new HashSet<>(); - Queue<Block> worklist = new LinkedList<>(); - Block cur = entry; - visited.add(entry); - - // header - sb1.append("digraph {\n"); - sb1.append(" node [shape=rectangle];\n\n"); - - // traverse control flow graph and define all arrows - while (true) { - if (cur == null) - break; - - if (cur.getType() == BlockType.CONDITIONAL_BLOCK) { - ConditionalBlock ccur = ((ConditionalBlock) cur); - Block thenSuccessor = ccur.getThenSuccessor(); - sb2.append(" " + ccur.getId() + " -> " - + thenSuccessor.getId()); - sb2.append(" [label=\"then\\n" + ccur.getThenFlowRule() + "\"];\n"); - if (!visited.contains(thenSuccessor)) { - visited.add(thenSuccessor); - worklist.add(thenSuccessor); - } - Block elseSuccessor = ccur.getElseSuccessor(); - sb2.append(" " + ccur.getId() + " -> " - + elseSuccessor.getId()); - sb2.append(" [label=\"else\\n" + ccur.getElseFlowRule() + "\"];\n"); - if (!visited.contains(elseSuccessor)) { - visited.add(elseSuccessor); - worklist.add(elseSuccessor); - } - } else { - assert cur instanceof SingleSuccessorBlock; - Block b = ((SingleSuccessorBlock) cur).getSuccessor(); - if (b != null) { - sb2.append(" " + cur.getId() + " -> " + b.getId()); - sb2.append(" [label=\"" + ((SingleSuccessorBlock) cur).getFlowRule() + "\"];\n"); - if (!visited.contains(b)) { - visited.add(b); - worklist.add(b); - } - } - } - - // exceptional edges - if (cur.getType() == BlockType.EXCEPTION_BLOCK) { - ExceptionBlock ecur = (ExceptionBlock) cur; - for (Entry<TypeMirror, Set<Block>> e : ecur - .getExceptionalSuccessors().entrySet()) { - Set<Block> blocks = e.getValue(); - TypeMirror cause = e.getKey(); - String exception = cause.toString(); - if (exception.startsWith("java.lang.")) { - exception = exception.replace("java.lang.", ""); - } - - for (Block b : blocks) { - sb2.append(" " + cur.getId() + " -> " + b.getId()); - sb2.append(" [label=\"" + exception + "\"];\n"); - if (!visited.contains(b)) { - visited.add(b); - worklist.add(b); - } - } - } - } - - cur = worklist.poll(); - } - - IdentityHashMap<Block, List<Integer>> processOrder = getProcessOrder(cfg); - - // definition of all nodes including their labels - for (Block v : visited) { - sb1.append(" " + v.getId() + " ["); - if (v.getType() == BlockType.CONDITIONAL_BLOCK) { - sb1.append("shape=polygon sides=8 "); - } else if (v.getType() == BlockType.SPECIAL_BLOCK) { - sb1.append("shape=oval "); - } - sb1.append("label=\""); - if (verbose) { - sb1.append("Process order: " + processOrder.get(v).toString().replaceAll("[\\[\\]]", "") + "\\n"); - } - sb1.append(visualizeContent(v, analysis, verbose).replace("\\n", "\\l") - + " \",];\n"); - } - - sb1.append("\n"); - sb1.append(sb2); - - // footer - sb1.append("}\n"); - - return sb1.toString(); - } - - private static IdentityHashMap<Block, List<Integer>> getProcessOrder(ControlFlowGraph cfg) { - IdentityHashMap<Block, List<Integer>> depthFirstOrder = new IdentityHashMap<>(); - int count = 1; - for (Block b : cfg.getDepthFirstOrderedBlocks()) { - if (depthFirstOrder.get(b) == null) { - depthFirstOrder.put(b, new ArrayList<Integer>()); - } - depthFirstOrder.get(b).add(count++); - } - return depthFirstOrder; - } - - /** - * Produce a string representation of the contests of a basic block. - * - * @param bb - * Basic block to visualize. - * @return String representation. - */ - protected static <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> String visualizeContent( - Block bb, - /*@Nullable*/ Analysis<A, S, T> analysis, - boolean verbose) { - - StringBuilder sb = new StringBuilder(); - - // loop over contents - List<Node> contents = new LinkedList<>(); - switch (bb.getType()) { - case REGULAR_BLOCK: - contents.addAll(((RegularBlock) bb).getContents()); - break; - case EXCEPTION_BLOCK: - contents.add(((ExceptionBlock) bb).getNode()); - break; - case CONDITIONAL_BLOCK: - break; - case SPECIAL_BLOCK: - break; - default: - assert false : "All types of basic blocks covered"; - } - boolean notFirst = false; - for (Node t : contents) { - if (notFirst) { - sb.append("\\n"); - } - notFirst = true; - sb.append(prepareString(visualizeNode(t, analysis))); - } - - // handle case where no contents are present - boolean centered = false; - if (sb.length() == 0) { - centered = true; - if (bb.getType() == BlockType.SPECIAL_BLOCK) { - SpecialBlock sbb = (SpecialBlock) bb; - switch (sbb.getSpecialType()) { - case ENTRY: - sb.append("<entry>"); - break; - case EXIT: - sb.append("<exit>"); - break; - case EXCEPTIONAL_EXIT: - sb.append("<exceptional-exit>"); - break; - } - } else if (bb.getType() == BlockType.CONDITIONAL_BLOCK) { - return ""; - } else { - return "?? empty ??"; - } - } - - // visualize transfer input if necessary - if (analysis != null) { - TransferInput<A, S> input = analysis.getInput(bb); - StringBuilder sb2 = new StringBuilder(); - - // split input representation to two lines - String s = input.toDOToutput().replace("}, else={", "}\\nelse={"); - sb2.append("Before:"); - sb2.append(s.subSequence(1, s.length() - 1)); - - // separator - sb2.append("\\n~~~~~~~~~\\n"); - sb2.append(sb); - sb = sb2; - - if (verbose) { - Node lastNode = null; - switch (bb.getType()) { - case REGULAR_BLOCK: - List<Node> blockContents = ((RegularBlock) bb).getContents(); - lastNode = contents.get(blockContents.size() - 1); - break; - case EXCEPTION_BLOCK: - lastNode = ((ExceptionBlock) bb).getNode(); - break; - } - if (lastNode != null) { - sb2.append("\\n~~~~~~~~~\\n"); - s = analysis.getResult().getStoreAfter(lastNode.getTree()). - toDOToutput().replace("}, else={", "}\\nelse={"); - sb2.append("After:"); - sb2.append(s); - } - } - } - - return sb.toString() + (centered ? "" : "\\n"); - } - - protected static <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> - String visualizeNode(Node t, /*@Nullable*/ Analysis<A, S, T> analysis) { - A value = analysis.getValue(t); - String valueInfo = ""; - if (value != null) { - valueInfo = " > " + value.toString(); - } - return t.toString() + " [ " + visualizeType(t) + " ]" + valueInfo; - } - - protected static String visualizeType(Node t) { - String name = t.getClass().getSimpleName(); - return name.replace("Node", ""); - } - - protected static String prepareString(String s) { - return s.replace("\"", "\\\""); - } -} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGVisualizer.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGVisualizer.java new file mode 100644 index 0000000000..9378d83726 --- /dev/null +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGVisualizer.java @@ -0,0 +1,167 @@ +package org.checkerframework.dataflow.cfg; + +/*>>> +import org.checkerframework.checker.nullness.qual.Nullable; +*/ + +import java.util.Map; +import org.checkerframework.dataflow.analysis.AbstractValue; +import org.checkerframework.dataflow.analysis.Analysis; +import org.checkerframework.dataflow.analysis.FlowExpressions; +import org.checkerframework.dataflow.analysis.Store; +import org.checkerframework.dataflow.analysis.TransferFunction; +import org.checkerframework.dataflow.cfg.block.Block; +import org.checkerframework.dataflow.cfg.block.SpecialBlock; +import org.checkerframework.dataflow.cfg.node.Node; + +/** + * Perform some visualization on a control flow graph. The particular operations depend on the + * implementation. + */ +public interface CFGVisualizer< + A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> { + /** + * Initialization method guaranteed to be called once before the first invocation of {@link + * visualize}. + * + * @param args implementation-dependent options + */ + void init(Map<String, Object> args); + + /** + * Output a visualization representing the control flow graph starting at {@code entry}. The + * concrete actions are implementation dependent. + * + * <p>An invocation {@code visualize(cfg, entry, null);} does not output stores at the beginning + * of basic blocks. + * + * @param cfg the CFG to visualize + * @param entry the entry node of the control flow graph to be represented + * @param analysis an analysis containing information about the program represented by the CFG. + * The information includes {@link Store}s that are valid at the beginning of basic blocks + * reachable from {@code entry} and per-node information for value producing {@link Node}s. + * Can also be {@code null} to indicate that this information should not be output. + * @return possible analysis results, e.g. generated file names. + */ + /*@Nullable*/ Map<String, Object> visualize( + ControlFlowGraph cfg, Block entry, /*@Nullable*/ Analysis<A, S, T> analysis); + + /** + * Delegate the visualization responsibility to the passed {@link Store} instance, which will + * call back to this visualizer instance for sub-components. + * + * @param store the store to visualize + */ + void visualizeStore(S store); + + /** + * Called by a {@code CFAbstractStore} to visualize the class name before calling the {@code + * CFAbstractStore#internalVisualize()} method. + * + * @param classCanonicalName the canonical name of the class + */ + void visualizeStoreHeader(String classCanonicalName); + + /** + * Called by {@code CFAbstractStore#internalVisualize()} to visualize a local variable. + * + * @param localVar the local variable + * @param value the value of the local variable + */ + void visualizeStoreLocalVar(FlowExpressions.LocalVariable localVar, A value); + + /** + * Called by {@code CFAbstractStore#internalVisualize()} to visualize the value of the current + * object {@code this} in this Store. + * + * @param value the value of the current object this + */ + void visualizeStoreThisVal(A value); + + /** + * Called by {@code CFAbstractStore#internalVisualize()} to visualize the value of fields + * collected by this Store. + * + * @param fieldAccess the field + * @param value the value of the field + */ + void visualizeStoreFieldVals(FlowExpressions.FieldAccess fieldAccess, A value); + + /** + * Called by {@code CFAbstractStore#internalVisualize()} to visualize the value of arrays + * collected by this Store. + * + * @param arrayValue the array + * @param value the value of the array + */ + void visualizeStoreArrayVal(FlowExpressions.ArrayAccess arrayValue, A value); + + /** + * Called by {@code CFAbstractStore#internalVisualize()} to visualize the value of pure method + * calls collected by this Store. + * + * @param methodCall the pure method call + * @param value the value of the pure method call + */ + void visualizeStoreMethodVals(FlowExpressions.MethodCall methodCall, A value); + + /** + * Called by {@code CFAbstractStore#internalVisualize()} to visualize the value of class names + * collected by this Store. + * + * @param className the class name + * @param value the value of the class name + */ + void visualizeStoreClassVals(FlowExpressions.ClassName className, A value); + + /** + * Called by {@code CFAbstractStore#internalVisualize()} to visualize the specific information + * collected according to the specific kind of Store. Currently, these Stores call this method: + * {@code LockStore}, {@code NullnessStore}, and {@code InitializationStore} to visualize + * additional information. + * + * @param keyName the name of the specific information to be visualized + * @param value the value of the specific information to be visualized + */ + void visualizeStoreKeyVal(String keyName, Object value); + + /** + * Called by {@code CFAbstractStore} to visualize any information after the invocation of {@code + * CFAbstractStore#internalVisualize()}. + */ + void visualizeStoreFooter(); + + /** + * Visualize a block based on the analysis. + * + * @param bb the block + * @param analysis the current analysis + */ + void visualizeBlock(Block bb, /*@Nullable*/ Analysis<A, S, T> analysis); + + /** + * Visualize a SpecialBlock. + * + * @param sbb the special block + */ + void visualizeSpecialBlock(SpecialBlock sbb); + + /** + * Visualize the transferInput of a Block based on the analysis. + * + * @param bb the block + * @param analysis the current analysis + */ + void visualizeBlockTransferInput(Block bb, Analysis<A, S, T> analysis); + + /** + * Visualize a Node based on the analysis. + * + * @param t the node + * @param analysis the current analysis + */ + void visualizeBlockNode(Node t, /*@Nullable*/ Analysis<A, S, T> analysis); + + /** Shutdown method called once from the shutdown hook of the {@code BaseTypeChecker}. */ + void shutdown(); +} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/ControlFlowGraph.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/ControlFlowGraph.java index 759d2616d0..0a2687aec2 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/ControlFlowGraph.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/ControlFlowGraph.java @@ -7,6 +7,14 @@ import org.checkerframework.checker.nullness.qual.Nullable; import com.sun.source.tree.ClassTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.Tree; +import java.util.Collections; +import java.util.Deque; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; import org.checkerframework.dataflow.cfg.block.Block; import org.checkerframework.dataflow.cfg.block.Block.BlockType; import org.checkerframework.dataflow.cfg.block.ConditionalBlock; @@ -17,20 +25,10 @@ import org.checkerframework.dataflow.cfg.block.SpecialBlockImpl; import org.checkerframework.dataflow.cfg.node.Node; import org.checkerframework.dataflow.cfg.node.ReturnNode; -import java.util.Collections; -import java.util.Deque; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; -import java.util.Set; - /** * A control flow graph (CFG for short) of a single method. * * @author Stefan Heule - * */ public class ControlFlowGraph { @@ -47,12 +45,10 @@ public class ControlFlowGraph { protected UnderlyingAST underlyingAST; /** - * 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 treeLookup, while the Node for the post-conversion value - * is stored in convertedTreeLookup. + * 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 treeLookup, + * while the Node for the post-conversion value is stored in convertedTreeLookup. */ protected IdentityHashMap<Tree, Node> treeLookup; @@ -60,12 +56,16 @@ public class ControlFlowGraph { protected IdentityHashMap<Tree, Node> convertedTreeLookup; /** - * All return nodes (if any) encountered. Only includes return - * statements that actually return something + * All return nodes (if any) encountered. Only includes return statements that actually return + * something */ protected final List<ReturnNode> returnNodes; - public ControlFlowGraph(SpecialBlock entryBlock, SpecialBlockImpl regularExitBlock, SpecialBlockImpl exceptionalExitBlock, UnderlyingAST underlyingAST, + public ControlFlowGraph( + SpecialBlock entryBlock, + SpecialBlockImpl regularExitBlock, + SpecialBlockImpl exceptionalExitBlock, + UnderlyingAST underlyingAST, IdentityHashMap<Tree, Node> treeLookup, IdentityHashMap<Tree, Node> convertedTreeLookup, List<ReturnNode> returnNodes) { @@ -79,10 +79,7 @@ public class ControlFlowGraph { this.returnNodes = returnNodes; } - /** - * @return The {@link Node} to which the {@link Tree} <code>t</code> - * corresponds. - */ + /** @return the {@link Node} to which the {@link Tree} {@code t} corresponds. */ public Node getNodeCorrespondingToTree(Tree t) { if (convertedTreeLookup.containsKey(t)) { return convertedTreeLookup.get(t); @@ -91,7 +88,7 @@ public class ControlFlowGraph { } } - /** @return The entry block of the control flow graph. */ + /** @return the entry block of the control flow graph. */ public SpecialBlock getEntryBlock() { return entryBlock; } @@ -108,14 +105,12 @@ public class ControlFlowGraph { return exceptionalExitBlock; } - /** @return The AST this CFG corresponds to. */ + /** @return the AST this CFG corresponds to. */ public UnderlyingAST getUnderlyingAST() { return underlyingAST; } - /** - * @return The set of all basic block in this control flow graph. - */ + /** @return the set of all basic block in this control flow graph */ public Set<Block> getAllBlocks() { Set<Block> visited = new HashSet<>(); Queue<Block> worklist = new LinkedList<>(); @@ -124,8 +119,9 @@ public class ControlFlowGraph { // traverse the whole control flow graph while (true) { - if (cur == null) + if (cur == null) { break; + } Queue<Block> succs = new LinkedList<>(); if (cur.getType() == BlockType.CONDITIONAL_BLOCK) { @@ -161,10 +157,9 @@ public class ControlFlowGraph { } /** - * @return The list of all basic block in this control flow graph - * in reversed depth-first postorder sequence. - * - * Blocks may appear more than once in the sequence. + * @return the list of all basic block in this control flow graph in reversed depth-first + * postorder sequence. + * <p>Blocks may appear more than once in the sequence. */ public List<Block> getDepthFirstOrderedBlocks() { List<Block> dfsOrderResult = new LinkedList<>(); @@ -190,8 +185,8 @@ public class ControlFlowGraph { /** * Get a list of all successor Blocks for cur - * @param cur - * @return A Deque of successor Blocks + * + * @return a Deque of successor Blocks */ private Deque<Block> getSuccessors(Block cur) { Deque<Block> succs = new LinkedList<>(); @@ -216,16 +211,14 @@ public class ControlFlowGraph { return succs; } - /** - * @return The tree-lookup map. - */ + /** @return the tree-lookup map */ public IdentityHashMap<Tree, Node> getTreeLookup() { return new IdentityHashMap<>(treeLookup); } /** - * Get the {@link MethodTree} of the CFG if the argument {@link Tree} maps - * to a {@link Node} in the CFG or null otherwise. + * Get the {@link MethodTree} of the CFG if the argument {@link Tree} maps to a {@link Node} in + * the CFG or null otherwise. */ public /*@Nullable*/ MethodTree getContainingMethod(Tree t) { if (treeLookup.containsKey(t)) { @@ -238,8 +231,8 @@ public class ControlFlowGraph { } /** - * Get the {@link ClassTree} of the CFG if the argument {@link Tree} maps - * to a {@link Node} in the CFG or null otherwise. + * Get the {@link ClassTree} of the CFG if the argument {@link Tree} maps to a {@link Node} in + * the CFG or null otherwise. */ public /*@Nullable*/ ClassTree getContainingClass(Tree t) { if (treeLookup.containsKey(t)) { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/DOTCFGVisualizer.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/DOTCFGVisualizer.java new file mode 100644 index 0000000000..1eb1cb87a2 --- /dev/null +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/DOTCFGVisualizer.java @@ -0,0 +1,511 @@ +package org.checkerframework.dataflow.cfg; + +/*>>> +import org.checkerframework.checker.nullness.qual.Nullable; +*/ + +import com.sun.tools.javac.tree.JCTree; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +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.Queue; +import java.util.Set; +import javax.lang.model.type.TypeMirror; +import org.checkerframework.dataflow.analysis.AbstractValue; +import org.checkerframework.dataflow.analysis.Analysis; +import org.checkerframework.dataflow.analysis.FlowExpressions; +import org.checkerframework.dataflow.analysis.Store; +import org.checkerframework.dataflow.analysis.TransferFunction; +import org.checkerframework.dataflow.analysis.TransferInput; +import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGMethod; +import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGStatement; +import org.checkerframework.dataflow.cfg.block.Block; +import org.checkerframework.dataflow.cfg.block.Block.BlockType; +import org.checkerframework.dataflow.cfg.block.ConditionalBlock; +import org.checkerframework.dataflow.cfg.block.ExceptionBlock; +import org.checkerframework.dataflow.cfg.block.RegularBlock; +import org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock; +import org.checkerframework.dataflow.cfg.block.SpecialBlock; +import org.checkerframework.dataflow.cfg.node.Node; +import org.checkerframework.javacutil.ErrorReporter; + +/** + * Generate a graph description in the DOT language of a control graph. + * + * @author Stefan Heule + */ +public class DOTCFGVisualizer< + A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> + implements CFGVisualizer<A, S, T> { + + protected String outdir; + protected boolean verbose; + protected String checkerName; + + protected StringBuilder sbDigraph; + protected StringBuilder sbStore; + protected StringBuilder sbBlock; + + /** Mapping from class/method representation to generated dot file. */ + protected Map<String, String> generated; + + @Override + public void init(Map<String, Object> args) { + this.outdir = (String) args.get("outdir"); + { + Object verb = args.get("verbose"); + this.verbose = + verb == null + ? false + : verb instanceof String + ? Boolean.getBoolean((String) verb) + : (boolean) verb; + } + this.checkerName = (String) args.get("checkerName"); + + this.generated = new HashMap<>(); + + this.sbDigraph = new StringBuilder(); + + this.sbStore = new StringBuilder(); + + this.sbBlock = new StringBuilder(); + } + + /** {@inheritDoc} */ + @Override + public /*@Nullable*/ Map<String, Object> visualize( + ControlFlowGraph cfg, Block entry, /*@Nullable*/ Analysis<A, S, T> analysis) { + + String dotgraph = generateDotGraph(cfg, entry, analysis); + + String dotfilename = dotOutputFileName(cfg.underlyingAST); + // System.err.println("Output to DOT file: " + dotfilename); + + try { + FileWriter fstream = new FileWriter(dotfilename); + BufferedWriter out = new BufferedWriter(fstream); + out.write(dotgraph); + out.close(); + } catch (IOException e) { + ErrorReporter.errorAbort( + "Error creating dot file: " + dotfilename + "; ensure the path is valid", e); + } + + Map<String, Object> res = new HashMap<>(); + res.put("dotFileName", dotfilename); + + return res; + } + + /** Generate the dot representation as String. */ + protected String generateDotGraph( + ControlFlowGraph cfg, Block entry, /*@Nullable*/ Analysis<A, S, T> analysis) { + this.sbDigraph.setLength(0); + Set<Block> visited = new HashSet<>(); + + // header + this.sbDigraph.append("digraph {\n"); + + Block cur = entry; + Queue<Block> worklist = new LinkedList<>(); + visited.add(entry); + // traverse control flow graph and define all arrows + while (true) { + if (cur == null) { + break; + } + + if (cur.getType() == BlockType.CONDITIONAL_BLOCK) { + ConditionalBlock ccur = ((ConditionalBlock) cur); + Block thenSuccessor = ccur.getThenSuccessor(); + addDotEdge(ccur.getId(), thenSuccessor.getId(), "then\\n" + ccur.getThenFlowRule()); + if (!visited.contains(thenSuccessor)) { + visited.add(thenSuccessor); + worklist.add(thenSuccessor); + } + Block elseSuccessor = ccur.getElseSuccessor(); + addDotEdge(ccur.getId(), elseSuccessor.getId(), "else\\n" + ccur.getElseFlowRule()); + if (!visited.contains(elseSuccessor)) { + visited.add(elseSuccessor); + worklist.add(elseSuccessor); + } + } else { + assert cur instanceof SingleSuccessorBlock; + Block b = ((SingleSuccessorBlock) cur).getSuccessor(); + if (b != null) { + addDotEdge( + cur.getId(), + b.getId(), + ((SingleSuccessorBlock) cur).getFlowRule().name()); + if (!visited.contains(b)) { + visited.add(b); + worklist.add(b); + } + } + } + + // exceptional edges + if (cur.getType() == BlockType.EXCEPTION_BLOCK) { + ExceptionBlock ecur = (ExceptionBlock) cur; + for (Entry<TypeMirror, Set<Block>> e : ecur.getExceptionalSuccessors().entrySet()) { + Set<Block> blocks = e.getValue(); + TypeMirror cause = e.getKey(); + String exception = cause.toString(); + if (exception.startsWith("java.lang.")) { + exception = exception.replace("java.lang.", ""); + } + + for (Block b : blocks) { + addDotEdge(cur.getId(), b.getId(), exception); + if (!visited.contains(b)) { + visited.add(b); + worklist.add(b); + } + } + } + } + + cur = worklist.poll(); + } + + generateDotNodes(visited, cfg, analysis); + + // footer + this.sbDigraph.append("}\n"); + + return this.sbDigraph.toString(); + } + + protected void generateDotNodes( + Set<Block> visited, ControlFlowGraph cfg, /*@Nullable*/ Analysis<A, S, T> analysis) { + IdentityHashMap<Block, List<Integer>> processOrder = getProcessOrder(cfg); + this.sbDigraph.append(" node [shape=rectangle];\n\n"); + // definition of all nodes including their labels + for (Block v : visited) { + this.sbDigraph.append(" " + v.getId() + " ["); + if (v.getType() == BlockType.CONDITIONAL_BLOCK) { + this.sbDigraph.append("shape=polygon sides=8 "); + } else if (v.getType() == BlockType.SPECIAL_BLOCK) { + this.sbDigraph.append("shape=oval "); + } + this.sbDigraph.append("label=\""); + if (verbose) { + this.sbDigraph.append( + "Process order: " + + processOrder.get(v).toString().replaceAll("[\\[\\]]", "") + + "\\n"); + } + visualizeBlock(v, analysis); + } + + this.sbDigraph.append("\n"); + } + + /** @return the file name used for DOT output. */ + protected String dotOutputFileName(UnderlyingAST ast) { + StringBuilder srcloc = new StringBuilder(); + + StringBuilder outfile = new StringBuilder(outdir); + outfile.append('/'); + if (ast.getKind() == UnderlyingAST.Kind.ARBITRARY_CODE) { + CFGStatement cfgs = (CFGStatement) ast; + String clsname = cfgs.getClassTree().getSimpleName().toString(); + outfile.append(clsname); + outfile.append("-initializer-"); + outfile.append(ast.hashCode()); + + srcloc.append('<'); + srcloc.append(clsname); + srcloc.append("::initializer::"); + srcloc.append(((JCTree) cfgs.getCode()).pos); + srcloc.append('>'); + } else if (ast.getKind() == UnderlyingAST.Kind.METHOD) { + CFGMethod cfgm = (CFGMethod) ast; + String clsname = cfgm.getClassTree().getSimpleName().toString(); + String methname = cfgm.getMethod().getName().toString(); + outfile.append(clsname); + outfile.append('-'); + outfile.append(methname); + + srcloc.append('<'); + srcloc.append(clsname); + srcloc.append("::"); + srcloc.append(methname); + srcloc.append('('); + srcloc.append(cfgm.getMethod().getParameters()); + srcloc.append(")::"); + srcloc.append(((JCTree) cfgm.getMethod()).pos); + srcloc.append('>'); + } else { + ErrorReporter.errorAbort( + "Unexpected AST kind: " + ast.getKind() + " value: " + ast.toString()); + return null; + } + outfile.append('-'); + outfile.append(checkerName); + outfile.append(".dot"); + + // make path safe for Windows + String out = outfile.toString().replace("<", "_").replace(">", ""); + + generated.put(srcloc.toString(), out); + + return out; + } + + protected IdentityHashMap<Block, List<Integer>> getProcessOrder(ControlFlowGraph cfg) { + IdentityHashMap<Block, List<Integer>> depthFirstOrder = new IdentityHashMap<>(); + int count = 1; + for (Block b : cfg.getDepthFirstOrderedBlocks()) { + if (depthFirstOrder.get(b) == null) { + depthFirstOrder.put(b, new ArrayList<Integer>()); + } + depthFirstOrder.get(b).add(count++); + } + return depthFirstOrder; + } + + /** + * Produce a representation of the contests of a basic block. + * + * @param bb basic block to visualize + */ + @Override + public void visualizeBlock(Block bb, /*@Nullable*/ Analysis<A, S, T> analysis) { + + this.sbBlock.setLength(0); + + // loop over contents + List<Node> contents = new LinkedList<>(); + switch (bb.getType()) { + case REGULAR_BLOCK: + contents.addAll(((RegularBlock) bb).getContents()); + break; + case EXCEPTION_BLOCK: + contents.add(((ExceptionBlock) bb).getNode()); + break; + case CONDITIONAL_BLOCK: + break; + case SPECIAL_BLOCK: + break; + default: + assert false : "All types of basic blocks covered"; + } + boolean notFirst = false; + for (Node t : contents) { + if (notFirst) { + this.sbBlock.append("\\n"); + } + notFirst = true; + visualizeBlockNode(t, analysis); + } + + // handle case where no contents are present + boolean centered = false; + if (this.sbBlock.length() == 0) { + centered = true; + if (bb.getType() == BlockType.SPECIAL_BLOCK) { + visualizeSpecialBlock((SpecialBlock) bb); + } else if (bb.getType() == BlockType.CONDITIONAL_BLOCK) { + this.sbDigraph.append(" \",];\n"); + return; + } else { + this.sbDigraph.append("?? empty ?? \",];\n"); + return; + } + } + + // visualize transfer input if necessary + if (analysis != null) { + visualizeBlockTransferInput(bb, analysis); + } + + this.sbDigraph.append( + (this.sbBlock.toString() + (centered ? "" : "\\n")).replace("\\n", "\\l") + + " \",];\n"); + } + + @Override + public void visualizeSpecialBlock(SpecialBlock sbb) { + switch (sbb.getSpecialType()) { + case ENTRY: + this.sbBlock.append("<entry>"); + break; + case EXIT: + this.sbBlock.append("<exit>"); + break; + case EXCEPTIONAL_EXIT: + this.sbBlock.append("<exceptional-exit>"); + break; + } + } + + @Override + public void visualizeBlockTransferInput(Block bb, Analysis<A, S, T> analysis) { + assert analysis != null + : "analysis should be non-null when visualizing the transfer input of a block."; + + TransferInput<A, S> input = analysis.getInput(bb); + this.sbStore.setLength(0); + + // split input representation to two lines + this.sbStore.append("Before:"); + S thenStore = input.getThenStore(); + if (!input.containsTwoStores()) { + S regularStore = input.getRegularStore(); + this.sbStore.append('['); + visualizeStore(regularStore); + this.sbStore.append(']'); + } else { + S elseStore = input.getElseStore(); + this.sbStore.append("[then="); + visualizeStore(thenStore); + this.sbStore.append(", else="); + visualizeStore(elseStore); + this.sbStore.append("]"); + } + // separator + this.sbStore.append("\\n~~~~~~~~~\\n"); + + // the transfer input before this block is added before the block content + this.sbBlock.insert(0, this.sbStore); + + if (verbose) { + Node lastNode; + switch (bb.getType()) { + case REGULAR_BLOCK: + List<Node> blockContents = ((RegularBlock) bb).getContents(); + lastNode = blockContents.get(blockContents.size() - 1); + break; + case EXCEPTION_BLOCK: + lastNode = ((ExceptionBlock) bb).getNode(); + break; + default: + lastNode = null; + } + if (lastNode != null) { + this.sbStore.setLength(0); + this.sbStore.append("\\n~~~~~~~~~\\n"); + this.sbStore.append("After:"); + visualizeStore(analysis.getResult().getStoreAfter(lastNode)); + this.sbBlock.append(this.sbStore); + } + } + } + + @Override + public void visualizeBlockNode(Node t, /*@Nullable*/ Analysis<A, S, T> analysis) { + this.sbBlock.append(prepareString(t.toString()) + " [ " + prepareNodeType(t) + " ]"); + if (analysis != null) { + A value = analysis.getValue(t); + if (value != null) { + this.sbBlock.append(" > " + prepareString(value.toString())); + } + } + } + + protected String prepareNodeType(Node t) { + String name = t.getClass().getSimpleName(); + return name.replace("Node", ""); + } + + protected String prepareString(String s) { + return s.replace("\"", "\\\""); + } + + protected void addDotEdge(long sId, long eId, String labelContent) { + this.sbDigraph.append(" " + sId + " -> " + eId + " [label=\"" + labelContent + "\"];\n"); + } + + @Override + public void visualizeStore(S store) { + store.visualize(this); + } + + @Override + public void visualizeStoreThisVal(A value) { + this.sbStore.append(" this > " + value + "\\n"); + } + + @Override + public void visualizeStoreLocalVar(FlowExpressions.LocalVariable localVar, A value) { + this.sbStore.append(" " + localVar + " > " + toStringEscapeDoubleQuotes(value) + "\\n"); + } + + @Override + public void visualizeStoreFieldVals(FlowExpressions.FieldAccess fieldAccess, A value) { + this.sbStore.append(" " + fieldAccess + " > " + toStringEscapeDoubleQuotes(value) + "\\n"); + } + + @Override + public void visualizeStoreArrayVal(FlowExpressions.ArrayAccess arrayValue, A value) { + this.sbStore.append(" " + arrayValue + " > " + toStringEscapeDoubleQuotes(value) + "\\n"); + } + + @Override + public void visualizeStoreMethodVals(FlowExpressions.MethodCall methodCall, A value) { + this.sbStore.append( + " " + methodCall.toString().replace("\"", "\\\"") + " > " + value + "\\n"); + } + + @Override + public void visualizeStoreClassVals(FlowExpressions.ClassName className, A value) { + this.sbStore.append(" " + className + " > " + toStringEscapeDoubleQuotes(value) + "\\n"); + } + + @Override + public void visualizeStoreKeyVal(String keyName, Object value) { + this.sbStore.append(" " + keyName + " = " + value + "\\n"); + } + + protected String escapeDoubleQuotes(final String str) { + return str.replace("\"", "\\\""); + } + + protected String toStringEscapeDoubleQuotes(final Object obj) { + return escapeDoubleQuotes(String.valueOf(obj)); + } + + @Override + public void visualizeStoreHeader(String classCanonicalName) { + this.sbStore.append(classCanonicalName + " (\\n"); + } + + @Override + public void visualizeStoreFooter() { + this.sbStore.append(")"); + } + + /** + * Write a file {@code methods.txt} that contains a mapping from source code location to + * generated dot file. + */ + @Override + public void shutdown() { + try { + // Open for append, in case of multiple sub-checkers. + FileWriter fstream = new FileWriter(outdir + "/methods.txt", true); + BufferedWriter out = new BufferedWriter(fstream); + for (Map.Entry<String, String> kv : generated.entrySet()) { + out.write(kv.getKey()); + out.append('\t'); + out.write(kv.getValue()); + out.append('\n'); + } + out.close(); + } catch (IOException e) { + ErrorReporter.errorAbort( + "Error creating methods.txt file in: " + outdir + "; ensure the path is valid", + e); + } + } +} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/JavaSource2CFGDOT.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/JavaSource2CFGDOT.java index 8d5ee1c130..fa6ff55dc5 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/JavaSource2CFGDOT.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/JavaSource2CFGDOT.java @@ -4,38 +4,34 @@ package org.checkerframework.dataflow.cfg; import org.checkerframework.checker.nullness.qual.Nullable; */ -import org.checkerframework.dataflow.analysis.AbstractValue; -import org.checkerframework.dataflow.analysis.Analysis; -import org.checkerframework.dataflow.analysis.Store; -import org.checkerframework.dataflow.analysis.TransferFunction; - -import org.checkerframework.javacutil.BasicTypeProcessor; -import org.checkerframework.javacutil.TreeUtils; - -import java.io.BufferedWriter; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.util.TreePathScanner; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Options; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; import java.util.Map.Entry; - import javax.lang.model.element.ExecutableElement; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.xml.ws.Holder; - -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.MethodTree; -import com.sun.source.util.TreePathScanner; -import com.sun.tools.javac.file.JavacFileManager; -import com.sun.tools.javac.main.JavaCompiler; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.List; +import org.checkerframework.dataflow.analysis.AbstractValue; +import org.checkerframework.dataflow.analysis.Analysis; +import org.checkerframework.dataflow.analysis.Store; +import org.checkerframework.dataflow.analysis.TransferFunction; +import org.checkerframework.javacutil.BasicTypeProcessor; +import org.checkerframework.javacutil.TreeUtils; /** - * Class to generate the DOT representation of the control flow graph of a given - * method. + * Class to generate the DOT representation of the control flow graph of a given method. * * @author Stefan Heule */ @@ -79,7 +75,7 @@ public class JavaSource2CFGDOT { i++; clas = args[i]; } else { - printError("Unknown command line argument: " + args[i]); + printError("Unknown command-line argument: " + args[i]); error = true; } } @@ -98,50 +94,56 @@ public class JavaSource2CFGDOT { /** Print usage information. */ protected static void printUsage() { - System.out - .println("Generate the control flow graph of a Java method, represented as a DOT graph."); - System.out - .println("Parameters: <inputfile> <outputfile> [-method <name>] [-class <name>] [-pdf]"); - System.out - .println(" -pdf: Also generate the PDF by invoking 'dot'."); - System.out - .println(" -method: The method to generate the CFG for (defaults to 'test')."); - System.out - .println(" -class: The class in which to find the method (defaults to 'Test')."); + System.out.println( + "Generate the control flow graph of a Java method, represented as a DOT graph."); + System.out.println( + "Parameters: <inputfile> <outputdir> [-method <name>] [-class <name>] [-pdf]"); + System.out.println(" -pdf: Also generate the PDF by invoking 'dot'."); + System.out.println(" -method: The method to generate the CFG for (defaults to 'test')."); + System.out.println( + " -class: The class in which to find the method (defaults to 'Test')."); } /** Just like method above but without analysis. */ - public static void generateDOTofCFG(String inputFile, String outputFile, - String method, String clas, boolean pdf) { - generateDOTofCFG(inputFile, outputFile, method, clas, pdf, null); + public static void generateDOTofCFG( + String inputFile, String outputDir, String method, String clas, boolean pdf) { + generateDOTofCFG(inputFile, outputDir, method, clas, pdf, null); } /** * Generate the DOT representation of the CFG for a method. * - * @param inputFile - * Java source input file. - * @param outputFile - * Source output file (without file extension) - * @param method - * Method name to generate the CFG for. - * @param pdf - * Also generate a PDF? - * @param analysis - * Analysis to perform befor the visualization (or - * <code>null</code> if no analysis is to be performed). + * @param inputFile java source input file + * @param outputDir source output directory + * @param method method name to generate the CFG for + * @param pdf also generate a PDF? + * @param analysis analysis to perform befor the visualization (or {@code null} if no analysis + * is to be performed). */ - public static <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> void generateDOTofCFG( - String inputFile, String outputFile, String method, String clas, - boolean pdf, /*@Nullable*/ Analysis<A, S, T> analysis) { - Entry<MethodTree, CompilationUnitTree> m = getMethodTreeAndCompilationUnit(inputFile, method, clas); - generateDOTofCFG(inputFile, outputFile, method, clas, pdf, analysis, m.getKey(), m.getValue()); + public static <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> + void generateDOTofCFG( + String inputFile, + String outputDir, + String method, + String clas, + boolean pdf, + /*@Nullable*/ Analysis<A, S, T> analysis) { + Entry<MethodTree, CompilationUnitTree> m = + getMethodTreeAndCompilationUnit(inputFile, method, clas); + generateDOTofCFG( + inputFile, outputDir, method, clas, pdf, analysis, m.getKey(), m.getValue()); } - public static <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> void generateDOTofCFG( - String inputFile, String outputFile, String method, String clas, - boolean pdf, /*@Nullable*/ Analysis<A, S, T> analysis, MethodTree m, - CompilationUnitTree r) { + public static <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> + void generateDOTofCFG( + String inputFile, + String outputDir, + String method, + String clas, + boolean pdf, + /*@Nullable*/ Analysis<A, S, T> analysis, + MethodTree m, + CompilationUnitTree r) { String fileName = (new File(inputFile)).getName(); System.out.println("Working on " + fileName + "..."); @@ -154,31 +156,25 @@ public class JavaSource2CFGDOT { if (analysis != null) { analysis.performAnalysis(cfg); } - String s = CFGDOTVisualizer.visualize(cfg, cfg.getEntryBlock(), analysis, false); - try { - FileWriter fstream = new FileWriter(outputFile + ".txt"); - BufferedWriter out = new BufferedWriter(fstream); - out.write(s); - System.out.println("Finished " + fileName + "."); - out.close(); - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } + Map<String, Object> args = new HashMap<>(); + args.put("outdir", outputDir); + args.put("checkerName", ""); + + CFGVisualizer<A, S, T> viz = new DOTCFGVisualizer<A, S, T>(); + viz.init(args); + Map<String, Object> res = viz.visualize(cfg, cfg.getEntryBlock(), analysis); + viz.shutdown(); if (pdf) { - producePDF(outputFile); + producePDF((String) res.get("dotFileName")); } } - /** - * Invoke DOT to generate a PDF. - */ + /** Invoke DOT to generate a PDF. */ protected static void producePDF(String file) { try { - String command = "dot -Tpdf \"" + file + ".txt\" -o \"" + file - + ".pdf\""; + String command = "dot -Tpdf \"" + file + ".txt\" -o \"" + file + ".pdf\""; Process child = Runtime.getRuntime().exec(command); child.waitFor(); } catch (InterruptedException | IOException e) { @@ -188,66 +184,65 @@ public class JavaSource2CFGDOT { } /** - * @return The AST of a specific method in a specific class in a specific - * file (or null if no such method exists). + * @return the AST of a specific method in a specific class in a specific file (or null if no + * such method exists) */ - public static /*@Nullable*/ MethodTree getMethodTree(String file, - final String method, String clas) { + public static /*@Nullable*/ MethodTree getMethodTree( + String file, final String method, String clas) { return getMethodTreeAndCompilationUnit(file, method, clas).getKey(); } /** - * @return The AST of a specific method in a specific class as well as the - * {@link CompilationUnitTree} in a specific file (or null they do - * not exist). + * @return the AST of a specific method in a specific class as well as the {@link + * CompilationUnitTree} in a specific file (or null they do not exist). */ - public static Entry</*@Nullable*/ MethodTree, /*@Nullable*/ CompilationUnitTree> getMethodTreeAndCompilationUnit( - String file, final String method, String clas) { + public static Entry</*@Nullable*/ MethodTree, /*@Nullable*/ CompilationUnitTree> + getMethodTreeAndCompilationUnit(String file, final String method, String clas) { final Holder<MethodTree> m = new Holder<>(); final Holder<CompilationUnitTree> c = new Holder<>(); - BasicTypeProcessor typeProcessor = new BasicTypeProcessor() { - @Override - protected TreePathScanner<?, ?> createTreePathScanner( - CompilationUnitTree root) { - c.value = root; - return new TreePathScanner<Void, Void>() { + BasicTypeProcessor typeProcessor = + new BasicTypeProcessor() { @Override - public Void visitMethod(MethodTree node, Void p) { - ExecutableElement el = TreeUtils - .elementFromDeclaration(node); - if (el.getSimpleName().contentEquals(method)) { - m.value = node; - // stop execution by throwing an exception. this - // makes sure that compilation does not proceed, and - // thus the AST is not modified by further phases of - // the compilation (and we save the work to do the - // compilation). - throw new RuntimeException(); - } - return null; + protected TreePathScanner<?, ?> createTreePathScanner( + CompilationUnitTree root) { + c.value = root; + return new TreePathScanner<Void, Void>() { + @Override + public Void visitMethod(MethodTree node, Void p) { + ExecutableElement el = TreeUtils.elementFromDeclaration(node); + if (el.getSimpleName().contentEquals(method)) { + m.value = node; + // stop execution by throwing an exception. this + // makes sure that compilation does not proceed, and + // thus the AST is not modified by further phases of + // the compilation (and we save the work to do the + // compilation). + throw new RuntimeException(); + } + return null; + } + }; } }; - } - }; Context context = new Context(); + Options.instance(context).put("compilePolicy", "ATTR_ONLY"); JavaCompiler javac = new JavaCompiler(context); - javac.attrParseOnly = true; - JavacFileManager fileManager = (JavacFileManager) context - .get(JavaFileManager.class); + JavacFileManager fileManager = (JavacFileManager) context.get(JavaFileManager.class); - JavaFileObject l = fileManager - .getJavaFileObjectsFromStrings(List.of(file)).iterator().next(); + JavaFileObject l = + fileManager.getJavaFileObjectsFromStrings(List.of(file)).iterator().next(); PrintStream err = System.err; try { // redirect syserr to nothing (and prevent the compiler from issuing // warnings about our exception. - System.setErr(new PrintStream(new OutputStream() { - @Override - public void write(int b) throws IOException { - } - })); + System.setErr( + new PrintStream( + new OutputStream() { + @Override + public void write(int b) throws IOException {} + })); javac.compile(List.of(l), List.of(clas), List.of(typeProcessor)); } catch (Throwable e) { // ok @@ -271,5 +266,4 @@ public class JavaSource2CFGDOT { } }; } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/UnderlyingAST.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/UnderlyingAST.java index 5f61468e8c..dd646f110d 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/UnderlyingAST.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/UnderlyingAST.java @@ -6,11 +6,10 @@ import com.sun.source.tree.MethodTree; import com.sun.source.tree.Tree; /** - * Represents an abstract syntax tree of type {@link Tree} that underlies a - * given control flow graph. + * Represents an abstract syntax tree of type {@link Tree} that underlies a given control flow + * graph. * * @author Stefan Heule - * */ public abstract class UnderlyingAST { public enum Kind { @@ -19,9 +18,7 @@ public abstract class UnderlyingAST { /** The underlying code is a lambda expression */ LAMBDA, - /** - * The underlying code is an arbitrary Java statement or expression - */ + /** The underlying code is an arbitrary Java statement or expression */ ARBITRARY_CODE, } @@ -31,18 +28,14 @@ public abstract class UnderlyingAST { this.kind = kind; } - /** - * @return The code that corresponds to the CFG. - */ - abstract public Tree getCode(); + /** @return the code that corresponds to the CFG */ + public abstract Tree getCode(); public Kind getKind() { return kind; } - /** - * If the underlying AST is a method. - */ + /** If the underlying AST is a method. */ public static class CFGMethod extends UnderlyingAST { /** The method declaration */ @@ -76,9 +69,7 @@ public abstract class UnderlyingAST { } } - /** - * If the underlying AST is a lambda. - */ + /** If the underlying AST is a lambda. */ public static class CFGLambda extends UnderlyingAST { private final LambdaExpressionTree lambda; @@ -103,16 +94,18 @@ public abstract class UnderlyingAST { } } - /** - * If the underlying AST is a statement or expression. - */ + /** If the underlying AST is a statement or expression. */ public static class CFGStatement extends UnderlyingAST { protected final Tree code; - public CFGStatement(Tree code) { + /** The class tree this method belongs to. */ + protected final ClassTree classTree; + + public CFGStatement(Tree code, ClassTree classTree) { super(Kind.ARBITRARY_CODE); this.code = code; + this.classTree = classTree; } @Override @@ -120,6 +113,10 @@ public abstract class UnderlyingAST { return code; } + public ClassTree getClassTree() { + return classTree; + } + @Override public String toString() { return "CFGStatement(\n" + code + "\n)"; diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/Block.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/Block.java index 81edef6b2d..2d58550568 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/Block.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/Block.java @@ -4,7 +4,6 @@ package org.checkerframework.dataflow.cfg.block; * Represents a basic block in a control flow graph. * * @author Stefan Heule - * */ public interface Block { @@ -24,14 +23,9 @@ public interface Block { EXCEPTION_BLOCK, } - /** - * @return The type of this basic block. - */ + /** @return the type of this basic block */ BlockType getType(); - /** - * @return The unique identifier of this block. - */ + /** @return the unique identifier of this block */ long getId(); - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/BlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/BlockImpl.java index c3df418ecc..0421a1e3f5 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/BlockImpl.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/BlockImpl.java @@ -6,9 +6,8 @@ import java.util.Set; /** * Base class of the {@link Block} implementation hierarchy. - * + * * @author Stefan Heule - * */ public abstract class BlockImpl implements Block { @@ -24,9 +23,7 @@ public abstract class BlockImpl implements Block { /** The set of predecessors. */ protected Set<BlockImpl> predecessors; - /** - * @return A fresh identifier. - */ + /** @return a fresh identifier */ private static long uniqueID() { return lastId++; } @@ -45,9 +42,7 @@ public abstract class BlockImpl implements Block { return type; } - /** - * @return The list of predecessors of this basic block. - */ + /** @return the list of predecessors of this basic block */ public Set<BlockImpl> getPredecessors() { return Collections.unmodifiableSet(predecessors); } @@ -59,5 +54,4 @@ public abstract class BlockImpl implements Block { public void removePredecessor(BlockImpl pred) { predecessors.remove(pred); } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlock.java index d267c99803..dce04abdfe 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlock.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlock.java @@ -4,45 +4,27 @@ import org.checkerframework.dataflow.analysis.Store; import org.checkerframework.dataflow.cfg.node.Node; /** - * Represents a conditional basic block that contains exactly one boolean - * {@link Node}. + * Represents a conditional basic block that contains exactly one boolean {@link Node}. * * @author Stefan Heule - * */ public interface ConditionalBlock extends Block { - /** - * @return The entry block of the then branch. - */ + /** @return the entry block of the then branch */ Block getThenSuccessor(); - /** - * @return The entry block of the else branch. - */ + /** @return the entry block of the else branch */ Block getElseSuccessor(); - /** - * @return The flow rule for information flowing from - * this block to its then successor. - */ + /** @return the flow rule for information flowing from this block to its then successor */ Store.FlowRule getThenFlowRule(); - /** - * @return The flow rule for information flowing from - * this block to its else successor. - */ + /** @return the flow rule for information flowing from this block to its else successor */ Store.FlowRule getElseFlowRule(); - /** - * Set the flow rule for information flowing from this block to - * its then successor. - */ + /** Set the flow rule for information flowing from this block to its then successor. */ void setThenFlowRule(Store.FlowRule rule); - /** - * Set the flow rule for information flowing from this block to - * its else successor. - */ + /** Set the flow rule for information flowing from this block to its else successor. */ void setElseFlowRule(Store.FlowRule rule); } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlockImpl.java index 629bb81070..bd12522983 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlockImpl.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlockImpl.java @@ -6,7 +6,6 @@ import org.checkerframework.dataflow.analysis.Store; * Implementation of a conditional basic block. * * @author Stefan Heule - * */ public class ConditionalBlockImpl extends BlockImpl implements ConditionalBlock { @@ -17,34 +16,29 @@ public class ConditionalBlockImpl extends BlockImpl implements ConditionalBlock protected BlockImpl elseSuccessor; /** - * The rules below say that the THEN store before a conditional - * block flows to BOTH of the stores of the then successor, while - * the ELSE store before a conditional block flows to BOTH of the - * stores of the else successor. + * The rules below say that the THEN store before a conditional block flows to BOTH of the + * stores of the then successor, while the ELSE store before a conditional block flows to BOTH + * of the stores of the else successor. */ protected Store.FlowRule thenFlowRule = Store.FlowRule.THEN_TO_BOTH; - + protected Store.FlowRule elseFlowRule = Store.FlowRule.ELSE_TO_BOTH; /** - * Initialize an empty conditional basic block to be filled with contents - * and linked to other basic blocks later. + * Initialize an empty conditional basic block to be filled with contents and linked to other + * basic blocks later. */ public ConditionalBlockImpl() { type = BlockType.CONDITIONAL_BLOCK; } - /** - * Set the then branch successor. - */ + /** Set the then branch successor. */ public void setThenSuccessor(BlockImpl b) { thenSuccessor = b; b.addPredecessor(this); } - /** - * Set the else branch successor. - */ + /** Set the else branch successor. */ public void setElseSuccessor(BlockImpl b) { elseSuccessor = b; b.addPredecessor(this); diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlock.java index c00a7e1e34..a549c5f03f 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlock.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlock.java @@ -2,37 +2,26 @@ package org.checkerframework.dataflow.cfg.block; import java.util.Map; import java.util.Set; - import javax.lang.model.type.TypeMirror; - import org.checkerframework.dataflow.cfg.node.Node; /** - * Represents a basic block that contains exactly one {@link Node} which can - * throw an exception. This block has exactly one non-exceptional successor, and - * one or more exceptional successors. - * - * <p> + * Represents a basic block that contains exactly one {@link Node} which can throw an exception. + * This block has exactly one non-exceptional successor, and one or more exceptional successors. * - * The following invariant holds. + * <p>The following invariant holds. * * <pre> * getNode().getBlock() == this * </pre> * * @author Stefan Heule - * */ public interface ExceptionBlock extends SingleSuccessorBlock { - /** - * @return The node of this block. - */ + /** @return the node of this block */ Node getNode(); - /** - * @return The list of exceptional successor blocks as an unmodifiable map. - */ + /** @return the list of exceptional successor blocks as an unmodifiable map */ Map<TypeMirror, Set<Block>> getExceptionalSuccessors(); - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlockImpl.java index 2148e060cd..2a6c3b4914 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlockImpl.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlockImpl.java @@ -5,19 +5,15 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; - import javax.lang.model.type.TypeMirror; - import org.checkerframework.dataflow.cfg.node.Node; /** * Base class of the {@link Block} implementation hierarchy. * * @author Stefan Heule - * */ -public class ExceptionBlockImpl extends SingleSuccessorBlockImpl implements - ExceptionBlock { +public class ExceptionBlockImpl extends SingleSuccessorBlockImpl implements ExceptionBlock { /** Set of exceptional successors. */ protected Map<TypeMirror, Set<Block>> exceptionalSuccessors; @@ -30,9 +26,7 @@ public class ExceptionBlockImpl extends SingleSuccessorBlockImpl implements /** The node of this block. */ protected Node node; - /** - * Set the node. - */ + /** Set the node. */ public void setNode(Node c) { node = c; c.setBlock(this); @@ -43,11 +37,8 @@ public class ExceptionBlockImpl extends SingleSuccessorBlockImpl implements return node; } - /** - * Add an exceptional successor. - */ - public void addExceptionalSuccessor(BlockImpl b, - TypeMirror cause) { + /** Add an exceptional successor. */ + public void addExceptionalSuccessor(BlockImpl b, TypeMirror cause) { if (exceptionalSuccessors == null) { exceptionalSuccessors = new HashMap<>(); } @@ -72,5 +63,4 @@ public class ExceptionBlockImpl extends SingleSuccessorBlockImpl implements public String toString() { return "ExceptionBlock(" + node + ")"; } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlock.java index dd6502fa1a..566449257b 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlock.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlock.java @@ -1,38 +1,27 @@ package org.checkerframework.dataflow.cfg.block; import java.util.List; - import org.checkerframework.dataflow.cfg.node.Node; /** * A regular basic block that contains a sequence of {@link Node}s. * - * <p> - * - * The following invariant holds. + * <p>The following invariant holds. * * <pre> * forall n in getContents() :: n.getBlock() == this * </pre> * * @author Stefan Heule - * */ public interface RegularBlock extends SingleSuccessorBlock { - /** - * @return The unmodifiable sequence of {@link Node}s. - */ + /** @return the unmodifiable sequence of {@link Node}s. */ List<Node> getContents(); - /** - * @return The regular successor block. - */ + /** @return the regular successor block */ Block getRegularSuccessor(); - /** - * Is this block empty (i.e., does it not contain any contents). - */ + /** Is this block empty (i.e., does it not contain any contents). */ boolean isEmpty(); - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlockImpl.java index 853f2c94f0..b302710d70 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlockImpl.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlockImpl.java @@ -3,41 +3,34 @@ package org.checkerframework.dataflow.cfg.block; import java.util.Collections; import java.util.LinkedList; import java.util.List; - import org.checkerframework.dataflow.cfg.node.Node; /** * Implementation of a regular basic block. * * @author Stefan Heule - * */ -public class RegularBlockImpl extends SingleSuccessorBlockImpl implements - RegularBlock { +public class RegularBlockImpl extends SingleSuccessorBlockImpl implements RegularBlock { /** Internal representation of the contents. */ protected List<Node> contents; /** - * Initialize an empty basic block to be filled with contents and linked to - * other basic blocks later. + * Initialize an empty basic block to be filled with contents and linked to other basic blocks + * later. */ public RegularBlockImpl() { contents = new LinkedList<>(); type = BlockType.REGULAR_BLOCK; } - /** - * Add a node to the contents of this basic block. - */ + /** Add a node to the contents of this basic block. */ public void addNode(Node t) { contents.add(t); t.setBlock(this); } - /** - * Add multiple nodes to the contents of this basic block. - */ + /** Add multiple nodes to the contents of this basic block. */ public void addNodes(List<? extends Node> ts) { for (Node t : ts) { addNode(t); @@ -63,5 +56,4 @@ public class RegularBlockImpl extends SingleSuccessorBlockImpl implements public boolean isEmpty() { return contents.isEmpty(); } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlock.java index 4d56291fe6..038a69d3e4 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlock.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlock.java @@ -10,23 +10,15 @@ import org.checkerframework.dataflow.analysis.Store; * A basic block that has at exactly one non-exceptional successor. * * @author Stefan Heule - * */ public interface SingleSuccessorBlock extends Block { - /** - * @return The non-exceptional successor block, or {@code null} if there is - * no successor. - */ + /** @return the non-exceptional successor block, or {@code null} if there is no successor. */ /*@Nullable*/ Block getSuccessor(); - /** - * @return The flow rule for information flowing from this block to its successor. - */ + /** @return the flow rule for information flowing from this block to its successor */ Store.FlowRule getFlowRule(); - /** - * Set the flow rule for information flowing from this block to its successor. - */ + /** Set the flow rule for information flowing from this block to its successor. */ void setFlowRule(Store.FlowRule rule); } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlockImpl.java index 7e5988e2e7..9e8872e850 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlockImpl.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlockImpl.java @@ -10,17 +10,15 @@ import org.checkerframework.dataflow.analysis.Store; * Implementation of a non-special basic block. * * @author Stefan Heule - * */ -public abstract class SingleSuccessorBlockImpl extends BlockImpl implements - SingleSuccessorBlock { +public abstract class SingleSuccessorBlockImpl extends BlockImpl implements SingleSuccessorBlock { /** Internal representation of the successor. */ protected /*@Nullable*/ BlockImpl successor; /** - * The rule below say that EACH store at the end of a single - * successor block flow to the corresponding store of the successor. + * The rule below say that EACH store at the end of a single successor block flow to the + * corresponding store of the successor. */ protected Store.FlowRule flowRule = Store.FlowRule.EACH_TO_EACH; @@ -29,9 +27,7 @@ public abstract class SingleSuccessorBlockImpl extends BlockImpl implements return successor; } - /** - * Set a basic block as the successor of this block. - */ + /** Set a basic block as the successor of this block. */ public void setSuccessor(BlockImpl successor) { this.successor = successor; successor.addPredecessor(this); diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlock.java index 89154bf604..dac9b4b8c2 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlock.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlock.java @@ -2,14 +2,14 @@ package org.checkerframework.dataflow.cfg.block; /** * Represents a special basic block; i.e., one of the following: + * * <ul> - * <li>Entry block of a method.</li> - * <li>Regular exit block of a method.</li> - * <li>Exceptional exit block of a method.</li> + * <li>Entry block of a method. + * <li>Regular exit block of a method. + * <li>Exceptional exit block of a method. * </ul> * * @author Stefan Heule - * */ public interface SpecialBlock extends SingleSuccessorBlock { @@ -26,9 +26,6 @@ public interface SpecialBlock extends SingleSuccessorBlock { EXCEPTIONAL_EXIT, } - /** - * @return The type of this special basic block. - */ + /** @return the type of this special basic block */ SpecialBlockType getSpecialType(); - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlockImpl.java index 9b3f8fb8e3..e6f25e8b1b 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlockImpl.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlockImpl.java @@ -1,7 +1,6 @@ package org.checkerframework.dataflow.cfg.block; -public class SpecialBlockImpl extends SingleSuccessorBlockImpl implements - SpecialBlock { +public class SpecialBlockImpl extends SingleSuccessorBlockImpl implements SpecialBlock { /** The type of this special basic block. */ protected SpecialBlockType specialType; @@ -20,5 +19,4 @@ public class SpecialBlockImpl extends SingleSuccessorBlockImpl implements public String toString() { return "SpecialBlock(" + specialType + ")"; } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java index 0f8a998efe..39f1f84074 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java @@ -1,28 +1,21 @@ package org.checkerframework.dataflow.cfg.node; - /** - * A default implementation of the node visitor interface. The class introduces - * several 'summary' methods, that can be overridden to change the behavior of - * several related visit methods at once. An example is the - * {@code visitValueLiteral} method, that is called for every - * {@link ValueLiteralNode}. - * - * <p> + * A default implementation of the node visitor interface. The class introduces several 'summary' + * methods, that can be overridden to change the behavior of several related visit methods at once. + * An example is the {@code visitValueLiteral} method, that is called for every {@link + * ValueLiteralNode}. * - * This is useful to implement a visitor that performs the same operation (e.g., - * nothing) for most {@link Node}s and only has special behavior for a few. + * <p>This is useful to implement a visitor that performs the same operation (e.g., nothing) for + * most {@link Node}s and only has special behavior for a few. * * @author Stefan Heule - * - * @param <R> - * Return type of the visitor. - * @param <P> - * Parameter type of the visitor. + * @param <R> return type of the visitor + * @param <P> parameter type of the visitor */ public abstract class AbstractNodeVisitor<R, P> implements NodeVisitor<R, P> { - abstract public R visitNode(Node n, P p); + public abstract R visitNode(Node n, P p); public R visitValueLiteral(ValueLiteralNode n, P p) { return visitNode(n, p); @@ -168,8 +161,7 @@ public abstract class AbstractNodeVisitor<R, P> implements NodeVisitor<R, P> { // Compound assignments @Override - public R visitStringConcatenateAssignment( - StringConcatenateAssignmentNode n, P p) { + public R visitStringConcatenateAssignment(StringConcatenateAssignmentNode n, P p) { return visitNode(n, p); } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayAccessNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayAccessNode.java index c51d7c1fbf..24ef689ee9 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayAccessNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayAccessNode.java @@ -1,29 +1,24 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.ArrayAccessTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.LinkedList; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.ArrayAccessTree; -import com.sun.source.tree.Tree; - /** * A node for an array access: * * <pre> - * <em>array ref</em> [ <em>index</em> ] + * <em>arrayref</em> [ <em>index</em> ] * </pre> * * We allow array accesses without corresponding AST {@link Tree}s. * * @author Stefan Heule * @author Charlie Garrett - * */ - public class ArrayAccessNode extends Node { protected Tree tree; @@ -68,8 +63,7 @@ public class ArrayAccessNode extends Node { return false; } ArrayAccessNode other = (ArrayAccessNode) obj; - return getArray().equals(other.getArray()) - && getIndex().equals(other.getIndex()); + return getArray().equals(other.getArray()) && getIndex().equals(other.getIndex()); } @Override diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayCreationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayCreationNode.java index 4af69077d3..5f277f2a0b 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayCreationNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayCreationNode.java @@ -4,16 +4,13 @@ package org.checkerframework.dataflow.cfg.node; import org.checkerframework.checker.nullness.qual.Nullable; */ -import org.checkerframework.dataflow.util.HashCodeUtils; - +import com.sun.source.tree.NewArrayTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.LinkedList; import java.util.List; - import javax.lang.model.type.TypeMirror; - -import com.sun.source.tree.NewArrayTree; -import com.sun.source.tree.Tree; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for new array creation @@ -25,17 +22,21 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class ArrayCreationNode extends Node { - // The tree is null when an array is created for - // variable arity method calls. + /** The tree is null when an array is created for variable arity method calls. */ protected /*@Nullable*/ NewArrayTree tree; + /** + * The length of this list is the number of dimensions in the array. Each element is the size of + * the given dimension. + */ protected List<Node> dimensions; + protected List<Node> initializers; - public ArrayCreationNode(/*@Nullable*/ NewArrayTree tree, + public ArrayCreationNode( + /*@Nullable*/ NewArrayTree tree, TypeMirror type, List<Node> dimensions, List<Node> initializers) { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayTypeNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayTypeNode.java index d7e0cd7f82..dfde575b5e 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayTypeNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayTypeNode.java @@ -1,24 +1,19 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.ArrayTypeTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.ArrayTypeTree; -import com.sun.source.tree.Tree; - /** - * A node representing a array type used in an expression - * such as a field access + * A node representing a array type used in an expression such as a field access * - * <em>type</em> .class + * <p><em>type</em> .class * * @author Stefan Heule * @author Charlie Garrett - * */ public class ArrayTypeNode extends Node { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssertionErrorNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssertionErrorNode.java index efc066a5bf..0b4d747331 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssertionErrorNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssertionErrorNode.java @@ -1,15 +1,12 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import java.util.Collection; import java.util.LinkedList; - import javax.lang.model.type.TypeMirror; - import org.checkerframework.dataflow.util.HashCodeUtils; -import com.sun.source.tree.Tree; -import com.sun.source.tree.Tree.Kind; - /** * A node for the {@link AssertionError} when an assertion fails. * @@ -19,7 +16,6 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ public class AssertionErrorNode extends Node { @@ -66,8 +62,7 @@ public class AssertionErrorNode extends Node { return false; } AssertionErrorNode other = (AssertionErrorNode) obj; - return getCondition().equals(other.getCondition()) && - getDetail().equals(other.getDetail()); + return getCondition().equals(other.getCondition()) && getDetail().equals(other.getDetail()); } @Override diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentContext.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentContext.java index 85d17e7c06..5e6a1606de 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentContext.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentContext.java @@ -1,33 +1,27 @@ package org.checkerframework.dataflow.cfg.node; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; - -import org.checkerframework.javacutil.TreeUtils; - import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import org.checkerframework.javacutil.TreeUtils; /** - * An assignment context for a node, which represents the place to which the - * node with this context is 'assigned' to. An 'assignment' (as we use the term - * here) can occur for Java assignments, method calls (for all the actual - * parameters which get assigned to their formal parameters) or method return - * statements. + * An assignment context for a node, which represents the place to which the node with this context + * is 'assigned' to. An 'assignment' (as we use the term here) can occur for Java assignments, + * method calls (for all the actual parameters which get assigned to their formal parameters) or + * method return statements. * - * <p> - * The main use of {@link AssignmentContext} is to be able to get the declared - * type of the left-hand side of the assignment for proper type-refinement. + * <p>The main use of {@link AssignmentContext} is to be able to get the declared type of the + * left-hand side of the assignment for proper type-refinement. * * @author Stefan Heule */ public abstract class AssignmentContext { - /** - * An assignment context for an assignment 'lhs = rhs'. - */ + /** An assignment context for an assignment 'lhs = rhs'. */ public static class AssignmentLhsContext extends AssignmentContext { protected final Node node; @@ -57,9 +51,7 @@ public abstract class AssignmentContext { } } - /** - * An assignment context for a method parameter. - */ + /** An assignment context for a method parameter. */ public static class MethodParameterContext extends AssignmentContext { protected final ExecutableElement method; @@ -83,9 +75,7 @@ public abstract class AssignmentContext { } } - /** - * An assignment context for method return statements. - */ + /** An assignment context for method return statements. */ public static class MethodReturnContext extends AssignmentContext { protected final ExecutableElement method; @@ -107,9 +97,7 @@ public abstract class AssignmentContext { } } - /** - * An assignment context for lambda return statements. - */ + /** An assignment context for lambda return statements. */ public static class LambdaReturnContext extends AssignmentContext { protected final ExecutableElement method; @@ -131,9 +119,7 @@ public abstract class AssignmentContext { } } - /** - * Returns an {@link Element} that has the type of this assignment context. - */ + /** Returns an {@link Element} that has the type of this assignment context. */ public abstract Element getElementForType(); public abstract Tree getContextTree(); diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentNode.java index d48666a1a6..32723dd8e4 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentNode.java @@ -1,18 +1,15 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.cfg.node.AssignmentContext.AssignmentLhsContext; -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.CompoundAssignmentTree; import com.sun.source.tree.Tree; import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; +import java.util.Collection; +import java.util.LinkedList; +import org.checkerframework.dataflow.cfg.node.AssignmentContext.AssignmentLhsContext; +import org.checkerframework.dataflow.util.HashCodeUtils; +import org.checkerframework.javacutil.InternalUtils; /** * A node for an assignment: @@ -26,7 +23,6 @@ import com.sun.source.tree.VariableTree; * We allow assignments without corresponding AST {@link Tree}s. * * @author Stefan Heule - * */ public class AssignmentNode extends Node { @@ -36,8 +32,10 @@ public class AssignmentNode extends Node { public AssignmentNode(Tree tree, Node target, Node expression) { super(InternalUtils.typeOf(tree)); - assert tree instanceof AssignmentTree || tree instanceof VariableTree - || tree instanceof CompoundAssignmentTree || tree instanceof UnaryTree; + assert tree instanceof AssignmentTree + || tree instanceof VariableTree + || tree instanceof CompoundAssignmentTree + || tree instanceof UnaryTree; assert target instanceof FieldAccessNode || target instanceof LocalVariableNode || target instanceof ArrayAccessNode; diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BinaryOperationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BinaryOperationNode.java new file mode 100644 index 0000000000..424747b4ac --- /dev/null +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BinaryOperationNode.java @@ -0,0 +1,52 @@ +package org.checkerframework.dataflow.cfg.node; + +import com.sun.source.tree.BinaryTree; +import java.util.Collection; +import java.util.LinkedList; +import org.checkerframework.javacutil.InternalUtils; + +/** + * A node for a binary expression. + * + * <p>For example: + * + * <pre> + * <em>lefOperandNode</em> <em>operator</em> <em>rightOperandNode</em> + * </pre> + * + * @author charleszhuochen + */ +public abstract class BinaryOperationNode extends Node { + + protected final BinaryTree tree; + protected final Node left; + protected final Node right; + + public BinaryOperationNode(BinaryTree tree, Node left, Node right) { + super(InternalUtils.typeOf(tree)); + this.tree = tree; + this.left = left; + this.right = right; + } + + public Node getLeftOperand() { + return left; + } + + public Node getRightOperand() { + return right; + } + + @Override + public BinaryTree getTree() { + return tree; + } + + @Override + public Collection<Node> getOperands() { + LinkedList<Node> list = new LinkedList<Node>(); + list.add(getLeftOperand()); + list.add(getRightOperand()); + return list; + } +} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseAndNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseAndNode.java index 82c5f5d5f8..18d7163f22 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseAndNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseAndNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the bitwise or logical (single bit) and operation: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class BitwiseAndNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class BitwiseAndNode extends BinaryOperationNode { - public BitwiseAndNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public BitwiseAndNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.AND; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class BitwiseAndNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseComplementNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseComplementNode.java index 1b8ad73175..a1c2111b8c 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseComplementNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseComplementNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.UnaryTree; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the bitwise complement operation: @@ -19,27 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class BitwiseComplementNode extends Node { +public class BitwiseComplementNode extends UnaryOperationNode { - protected Tree tree; - protected Node operand; - - public BitwiseComplementNode(Tree tree, Node operand) { - super(InternalUtils.typeOf(tree)); + public BitwiseComplementNode(UnaryTree tree, Node operand) { + super(tree, operand); assert tree.getKind() == Kind.BITWISE_COMPLEMENT; - this.tree = tree; - this.operand = operand; - } - - public Node getOperand() { - return operand; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -65,9 +44,4 @@ public class BitwiseComplementNode extends Node { public int hashCode() { return HashCodeUtils.hash(getOperand()); } - - @Override - public Collection<Node> getOperands() { - return Collections.singletonList(getOperand()); - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseOrNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseOrNode.java index f9763c9b14..66089133f2 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseOrNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseOrNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the bitwise or logical (single bit) or operation: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class BitwiseOrNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class BitwiseOrNode extends BinaryOperationNode { - public BitwiseOrNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public BitwiseOrNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.OR; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class BitwiseOrNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseXorNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseXorNode.java index 848b2f17ec..cdbe0ed93d 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseXorNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseXorNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the bitwise or logical (single bit) xor operation: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class BitwiseXorNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class BitwiseXorNode extends BinaryOperationNode { - public BitwiseXorNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public BitwiseXorNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.XOR; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class BitwiseXorNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BooleanLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BooleanLiteralNode.java index 8368c87793..7f5a7a1eda 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BooleanLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BooleanLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for a boolean literal: @@ -15,7 +14,6 @@ import com.sun.source.tree.Tree; * </pre> * * @author Stefan Heule - * */ public class BooleanLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CaseNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CaseNode.java index f5a3d460a5..a694dfb975 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CaseNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CaseNode.java @@ -1,20 +1,16 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.CaseTree; +import com.sun.source.tree.Tree.Kind; import java.util.Collection; import java.util.LinkedList; - import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; - import org.checkerframework.dataflow.util.HashCodeUtils; -import com.sun.source.tree.CaseTree; -import com.sun.source.tree.Tree.Kind; - /** - * A node for a case in a switch statement. Although - * a case has no abstract value, it can imply facts about - * the abstract values of its operands. + * A node for a case in a switch statement. Although a case has no abstract value, it can imply + * facts about the abstract values of its operands. * * <pre> * case <em>constant</em>: @@ -22,7 +18,6 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ public class CaseNode extends Node { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CharacterLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CharacterLiteralNode.java index 979a4177d8..c1d51c7523 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CharacterLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CharacterLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for a character literal. For example: @@ -17,7 +16,6 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class CharacterLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java index b74965d122..c3e2471309 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java @@ -4,34 +4,30 @@ package org.checkerframework.dataflow.cfg.node; import org.checkerframework.checker.nullness.qual.Nullable; */ -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; -import org.checkerframework.javacutil.TreeUtils; - -import java.util.Collection; -import java.util.Collections; - -import javax.lang.model.element.Element; - +import com.sun.source.tree.ClassTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; +import org.checkerframework.dataflow.util.HashCodeUtils; +import org.checkerframework.javacutil.InternalUtils; +import org.checkerframework.javacutil.TreeUtils; /** - * A node representing a class name used in an expression - * such as a static method invocation. + * A node representing a class name used in an expression such as a static method invocation. * - * parent.<em>class</em> .forName(...) + * <p>parent.<em>class</em> .forName(...) * * @author Stefan Heule * @author Charlie Garrett - * */ public class ClassNameNode extends Node { protected final Tree tree; - // The class named by this node + /** The class named by this node */ protected final Element element; /** The parent name, if any. */ @@ -45,6 +41,17 @@ public class ClassNameNode extends Node { this.parent = null; } + public ClassNameNode(ClassTree tree) { + super(InternalUtils.typeOf(tree)); + assert tree.getKind() == Tree.Kind.CLASS + || tree.getKind() == Tree.Kind.ENUM + || tree.getKind() == Tree.Kind.INTERFACE + || tree.getKind() == Tree.Kind.ANNOTATION_TYPE; + this.tree = tree; + this.element = TreeUtils.elementFromDeclaration(tree); + this.parent = null; + } + public ClassNameNode(MemberSelectTree tree, Node parent) { super(InternalUtils.typeOf(tree)); this.tree = tree; @@ -52,6 +59,13 @@ public class ClassNameNode extends Node { this.parent = parent; } + public ClassNameNode(TypeMirror type, Element element) { + super(type); + this.tree = null; + this.element = element; + this.parent = null; + } + public Element getElement() { return element; } @@ -82,11 +96,9 @@ public class ClassNameNode extends Node { } ClassNameNode other = (ClassNameNode) obj; if (getParent() == null) { - return other.getParent() == null - && getElement().equals(other.getElement()); + return other.getParent() == null && getElement().equals(other.getElement()); } else { - return getParent().equals(other.getParent()) - && getElement().equals(other.getElement()); + return getParent().equals(other.getParent()) && getElement().equals(other.getElement()); } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalAndNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalAndNode.java index dfa9f99c27..fb5145ab89 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalAndNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalAndNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for a conditional and expression: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class ConditionalAndNode extends Node { - - protected BinaryTree tree; - protected Node lhs; - protected Node rhs; +public class ConditionalAndNode extends BinaryOperationNode { - public ConditionalAndNode(BinaryTree tree, Node lhs, Node rhs) { - super(InternalUtils.typeOf(tree)); + public ConditionalAndNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind().equals(Kind.CONDITIONAL_AND); - this.tree = tree; - this.lhs = lhs; - this.rhs = rhs; - } - - public Node getLeftOperand() { - return lhs; - } - - public Node getRightOperand() { - return rhs; - } - - @Override - public BinaryTree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class ConditionalAndNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalNotNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalNotNode.java index 5143c69c50..6a28c67a16 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalNotNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalNotNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.UnaryTree; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for a conditional not expression: @@ -19,27 +13,12 @@ import com.sun.source.tree.UnaryTree; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class ConditionalNotNode extends Node { - - protected UnaryTree tree; - protected Node operand; +public class ConditionalNotNode extends UnaryOperationNode { public ConditionalNotNode(UnaryTree tree, Node operand) { - super(InternalUtils.typeOf(tree)); + super(tree, operand); assert tree.getKind().equals(Kind.LOGICAL_COMPLEMENT); - this.tree = tree; - this.operand = operand; - } - - public Node getOperand() { - return operand; - } - - @Override - public UnaryTree getTree() { - return tree; } @Override @@ -65,9 +44,4 @@ public class ConditionalNotNode extends Node { public int hashCode() { return HashCodeUtils.hash(getOperand()); } - - @Override - public Collection<Node> getOperands() { - return Collections.singletonList(getOperand()); - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalOrNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalOrNode.java index 2da33fadf6..e5cb0bfc13 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalOrNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalOrNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for a conditional or expression: @@ -18,33 +12,12 @@ import com.sun.source.tree.Tree.Kind; * </pre> * * @author Stefan Heule - * */ -public class ConditionalOrNode extends Node { - - protected BinaryTree tree; - protected Node lhs; - protected Node rhs; +public class ConditionalOrNode extends BinaryOperationNode { - public ConditionalOrNode(BinaryTree tree, Node lhs, Node rhs) { - super(InternalUtils.typeOf(tree)); + public ConditionalOrNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind().equals(Kind.CONDITIONAL_OR); - this.tree = tree; - this.lhs = lhs; - this.rhs = rhs; - } - - public Node getLeftOperand() { - return lhs; - } - - public Node getRightOperand() { - return rhs; - } - - @Override - public BinaryTree getTree() { - return tree; } @Override @@ -71,12 +44,4 @@ public class ConditionalOrNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/DoubleLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/DoubleLiteralNode.java index 855a95a7b5..60c25749a4 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/DoubleLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/DoubleLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for a double literal. For example: @@ -16,7 +15,6 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class DoubleLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/EqualToNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/EqualToNode.java index ed56d4ab68..b1fb5d8f68 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/EqualToNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/EqualToNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for an equality check: @@ -18,33 +12,12 @@ import com.sun.source.tree.Tree.Kind; * </pre> * * @author Stefan Heule - * */ -public class EqualToNode extends Node { - - protected BinaryTree tree; - protected Node lhs; - protected Node rhs; +public class EqualToNode extends BinaryOperationNode { - public EqualToNode(BinaryTree tree, Node lhs, Node rhs) { - super(InternalUtils.typeOf(tree)); + public EqualToNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind().equals(Kind.EQUAL_TO); - this.tree = tree; - this.lhs = lhs; - this.rhs = rhs; - } - - public Node getLeftOperand() { - return lhs; - } - - public Node getRightOperand() { - return rhs; - } - - @Override - public BinaryTree getTree() { - return tree; } @Override @@ -71,12 +44,4 @@ public class EqualToNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ExplicitThisLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ExplicitThisLiteralNode.java index 0c1069843c..abb3b5447e 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ExplicitThisLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ExplicitThisLiteralNode.java @@ -1,9 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import org.checkerframework.javacutil.InternalUtils; - import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.Tree; +import org.checkerframework.javacutil.InternalUtils; /** * A node for a reference to 'this'. @@ -14,7 +13,6 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class ExplicitThisLiteralNode extends ThisLiteralNode { @@ -22,8 +20,7 @@ public class ExplicitThisLiteralNode extends ThisLiteralNode { public ExplicitThisLiteralNode(Tree t) { super(InternalUtils.typeOf(t)); - assert t instanceof IdentifierTree - && ((IdentifierTree) t).getName().contentEquals("this"); + assert t instanceof IdentifierTree && ((IdentifierTree) t).getName().contentEquals("this"); tree = t; } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FieldAccessNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FieldAccessNode.java index db5bf51c5b..1b8e856a17 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FieldAccessNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FieldAccessNode.java @@ -1,20 +1,16 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.element.VariableElement; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.InternalUtils; import org.checkerframework.javacutil.TreeUtils; -import com.sun.source.tree.IdentifierTree; -import com.sun.source.tree.MemberSelectTree; -import com.sun.source.tree.Tree; - /** * A node for a field access, including a method accesses: * @@ -23,7 +19,6 @@ import com.sun.source.tree.Tree; * </pre> * * @author Stefan Heule - * */ public class FieldAccessNode extends Node { @@ -45,7 +40,7 @@ public class FieldAccessNode extends Node { this.element = (VariableElement) TreeUtils.elementFromUse((MemberSelectTree) tree); } else { assert tree instanceof IdentifierTree; - this.element = (VariableElement) TreeUtils.elementFromUse((IdentifierTree) tree); + this.element = (VariableElement) TreeUtils.elementFromUse((IdentifierTree) tree); } } @@ -84,9 +79,7 @@ public class FieldAccessNode extends Node { return getReceiver() + "." + field; } - /** - * Is this a static field? - */ + /** Is this a static field? */ public boolean isStatic() { return ElementUtils.isStatic(getElement()); } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatLiteralNode.java index 518c974868..ee8b5103ed 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for a float literal. For example: @@ -16,7 +15,6 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class FloatLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingDivisionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingDivisionNode.java index cd495b7b01..6eb6c41ac4 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingDivisionNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingDivisionNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the floating-point division: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class FloatingDivisionNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class FloatingDivisionNode extends BinaryOperationNode { - public FloatingDivisionNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public FloatingDivisionNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.DIVIDE; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class FloatingDivisionNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingRemainderNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingRemainderNode.java index d3b3caa0ab..d3a7918ff1 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingRemainderNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingRemainderNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the floating-point remainder: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class FloatingRemainderNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class FloatingRemainderNode extends BinaryOperationNode { - public FloatingRemainderNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public FloatingRemainderNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.REMAINDER; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class FloatingRemainderNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FunctionalInterfaceNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FunctionalInterfaceNode.java index 50c45daf2c..a0ddbaf10c 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FunctionalInterfaceNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FunctionalInterfaceNode.java @@ -3,24 +3,23 @@ package org.checkerframework.dataflow.cfg.node; import com.sun.source.tree.LambdaExpressionTree; import com.sun.source.tree.MemberReferenceTree; import com.sun.source.tree.Tree; -import org.checkerframework.javacutil.ErrorReporter; -import org.checkerframework.javacutil.InternalUtils; - import java.util.Collection; import java.util.LinkedList; +import org.checkerframework.javacutil.ErrorReporter; +import org.checkerframework.javacutil.InternalUtils; /** * A node for member references and lambdas. * - * The {@link Node#type} of a FunctionalInterfaceNode is determined by the - * assignment context the member reference or lambda is used in. + * <p>The {@link Node#type} of a FunctionalInterfaceNode is determined by the assignment context the + * member reference or lambda is used in. * * <pre> - * <em>FunctionalInterface func = param1, param2, ... -> statement</em> + * <em>FunctionalInterface func = param1, param2, ... → statement</em> * </pre> * * <pre> - * <em>FunctionalInterface func = param1, param2, ... -> { ... }</em> + * <em>FunctionalInterface func = param1, param2, ... → { ... }</em> * </pre> * * <pre> @@ -28,7 +27,6 @@ import java.util.LinkedList; * </pre> * * @author David - * */ public class FunctionalInterfaceNode extends Node { @@ -56,7 +54,7 @@ public class FunctionalInterfaceNode extends Node { @Override public String toString() { - if (tree instanceof LambdaExpressionTree){ + if (tree instanceof LambdaExpressionTree) { return "FunctionalInterfaceNode:" + ((LambdaExpressionTree) tree).getBodyKind(); } else if (tree instanceof MemberReferenceTree) { return "FunctionalInterfaceNode:" + ((MemberReferenceTree) tree).getName(); diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanNode.java index 7e51ccd115..f1713a9241 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the greater than comparison: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class GreaterThanNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class GreaterThanNode extends BinaryOperationNode { - public GreaterThanNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public GreaterThanNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.GREATER_THAN; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class GreaterThanNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanOrEqualNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanOrEqualNode.java index e23c824512..058ae7be84 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanOrEqualNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanOrEqualNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the greater than or equal comparison: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class GreaterThanOrEqualNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class GreaterThanOrEqualNode extends BinaryOperationNode { - public GreaterThanOrEqualNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public GreaterThanOrEqualNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.GREATER_THAN_EQUAL; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class GreaterThanOrEqualNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ImplicitThisLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ImplicitThisLiteralNode.java index 56bfa83e75..25a63617ad 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ImplicitThisLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ImplicitThisLiteralNode.java @@ -1,14 +1,12 @@ package org.checkerframework.dataflow.cfg.node; -import javax.lang.model.type.TypeMirror; - import com.sun.source.tree.Tree; +import javax.lang.model.type.TypeMirror; /** - * A node to model the implicit <code>this</code>, e.g., in a field access. + * A node to model the implicit {@code this}, e.g., in a field access. * * @author Stefan Heule - * */ public class ImplicitThisLiteralNode extends ThisLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java index 2f49436da2..78b4e06916 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java @@ -1,25 +1,21 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.InstanceOfTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; - import org.checkerframework.dataflow.util.HashCodeUtils; -import com.sun.source.tree.InstanceOfTree; -import com.sun.source.tree.Tree; - /** * A node for the instanceof operator: * - * <em>x</em> instanceof <em>Point</em> + * <p><em>x</em> instanceof <em>Point</em> * * @author Stefan Heule * @author Charlie Garrett - * */ public class InstanceOfNode extends Node { @@ -76,8 +72,7 @@ public class InstanceOfNode extends Node { InstanceOfNode other = (InstanceOfNode) obj; // TODO: TypeMirror.equals may be too restrictive. // Check whether Types.isSameType is the better comparison. - return getOperand().equals(other.getOperand()) - && getRefType().equals(other.getRefType()); + return getOperand().equals(other.getOperand()) && getRefType().equals(other.getRefType()); } @Override diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerDivisionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerDivisionNode.java index 0b5701413b..e69947bea5 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerDivisionNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerDivisionNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the integer division: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class IntegerDivisionNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class IntegerDivisionNode extends BinaryOperationNode { - public IntegerDivisionNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public IntegerDivisionNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.DIVIDE; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class IntegerDivisionNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerLiteralNode.java index b23a9cb512..5bea779369 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for an integer literal. For example: @@ -14,7 +13,6 @@ import com.sun.source.tree.Tree; * </pre> * * @author Stefan Heule - * */ public class IntegerLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerRemainderNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerRemainderNode.java index 39021cd2ab..d5399183a7 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerRemainderNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerRemainderNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the integer remainder: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class IntegerRemainderNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class IntegerRemainderNode extends BinaryOperationNode { - public IntegerRemainderNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public IntegerRemainderNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.REMAINDER; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class IntegerRemainderNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LeftShiftNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LeftShiftNode.java index 60652e6100..65a1e13d3c 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LeftShiftNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LeftShiftNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for bitwise left shift operations: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class LeftShiftNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class LeftShiftNode extends BinaryOperationNode { - public LeftShiftNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public LeftShiftNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.LEFT_SHIFT; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class LeftShiftNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanNode.java index a8dd5a18b4..fcc901150a 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanNode.java @@ -1,14 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the less than comparison: @@ -21,33 +16,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class LessThanNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class LessThanNode extends BinaryOperationNode { - public LessThanNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public LessThanNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.LESS_THAN; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -74,12 +48,4 @@ public class LessThanNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanOrEqualNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanOrEqualNode.java index 4944ab66a0..0dfb65ec41 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanOrEqualNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanOrEqualNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the less than or equal comparison: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class LessThanOrEqualNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class LessThanOrEqualNode extends BinaryOperationNode { - public LessThanOrEqualNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public LessThanOrEqualNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.LESS_THAN_EQUAL; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class LessThanOrEqualNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LocalVariableNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LocalVariableNode.java index 5b18a71342..724d63a46b 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LocalVariableNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LocalVariableNode.java @@ -1,19 +1,15 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.element.Element; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; import org.checkerframework.javacutil.TreeUtils; -import com.sun.source.tree.IdentifierTree; -import com.sun.source.tree.Tree; -import com.sun.source.tree.VariableTree; - /** * A node for a local variable or a parameter: * @@ -21,11 +17,10 @@ import com.sun.source.tree.VariableTree; * <em>identifier</em> * </pre> * - * We allow local variable uses introduced by the {@link org.checkerframework.dataflow.cfg.CFGBuilder} without - * corresponding AST {@link Tree}s. + * We allow local variable uses introduced by the {@link + * org.checkerframework.dataflow.cfg.CFGBuilder} without corresponding AST {@link Tree}s. * * @author Stefan Heule - * */ // TODO: don't use for parameters, as they don't have a tree public class LocalVariableNode extends Node { @@ -58,11 +53,11 @@ public class LocalVariableNode extends Node { } return el; } - + public Node getReceiver() { return receiver; } - + public String getName() { if (tree instanceof IdentifierTree) { return ((IdentifierTree) tree).getName().toString(); diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LongLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LongLiteralNode.java index 47f856a0d8..b92575ef90 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LongLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LongLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for a long literal. For example: @@ -16,7 +15,6 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class LongLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MarkerNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MarkerNode.java index fa7238e1c6..a7a892f3be 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MarkerNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MarkerNode.java @@ -4,27 +4,21 @@ package org.checkerframework.dataflow.cfg.node; import org.checkerframework.checker.nullness.qual.Nullable; */ -import org.checkerframework.dataflow.util.HashCodeUtils; - +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; - -import com.sun.source.tree.Tree; +import org.checkerframework.dataflow.util.HashCodeUtils; /** - * MarkerNodes are no-op Nodes used for debugging information. - * They can hold a Tree and a message, which will be part of the - * String representation of the MarkerNode. + * MarkerNodes are no-op Nodes used for debugging information. They can hold a Tree and a message, + * which will be part of the String representation of the MarkerNode. * - * An example use case for MarkerNodes is representing switch - * statements. + * <p>An example use case for MarkerNodes is representing switch statements. * * @author Stefan Heule * @author Charlie Garrett - * */ public class MarkerNode extends Node { @@ -69,8 +63,7 @@ public class MarkerNode extends Node { return false; } - return getTree().equals(other.getTree()) - && getMessage().equals(other.getMessage()); + return getTree().equals(other.getTree()) && getMessage().equals(other.getMessage()); } @Override diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodAccessNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodAccessNode.java index 75a8d681d3..060f17b62d 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodAccessNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodAccessNode.java @@ -1,18 +1,14 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.element.ExecutableElement; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; import org.checkerframework.javacutil.TreeUtils; -import com.sun.source.tree.ExpressionTree; -import com.sun.source.tree.Tree; - /** * A node for a method access, including a method accesses: * @@ -21,7 +17,6 @@ import com.sun.source.tree.Tree; * </pre> * * @author Stefan Heule - * */ public class MethodAccessNode extends Node { @@ -68,8 +63,7 @@ public class MethodAccessNode extends Node { return false; } MethodAccessNode other = (MethodAccessNode) obj; - return getReceiver().equals(other.getReceiver()) - && getMethod().equals(other.getMethod()); + return getReceiver().equals(other.getReceiver()) && getMethod().equals(other.getMethod()); } @Override diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodInvocationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodInvocationNode.java index 797185a83b..9456f37808 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodInvocationNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodInvocationNode.java @@ -1,18 +1,15 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; import java.util.Collection; import java.util.LinkedList; import java.util.List; - import org.checkerframework.dataflow.cfg.node.AssignmentContext.MethodParameterContext; import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.MethodInvocationTree; -import com.sun.source.tree.Tree; -import com.sun.source.util.TreePath; - /** * A node for method invocation * @@ -20,12 +17,11 @@ import com.sun.source.util.TreePath; * <em>target(arg1, arg2, ...)</em> * </pre> * - * CFGs may contain {@link MethodInvocationNode}s that correspond to no AST - * {@link Tree}, in which case, the tree field will be null. + * CFGs may contain {@link MethodInvocationNode}s that correspond to no AST {@link Tree}, in which + * case, the tree field will be null. * * @author Stefan Heule * @author Charlie Garrett - * */ public class MethodInvocationNode extends Node { @@ -34,8 +30,11 @@ public class MethodInvocationNode extends Node { protected List<Node> arguments; protected TreePath treePath; - public MethodInvocationNode(MethodInvocationTree tree, - MethodAccessNode target, List<Node> arguments, TreePath treePath) { + public MethodInvocationNode( + MethodInvocationTree tree, + MethodAccessNode target, + List<Node> arguments, + TreePath treePath) { super(tree != null ? InternalUtils.typeOf(tree) : target.getMethod().getReturnType()); this.tree = tree; this.target = target; @@ -50,8 +49,7 @@ public class MethodInvocationNode extends Node { } } - public MethodInvocationNode(MethodAccessNode target, List<Node> arguments, - TreePath treePath) { + public MethodInvocationNode(MethodAccessNode target, List<Node> arguments, TreePath treePath) { this(null, target, arguments, treePath); } @@ -105,8 +103,7 @@ public class MethodInvocationNode extends Node { } MethodInvocationNode other = (MethodInvocationNode) obj; - return getTarget().equals(other.getTarget()) - && getArguments().equals(other.getArguments()); + return getTarget().equals(other.getTarget()) && getArguments().equals(other.getArguments()); } @Override diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NarrowingConversionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NarrowingConversionNode.java index f0165d9589..79a47283eb 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NarrowingConversionNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NarrowingConversionNode.java @@ -1,27 +1,22 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeMirror; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.TypesUtils; -import com.sun.source.tree.Tree; - /** - * A node for the narrowing primitive conversion operation. See JLS 5.1.3 for - * the definition of narrowing primitive conversion. + * A node for the narrowing primitive conversion operation. See JLS 5.1.3 for the definition of + * narrowing primitive conversion. * - * A {@link NarrowingConversionNode} does not correspond to any tree node in the - * parsed AST. It is introduced when a value of some primitive type appears in a - * context that requires a different primitive with more bits of precision. + * <p>A {@link NarrowingConversionNode} does not correspond to any tree node in the parsed AST. It + * is introduced when a value of some primitive type appears in a context that requires a different + * primitive with more bits of precision. * * @author Stefan Heule * @author Charlie Garrett - * */ public class NarrowingConversionNode extends Node { @@ -39,6 +34,7 @@ public class NarrowingConversionNode extends Node { return operand; } + @Override public TypeMirror getType() { return type; } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/Node.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/Node.java index f59cc81bb8..fb5bf5335d 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/Node.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/Node.java @@ -4,67 +4,52 @@ package org.checkerframework.dataflow.cfg.node; import org.checkerframework.checker.nullness.qual.Nullable; */ -import org.checkerframework.dataflow.cfg.CFGBuilder; -import org.checkerframework.dataflow.cfg.block.Block; - +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.LinkedList; - import javax.lang.model.type.TypeMirror; - -import com.sun.source.tree.Tree; +import org.checkerframework.dataflow.cfg.CFGBuilder; +import org.checkerframework.dataflow.cfg.block.Block; /** - * A node in the abstract representation used for Java code inside a basic - * block. + * A node in the abstract representation used for Java code inside a basic block. * - * <p> - * - * The following invariants hold: + * <p>The following invariants hold: * * <pre> * block == null || block instanceof RegularBlock || block instanceof ExceptionBlock - * block instanceof RegularBlock ==> block.getContents().contains(this) - * block instanceof ExceptionBlock ==> block.getNode() == this - * block == null <==> "This object represents a parameter of the method." + * block instanceof RegularBlock ⇒ block.getContents().contains(this) + * block instanceof ExceptionBlock ⇒ block.getNode() == this + * block == null ⇔ "This object represents a parameter of the method." * </pre> * * <pre> * type != null - * tree != null ==> node.getType() == InternalUtils.typeOf(node.getTree()) + * tree != null ⇒ node.getType() == InternalUtils.typeOf(node.getTree()) * </pre> * * @author Stefan Heule - * */ public abstract class Node { - /** - * The basic block this node belongs to (see invariant about this field - * above). - */ + /** The basic block this node belongs to (see invariant about this field above). */ protected /*@Nullable*/ Block block; - /** - * Is this node an l-value? - */ + /** Is this node an l-value? */ protected boolean lvalue = false; - /** - * The assignment context of this node. See {@link AssignmentContext}. - */ + /** The assignment context of this node. See {@link AssignmentContext}. */ protected /*@Nullable*/ AssignmentContext assignmentContext; /** - * Does this node represent a tree that appears in the source code (true) - * or one that the CFG builder added while desugaring (false). + * Does this node represent a tree that appears in the source code (true) or one that the CFG + * builder added while desugaring (false). */ protected boolean inSource = true; /** - * The type of this node. For {@link Node}s with {@link Tree}s, this type is - * the type of the {@link Tree}. Otherwise, it is the type is set by the - * {@link CFGBuilder}. + * The type of this node. For {@link Node}s with {@link Tree}s, this type is the type of the + * {@link Tree}. Otherwise, it is the type is set by the {@link CFGBuilder}. */ protected final TypeMirror type; @@ -74,8 +59,8 @@ public abstract class Node { } /** - * @return The basic block this node belongs to (or {@code null} if it - * represents the parameter of a method). + * @return the basic block this node belongs to (or {@code null} if it represents the parameter + * of a method). */ public /*@Nullable*/ Block getBlock() { return block; @@ -87,19 +72,18 @@ public abstract class Node { } /** - * Returns the {@link Tree} in the abstract syntax tree, or - * <code>null</code> if no corresponding tree exists. For instance, this is - * the case for an {@link ImplicitThisLiteralNode}. + * Returns the {@link Tree} in the abstract syntax tree, or {@code null} if no corresponding + * tree exists. For instance, this is the case for an {@link ImplicitThisLiteralNode}. * - * @return The corresponding {@link Tree} or <code>null</code>. + * @return the corresponding {@link Tree} or {@code null}. */ - abstract public /*@Nullable*/ Tree getTree(); + public abstract /*@Nullable*/ Tree getTree(); /** - * Returns a {@link TypeMirror} representing the type of a {@link Node} A - * {@link Node} will always have a type even when it has no {@link Tree}. + * Returns a {@link TypeMirror} representing the type of a {@link Node} A {@link Node} will + * always have a type even when it has no {@link Tree}. * - * @return A {@link TypeMirror} representing the type of this {@link Node}. + * @return a {@link TypeMirror} representing the type of this {@link Node} */ public TypeMirror getType() { return type; @@ -108,14 +92,10 @@ public abstract class Node { /** * Accept method of the visitor pattern * - * @param <R> - * Result type of the operation. - * @param <P> - * Parameter type. - * @param visitor - * The visitor to be applied to this node. - * @param p - * The parameter for this operation. + * @param <R> result type of the operation + * @param <P> parameter type + * @param visitor the visitor to be applied to this node + * @param p the parameter for this operation */ public abstract <R, P> R accept(NodeVisitor<R, P> visitor, P p); @@ -123,9 +103,7 @@ public abstract class Node { return lvalue; } - /** - * Make this node an l-value. - */ + /** Make this node an l-value. */ public void setLValue() { lvalue = true; } @@ -146,16 +124,12 @@ public abstract class Node { this.assignmentContext = assignmentContext; } - /** - * @return A collection containing all of the operand {@link Node}s of this - * {@link Node}. - */ + /** @return a collection containing all of the operand {@link Node}s of this {@link Node}. */ public abstract Collection<Node> getOperands(); /** - * @return A collection containing all of the operand {@link Node}s of this - * {@link Node}, as well as (transitively) the operands of its - * operands. + * @return a collection containing all of the operand {@link Node}s of this {@link Node}, as + * well as (transitively) the operands of its operands */ public Collection<Node> getTransitiveOperands() { LinkedList<Node> operands = new LinkedList<>(getOperands()); @@ -167,5 +141,4 @@ public abstract class Node { } return transitiveOperands; } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java index fe751867a8..8a2f513924 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java @@ -4,13 +4,10 @@ package org.checkerframework.dataflow.cfg.node; * A visitor for a {@link Node} tree. * * @author Stefan Heule - * - * @param <R> - * Return type of the visitor. Use {@link Void} if the visitor does - * not have a return value. - * @param <P> - * Parameter type of the visitor. Use {@link Void} if the visitor - * does not have a parameter. + * @param <R> return type of the visitor. Use {@link Void} if the visitor does not have a return + * value. + * @param <P> parameter type of the visitor. Use {@link Void} if the visitor does not have a + * parameter. */ public interface NodeVisitor<R, P> { // Literals @@ -126,9 +123,9 @@ public interface NodeVisitor<R, P> { R visitTypeCast(TypeCastNode n, P p); // Blocks - + R visitSynchronized(SynchronizedNode n, P p); - + // Statements R visitAssertionError(AssertionErrorNode n, P p); diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NotEqualNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NotEqualNode.java index c043e20dee..debd7cbaf0 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NotEqualNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NotEqualNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the not equal comparison: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class NotEqualNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class NotEqualNode extends BinaryOperationNode { - public NotEqualNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public NotEqualNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.NOT_EQUAL_TO; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class NotEqualNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullChkNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullChkNode.java index 01ac008f40..6609a29b59 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullChkNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullChkNode.java @@ -1,15 +1,12 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import java.util.Collection; import java.util.Collections; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.Tree; -import com.sun.source.tree.Tree.Kind; - /** * A node for the unary 'nullchk' operation (generated by the Java compiler): * @@ -22,51 +19,51 @@ import com.sun.source.tree.Tree.Kind; */ public class NullChkNode extends Node { - protected Tree tree; - protected Node operand; + protected Tree tree; + protected Node operand; - public NullChkNode(Tree tree, Node operand) { - super(InternalUtils.typeOf(tree)); - assert tree.getKind() == Kind.OTHER; - this.tree = tree; - this.operand = operand; - } + public NullChkNode(Tree tree, Node operand) { + super(InternalUtils.typeOf(tree)); + assert tree.getKind() == Kind.OTHER; + this.tree = tree; + this.operand = operand; + } - public Node getOperand() { - return operand; - } + public Node getOperand() { + return operand; + } - @Override - public Tree getTree() { - return tree; - } + @Override + public Tree getTree() { + return tree; + } - @Override - public <R, P> R accept(NodeVisitor<R, P> visitor, P p) { - return visitor.visitNullChk(this, p); - } + @Override + public <R, P> R accept(NodeVisitor<R, P> visitor, P p) { + return visitor.visitNullChk(this, p); + } - @Override - public String toString() { - return "(+ " + getOperand() + ")"; - } + @Override + public String toString() { + return "(+ " + getOperand() + ")"; + } - @Override - public boolean equals(Object obj) { - if (obj == null || !(obj instanceof NumericalPlusNode)) { - return false; - } - NumericalPlusNode other = (NumericalPlusNode) obj; - return getOperand().equals(other.getOperand()); + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof NumericalPlusNode)) { + return false; } + NumericalPlusNode other = (NumericalPlusNode) obj; + return getOperand().equals(other.getOperand()); + } - @Override - public int hashCode() { - return HashCodeUtils.hash(getOperand()); - } + @Override + public int hashCode() { + return HashCodeUtils.hash(getOperand()); + } - @Override - public Collection<Node> getOperands() { - return Collections.singletonList(getOperand()); - } + @Override + public Collection<Node> getOperands() { + return Collections.singletonList(getOperand()); } +} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullLiteralNode.java index 66e67152cd..1fcdeb4825 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for the null literal. @@ -15,7 +14,6 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class NullLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalAdditionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalAdditionNode.java index d4192adf47..4692856dd8 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalAdditionNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalAdditionNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the numerical addition: @@ -18,34 +12,12 @@ import com.sun.source.tree.Tree.Kind; * </pre> * * @author Stefan Heule - * */ -public class NumericalAdditionNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; - - public NumericalAdditionNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); - assert tree.getKind() == Kind.PLUS - || tree.getKind() == Kind.PLUS_ASSIGNMENT; - this.tree = tree; - this.left = left; - this.right = right; - } +public class NumericalAdditionNode extends BinaryOperationNode { - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; + public NumericalAdditionNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); + assert tree.getKind() == Kind.PLUS || tree.getKind() == Kind.PLUS_ASSIGNMENT; } @Override @@ -72,12 +44,4 @@ public class NumericalAdditionNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMinusNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMinusNode.java index 459f299262..141ab580e0 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMinusNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMinusNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.UnaryTree; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the unary minus operation: @@ -19,27 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class NumericalMinusNode extends Node { +public class NumericalMinusNode extends UnaryOperationNode { - protected Tree tree; - protected Node operand; - - public NumericalMinusNode(Tree tree, Node operand) { - super(InternalUtils.typeOf(tree)); + public NumericalMinusNode(UnaryTree tree, Node operand) { + super(tree, operand); assert tree.getKind() == Kind.UNARY_MINUS; - this.tree = tree; - this.operand = operand; - } - - public Node getOperand() { - return operand; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -65,9 +44,4 @@ public class NumericalMinusNode extends Node { public int hashCode() { return HashCodeUtils.hash(getOperand()); } - - @Override - public Collection<Node> getOperands() { - return Collections.singletonList(getOperand()); - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMultiplicationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMultiplicationNode.java index 947dc5dcde..85561a1a02 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMultiplicationNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMultiplicationNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the numerical multiplication: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class NumericalMultiplicationNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class NumericalMultiplicationNode extends BinaryOperationNode { - public NumericalMultiplicationNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public NumericalMultiplicationNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.MULTIPLY; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class NumericalMultiplicationNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalPlusNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalPlusNode.java index 3d19b278a7..21b5b584d2 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalPlusNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalPlusNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.UnaryTree; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the unary plus operation: @@ -19,27 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class NumericalPlusNode extends Node { +public class NumericalPlusNode extends UnaryOperationNode { - protected Tree tree; - protected Node operand; - - public NumericalPlusNode(Tree tree, Node operand) { - super(InternalUtils.typeOf(tree)); + public NumericalPlusNode(UnaryTree tree, Node operand) { + super(tree, operand); assert tree.getKind() == Kind.UNARY_PLUS; - this.tree = tree; - this.operand = operand; - } - - public Node getOperand() { - return operand; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -65,9 +44,4 @@ public class NumericalPlusNode extends Node { public int hashCode() { return HashCodeUtils.hash(getOperand()); } - - @Override - public Collection<Node> getOperands() { - return Collections.singletonList(getOperand()); - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalSubtractionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalSubtractionNode.java index 1526a18461..a962eadd78 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalSubtractionNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalSubtractionNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for the numerical subtraction: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class NumericalSubtractionNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class NumericalSubtractionNode extends BinaryOperationNode { - public NumericalSubtractionNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public NumericalSubtractionNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.MINUS; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class NumericalSubtractionNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ObjectCreationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ObjectCreationNode.java index 67f2dee3bb..8ab862a2ef 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ObjectCreationNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ObjectCreationNode.java @@ -1,16 +1,12 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.NewClassTree; import java.util.Collection; import java.util.LinkedList; import java.util.List; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.NewClassTree; -import com.sun.source.tree.Tree; - /** * A node for new object creation * @@ -20,7 +16,6 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class ObjectCreationNode extends Node { @@ -28,9 +23,7 @@ public class ObjectCreationNode extends Node { protected Node constructor; protected List<Node> arguments; - public ObjectCreationNode(NewClassTree tree, - Node constructor, - List<Node> arguments) { + public ObjectCreationNode(NewClassTree tree, Node constructor, List<Node> arguments) { super(InternalUtils.typeOf(tree)); this.tree = tree; this.constructor = constructor; @@ -50,7 +43,7 @@ public class ObjectCreationNode extends Node { } @Override - public Tree getTree() { + public NewClassTree getTree() { return tree; } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PackageNameNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PackageNameNode.java index 326ac5e43e..3c69b8252b 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PackageNameNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PackageNameNode.java @@ -4,37 +4,30 @@ package org.checkerframework.dataflow.cfg.node; import org.checkerframework.checker.nullness.qual.Nullable; */ -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; -import org.checkerframework.javacutil.TreeUtils; - -import java.util.Collection; -import java.util.Collections; - -import javax.lang.model.element.Element; - import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; +import javax.lang.model.element.Element; +import org.checkerframework.dataflow.util.HashCodeUtils; +import org.checkerframework.javacutil.InternalUtils; +import org.checkerframework.javacutil.TreeUtils; /** - * A node representing a package name used in an expression such as a - * constructor invocation + * A node representing a package name used in an expression such as a constructor invocation * - * <p> - * <em>package</em>.class.object(...) - * <p> - * parent.<em>package</em>.class.object(...) + * <p><em>package</em>.class.object(...) + * + * <p>parent.<em>package</em>.class.object(...) * * @author Stefan Heule * @author Charlie Garrett - * */ public class PackageNameNode extends Node { protected final Tree tree; - // The package named by this node + /** The package named by this node */ protected final Element element; /** The parent name, if any. */ @@ -84,11 +77,9 @@ public class PackageNameNode extends Node { } PackageNameNode other = (PackageNameNode) obj; if (getParent() == null) { - return other.getParent() == null - && getElement().equals(other.getElement()); + return other.getParent() == null && getElement().equals(other.getElement()); } else { - return getParent().equals(other.getParent()) - && getElement().equals(other.getElement()); + return getParent().equals(other.getParent()) && getElement().equals(other.getElement()); } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ParameterizedTypeNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ParameterizedTypeNode.java index 7939b41fa7..774ac8b7c5 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ParameterizedTypeNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ParameterizedTypeNode.java @@ -1,15 +1,12 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.ParameterizedTypeTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.ParameterizedTypeTree; -import com.sun.source.tree.Tree; - /** * A node for a parameterized type occurring in an expression: * @@ -17,16 +14,13 @@ import com.sun.source.tree.Tree; * <em>type<arg1, arg2></em> * </pre> * - * Parameterized types don't represent any computation to be done - * at runtime, so we might choose to represent them differently by - * modifying the {@link Node}s in which parameterized types can occur, such - * as {@link ObjectCreationNode}s. + * Parameterized types don't represent any computation to be done at runtime, so we might choose to + * represent them differently by modifying the {@link Node}s in which parameterized types can occur, + * such as {@link ObjectCreationNode}s. * * @author Stefan Heule * @author Charlie Garrett - * */ - public class ParameterizedTypeNode extends Node { protected Tree tree; diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PrimitiveTypeNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PrimitiveTypeNode.java index 69b185641d..379bb92024 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PrimitiveTypeNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PrimitiveTypeNode.java @@ -1,24 +1,19 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.PrimitiveTypeTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.PrimitiveTypeTree; -import com.sun.source.tree.Tree; - /** - * A node representing a primitive type used in an expression - * such as a field access + * A node representing a primitive type used in an expression such as a field access * - * <em>type</em> .class + * <p><em>type</em> .class * * @author Stefan Heule * @author Charlie Garrett - * */ public class PrimitiveTypeNode extends Node { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ReturnNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ReturnNode.java index dbc74c83e3..b7222dfa18 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ReturnNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ReturnNode.java @@ -5,19 +5,16 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.ReturnTree; import com.sun.tools.javac.code.Symbol.MethodSymbol; -import org.checkerframework.dataflow.cfg.node.AssignmentContext.LambdaReturnContext; -import org.checkerframework.dataflow.cfg.node.AssignmentContext.MethodReturnContext; -import org.checkerframework.dataflow.util.HashCodeUtils; - import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; - -import com.sun.source.tree.MethodTree; -import com.sun.source.tree.ReturnTree; +import org.checkerframework.dataflow.cfg.node.AssignmentContext.LambdaReturnContext; +import org.checkerframework.dataflow.cfg.node.AssignmentContext.MethodReturnContext; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for a return statement: @@ -28,7 +25,6 @@ import com.sun.source.tree.ReturnTree; * </pre> * * @author Stefan Heule - * */ public class ReturnNode extends Node { @@ -42,14 +38,18 @@ public class ReturnNode extends Node { result.setAssignmentContext(new MethodReturnContext(methodTree)); } - public ReturnNode(ReturnTree t, /*@Nullable*/ Node result, Types types, LambdaExpressionTree lambda, MethodSymbol methodSymbol) { + public ReturnNode( + ReturnTree t, + /*@Nullable*/ Node result, + Types types, + LambdaExpressionTree lambda, + MethodSymbol methodSymbol) { super(types.getNoType(TypeKind.NONE)); this.result = result; tree = t; result.setAssignmentContext(new LambdaReturnContext(methodSymbol)); } - public Node getResult() { return result; } @@ -97,5 +97,4 @@ public class ReturnNode extends Node { return Collections.singletonList(result); } } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ShortLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ShortLiteralNode.java index f61da82397..cd1db1e560 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ShortLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ShortLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for a short literal. For example: @@ -14,14 +13,12 @@ import com.sun.source.tree.Tree; * <em>0x8fff</em> * </pre> * - * Java source and the AST representation do not have "short" literals. They - * have integer literals that may be narrowed to shorts depending on context. If - * we use explicit NarrowingConversionNodes, do we need ShortLiteralNodes too? - * TODO: Decide this question. + * Java source and the AST representation do not have "short" literals. They have integer literals + * that may be narrowed to shorts depending on context. If we use explicit NarrowingConversionNodes, + * do we need ShortLiteralNodes too? TODO: Decide this question. * * @author Stefan Heule * @author Charlie Garrett - * */ public class ShortLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SignedRightShiftNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SignedRightShiftNode.java index 3683ab9a3c..ac4aa58783 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SignedRightShiftNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SignedRightShiftNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for bitwise right shift operations with sign extension: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class SignedRightShiftNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class SignedRightShiftNode extends BinaryOperationNode { - public SignedRightShiftNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public SignedRightShiftNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.RIGHT_SHIFT; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class SignedRightShiftNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateAssignmentNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateAssignmentNode.java index d6c4c532b0..0e6f006d7e 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateAssignmentNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateAssignmentNode.java @@ -1,13 +1,11 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; import java.util.Collection; import java.util.LinkedList; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.Tree; -import com.sun.source.tree.Tree.Kind; - /** * A node for the string concatenation compound assignment: * @@ -17,7 +15,6 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ public class StringConcatenateAssignmentNode extends Node { protected Tree tree; diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateNode.java index e42d6ac7db..c69beea694 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for string concatenation: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class StringConcatenateNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class StringConcatenateNode extends BinaryOperationNode { - public StringConcatenateNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public StringConcatenateNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.PLUS; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class StringConcatenateNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConversionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConversionNode.java index 2f62c8af9a..8ba8c6ff60 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConversionNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConversionNode.java @@ -1,31 +1,25 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeMirror; - import org.checkerframework.dataflow.util.HashCodeUtils; -import com.sun.source.tree.Tree; - /** - * A node for the string conversion operation. See JLS 5.1.11 for the definition - * of string conversion. + * A node for the string conversion operation. See JLS 5.1.11 for the definition of string + * conversion. * - * A {@link StringConversionNode} does not correspond to any tree node in the - * parsed AST. It is introduced when a value of non-string type appears in a - * context that requires a {@link String}, such as in a string concatenation. A - * {@link StringConversionNode} should be treated as a potential call to the - * toString method of its operand, but does not necessarily call any method + * <p>A {@link StringConversionNode} does not correspond to any tree node in the parsed AST. It is + * introduced when a value of non-string type appears in a context that requires a {@link String}, + * such as in a string concatenation. A {@link StringConversionNode} should be treated as a + * potential call to the toString method of its operand, but does not necessarily call any method * because null is converted to the string "null". * - * Conversion of primitive types to Strings requires first boxing and then - * string conversion. + * <p>Conversion of primitive types to Strings requires first boxing and then string conversion. * * @author Stefan Heule * @author Charlie Garrett - * */ public class StringConversionNode extends Node { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringLiteralNode.java index e8d9291c55..c6ec1c90b8 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringLiteralNode.java @@ -1,10 +1,9 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.Collections; - import com.sun.source.tree.LiteralTree; import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; /** * A node for an string literal. For example: @@ -14,7 +13,6 @@ import com.sun.source.tree.Tree; * </pre> * * @author Stefan Heule - * */ public class StringLiteralNode extends ValueLiteralNode { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SuperNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SuperNode.java index d6ce410285..797eae8e7a 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SuperNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SuperNode.java @@ -1,15 +1,12 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.IdentifierTree; -import com.sun.source.tree.Tree; - /** * A node for a reference to 'super'. * @@ -19,7 +16,6 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class SuperNode extends Node { @@ -27,8 +23,7 @@ public class SuperNode extends Node { public SuperNode(Tree t) { super(InternalUtils.typeOf(t)); - assert t instanceof IdentifierTree - && ((IdentifierTree) t).getName().contentEquals("super"); + assert t instanceof IdentifierTree && ((IdentifierTree) t).getName().contentEquals("super"); tree = t; } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SynchronizedNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SynchronizedNode.java index 86d28bd87e..a17e2fa2e8 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SynchronizedNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SynchronizedNode.java @@ -10,15 +10,12 @@ import org.checkerframework.checker.nullness.qual.Nullable; * Otherwise it is the node immediately after a synchronized code block. */ -import org.checkerframework.dataflow.util.HashCodeUtils; - +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; - -import com.sun.source.tree.Tree; +import org.checkerframework.dataflow.util.HashCodeUtils; public class SynchronizedNode extends Node { @@ -26,7 +23,8 @@ public class SynchronizedNode extends Node { protected Node expression; protected boolean startOfBlock; - public SynchronizedNode(/*@Nullable*/ Tree tree, Node expression, boolean startOfBlock, Types types) { + public SynchronizedNode( + /*@Nullable*/ Tree tree, Node expression, boolean startOfBlock, Types types) { super(types.getNoType(TypeKind.NONE)); this.tree = tree; this.expression = expression; @@ -88,4 +86,4 @@ public class SynchronizedNode extends Node { public Collection<Node> getOperands() { return Collections.emptyList(); } -}
\ No newline at end of file +} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TernaryExpressionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TernaryExpressionNode.java index 514f85944d..9a149a3325 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TernaryExpressionNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TernaryExpressionNode.java @@ -1,15 +1,12 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.ConditionalExpressionTree; +import com.sun.source.tree.Tree.Kind; import java.util.Collection; import java.util.LinkedList; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.ConditionalExpressionTree; -import com.sun.source.tree.Tree.Kind; - /** * A node for a conditional expression: * @@ -19,7 +16,6 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ public class TernaryExpressionNode extends Node { @@ -28,8 +24,8 @@ public class TernaryExpressionNode extends Node { protected Node thenOperand; protected Node elseOperand; - public TernaryExpressionNode(ConditionalExpressionTree tree, Node condition, - Node thenOperand, Node elseOperand) { + public TernaryExpressionNode( + ConditionalExpressionTree tree, Node condition, Node thenOperand, Node elseOperand) { super(InternalUtils.typeOf(tree)); assert tree.getKind().equals(Kind.CONDITIONAL_EXPRESSION); this.tree = tree; @@ -62,8 +58,13 @@ public class TernaryExpressionNode extends Node { @Override public String toString() { - return "(" + getConditionOperand() + " ? " + getThenOperand() + " : " - + getElseOperand() + ")"; + return "(" + + getConditionOperand() + + " ? " + + getThenOperand() + + " : " + + getElseOperand() + + ")"; } @Override @@ -79,8 +80,7 @@ public class TernaryExpressionNode extends Node { @Override public int hashCode() { - return HashCodeUtils.hash(getConditionOperand(), getThenOperand(), - getElseOperand()); + return HashCodeUtils.hash(getConditionOperand(), getThenOperand(), getElseOperand()); } @Override diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThisLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThisLiteralNode.java index f62d6ac675..3bb4c186e6 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThisLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThisLiteralNode.java @@ -2,9 +2,7 @@ package org.checkerframework.dataflow.cfg.node; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeMirror; - import org.checkerframework.dataflow.util.HashCodeUtils; /** @@ -16,7 +14,6 @@ import org.checkerframework.dataflow.util.HashCodeUtils; * * @author Stefan Heule * @author Charlie Garrett - * */ public abstract class ThisLiteralNode extends Node { diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThrowNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThrowNode.java index f563cef96b..bb36e10a4a 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThrowNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThrowNode.java @@ -1,16 +1,13 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.ThrowTree; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; - import org.checkerframework.dataflow.util.HashCodeUtils; -import com.sun.source.tree.ThrowTree; -import com.sun.source.tree.Tree; - /** * A node for exception throws: * @@ -20,15 +17,13 @@ import com.sun.source.tree.Tree; * * @author Stefan Heule * @author Charlie Garrett - * */ public class ThrowNode extends Node { protected ThrowTree tree; protected Node expression; - public ThrowNode(ThrowTree tree, - Node expression, Types types) { + public ThrowNode(ThrowTree tree, Node expression, Types types) { super(types.getNoType(TypeKind.NONE)); this.tree = tree; this.expression = expression; diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TypeCastNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TypeCastNode.java index ce287a5cb6..f6e71bf6aa 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TypeCastNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TypeCastNode.java @@ -1,22 +1,18 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeMirror; - import org.checkerframework.dataflow.util.HashCodeUtils; -import com.sun.source.tree.Tree; - /** * A node for the cast operator: * - * (<em>Point</em>) <em>x</em> + * <p>(<em>Point</em>) <em>x</em> * * @author Stefan Heule * @author Charlie Garrett - * */ public class TypeCastNode extends Node { @@ -33,6 +29,7 @@ public class TypeCastNode extends Node { return operand; } + @Override public TypeMirror getType() { return type; } @@ -60,8 +57,7 @@ public class TypeCastNode extends Node { TypeCastNode other = (TypeCastNode) obj; // TODO: TypeMirror.equals may be too restrictive. // Check whether Types.isSameType is the better comparison. - return getOperand().equals(other.getOperand()) - && getType().equals(other.getType()); + return getOperand().equals(other.getOperand()) && getType().equals(other.getType()); } @Override diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnaryOperationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnaryOperationNode.java new file mode 100644 index 0000000000..cb33e6a3f1 --- /dev/null +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnaryOperationNode.java @@ -0,0 +1,45 @@ +package org.checkerframework.dataflow.cfg.node; + +import com.sun.source.tree.UnaryTree; +import java.util.Collection; +import java.util.Collections; +import org.checkerframework.javacutil.InternalUtils; + +/** + * A node for a postfix or an unary expression. + * + * <p>For example: + * + * <pre> + * <em>operator</em> <em>expressionNode</em> + * + * <em>expressionNode</em> <em>operator</em> + * </pre> + * + * @author charleszhuochen + */ +public abstract class UnaryOperationNode extends Node { + + protected final UnaryTree tree; + protected final Node operand; + + public UnaryOperationNode(UnaryTree tree, Node operand) { + super(InternalUtils.typeOf(tree)); + this.tree = tree; + this.operand = operand; + } + + public Node getOperand() { + return this.operand; + } + + @Override + public UnaryTree getTree() { + return tree; + } + + @Override + public Collection<Node> getOperands() { + return Collections.singletonList(getOperand()); + } +} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnsignedRightShiftNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnsignedRightShiftNode.java index 0ddea5b6c6..2609a7d7ae 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnsignedRightShiftNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnsignedRightShiftNode.java @@ -1,14 +1,8 @@ package org.checkerframework.dataflow.cfg.node; -import java.util.Collection; -import java.util.LinkedList; - -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - -import com.sun.source.tree.Tree; +import com.sun.source.tree.BinaryTree; import com.sun.source.tree.Tree.Kind; +import org.checkerframework.dataflow.util.HashCodeUtils; /** * A node for bitwise right shift operations with zero extension: @@ -19,33 +13,12 @@ import com.sun.source.tree.Tree.Kind; * * @author Stefan Heule * @author Charlie Garrett - * */ -public class UnsignedRightShiftNode extends Node { - - protected Tree tree; - protected Node left; - protected Node right; +public class UnsignedRightShiftNode extends BinaryOperationNode { - public UnsignedRightShiftNode(Tree tree, Node left, Node right) { - super(InternalUtils.typeOf(tree)); + public UnsignedRightShiftNode(BinaryTree tree, Node left, Node right) { + super(tree, left, right); assert tree.getKind() == Kind.UNSIGNED_RIGHT_SHIFT; - this.tree = tree; - this.left = left; - this.right = right; - } - - public Node getLeftOperand() { - return left; - } - - public Node getRightOperand() { - return right; - } - - @Override - public Tree getTree() { - return tree; } @Override @@ -72,12 +45,4 @@ public class UnsignedRightShiftNode extends Node { public int hashCode() { return HashCodeUtils.hash(getLeftOperand(), getRightOperand()); } - - @Override - public Collection<Node> getOperands() { - LinkedList<Node> list = new LinkedList<Node>(); - list.add(getLeftOperand()); - list.add(getRightOperand()); - return list; - } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ValueLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ValueLiteralNode.java index 68058d3ccc..4c9c18f0af 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ValueLiteralNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ValueLiteralNode.java @@ -1,13 +1,10 @@ package org.checkerframework.dataflow.cfg.node; -import org.checkerframework.dataflow.util.HashCodeUtils; - -import org.checkerframework.javacutil.InternalUtils; - +import com.sun.source.tree.LiteralTree; import java.util.Collection; import java.util.Collections; - -import com.sun.source.tree.LiteralTree; +import org.checkerframework.dataflow.util.HashCodeUtils; +import org.checkerframework.javacutil.InternalUtils; /*>>> import org.checkerframework.checker.nullness.qual.Nullable; @@ -15,28 +12,26 @@ import org.checkerframework.checker.nullness.qual.Nullable; /** * A node for a literals that have some form of value: + * * <ul> - * <li>integer literal</li> - * <li>long literal</li> - * <li>char literal</li> - * <li>string literal</li> - * <li>float literal</li> - * <li>double literal</li> - * <li>boolean literal</li> - * <li>null literal</li> + * <li>integer literal + * <li>long literal + * <li>char literal + * <li>string literal + * <li>float literal + * <li>double literal + * <li>boolean literal + * <li>null literal * </ul> * * @author Stefan Heule - * */ public abstract class ValueLiteralNode extends Node { protected final LiteralTree tree; - /** - * @return The value of the literal. - */ - abstract public /*@Nullable*/ Object getValue(); + /** @return the value of the literal */ + public abstract /*@Nullable*/ Object getValue(); public ValueLiteralNode(LiteralTree tree) { super(InternalUtils.typeOf(tree)); @@ -53,9 +48,7 @@ public abstract class ValueLiteralNode extends Node { return String.valueOf(getValue()); } - /** - * Compare the value of this nodes. - */ + /** Compare the value of this nodes. */ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof ValueLiteralNode)) { @@ -76,5 +69,4 @@ public abstract class ValueLiteralNode extends Node { public Collection<Node> getOperands() { return Collections.emptyList(); } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/VariableDeclarationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/VariableDeclarationNode.java index 5841768e38..867cdf1ad9 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/VariableDeclarationNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/VariableDeclarationNode.java @@ -1,14 +1,11 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.VariableTree; import java.util.Collection; import java.util.Collections; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.VariableTree; - /** * A node for a local variable declaration: * @@ -16,11 +13,10 @@ import com.sun.source.tree.VariableTree; * <em>modifier</em> <em>type</em> <em>identifier</em>; * </pre> * - * Note: Does not have an initializer block, as that will be translated to a - * separate {@link AssignmentNode}. + * Note: Does not have an initializer block, as that will be translated to a separate {@link + * AssignmentNode}. * * @author Stefan Heule - * */ public class VariableDeclarationNode extends Node { @@ -72,5 +68,4 @@ public class VariableDeclarationNode extends Node { public Collection<Node> getOperands() { return Collections.emptyList(); } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/WideningConversionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/WideningConversionNode.java index 4724ec1066..9b949ea546 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/WideningConversionNode.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/WideningConversionNode.java @@ -1,27 +1,22 @@ package org.checkerframework.dataflow.cfg.node; +import com.sun.source.tree.Tree; import java.util.Collection; import java.util.Collections; - import javax.lang.model.type.TypeMirror; - import org.checkerframework.dataflow.util.HashCodeUtils; - import org.checkerframework.javacutil.TypesUtils; -import com.sun.source.tree.Tree; - /** - * A node for the widening primitive conversion operation. See JLS 5.1.2 for the - * definition of widening primitive conversion. + * A node for the widening primitive conversion operation. See JLS 5.1.2 for the definition of + * widening primitive conversion. * - * A {@link WideningConversionNode} does not correspond to any tree node in the - * parsed AST. It is introduced when a value of some primitive type appears in a - * context that requires a different primitive with more bits of precision. + * <p>A {@link WideningConversionNode} does not correspond to any tree node in the parsed AST. It is + * introduced when a value of some primitive type appears in a context that requires a different + * primitive with more bits of precision. * * @author Stefan Heule * @author Charlie Garrett - * */ public class WideningConversionNode extends Node { @@ -39,6 +34,7 @@ public class WideningConversionNode extends Node { return operand; } + @Override public TypeMirror getType() { return type; } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/playground/ConstantPropagationPlayground.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/playground/ConstantPropagationPlayground.java index 25cf73995a..79088386a9 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/playground/ConstantPropagationPlayground.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/playground/ConstantPropagationPlayground.java @@ -8,26 +8,20 @@ import org.checkerframework.dataflow.constantpropagation.ConstantPropagationTran public class ConstantPropagationPlayground { - /** - * Run constant propagation for a specific file and create a PDF of the CFG - * in the end. - */ + /** Run constant propagation for a specific file and create a PDF of the CFG in the end. */ public static void main(String[] args) { /* Configuration: change as appropriate */ String inputFile = "cfg-input.java"; // input file name and path - String outputFileName = "cfg"; // output file name and path (without - // extension) + String outputDir = "cfg"; // output directory String method = "test"; // name of the method to analyze String clazz = "Test"; // name of the class to consider // run the analysis and create a PDF file ConstantPropagationTransfer transfer = new ConstantPropagationTransfer(); // TODO: correct processing environment - Analysis<Constant, ConstantPropagationStore, ConstantPropagationTransfer> analysis = new Analysis<>( - null, transfer); - JavaSource2CFGDOT.generateDOTofCFG(inputFile, outputFileName, method, - clazz, true, analysis); + Analysis<Constant, ConstantPropagationStore, ConstantPropagationTransfer> analysis = + new Analysis<>(null, transfer); + JavaSource2CFGDOT.generateDOTofCFG(inputFile, outputDir, method, clazz, true, analysis); } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/Constant.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/Constant.java index f63c23202e..a95af7fa43 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/Constant.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/Constant.java @@ -5,7 +5,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ import java.util.Objects; - import org.checkerframework.dataflow.analysis.AbstractValue; public class Constant implements AbstractValue<Constant> { @@ -17,7 +16,9 @@ public class Constant implements AbstractValue<Constant> { protected /*@Nullable*/ Integer value; public enum Type { - CONSTANT, TOP, BOTTOM, + CONSTANT, + TOP, + BOTTOM, } public Constant(Type type) { @@ -56,12 +57,15 @@ public class Constant implements AbstractValue<Constant> { @Override public Constant leastUpperBound(Constant other) { - if (other.isBottom()) + if (other.isBottom()) { return this.copy(); - if (this.isBottom()) + } + if (this.isBottom()) { return other.copy(); - if (other.isTop() || this.isTop()) + } + if (other.isTop() || this.isTop()) { return new Constant(Type.TOP); + } if (other.getValue().equals(getValue())) { return this.copy(); } @@ -70,8 +74,9 @@ public class Constant implements AbstractValue<Constant> { @Override public boolean equals(Object obj) { - if (obj == null || !(obj instanceof Constant)) + if (obj == null || !(obj instanceof Constant)) { return false; + } Constant other = (Constant) obj; return type == other.type && Objects.equals(value, other.value); } @@ -84,12 +89,12 @@ public class Constant implements AbstractValue<Constant> { @Override public String toString() { switch (type) { - case TOP: - return "T"; - case BOTTOM: - return "-"; - case CONSTANT: - return value.toString(); + case TOP: + return "T"; + case BOTTOM: + return "-"; + case CONSTANT: + return value.toString(); } assert false; return "???"; diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationStore.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationStore.java index 3c96f8fd82..e7a718ee5b 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationStore.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationStore.java @@ -3,16 +3,15 @@ package org.checkerframework.dataflow.constantpropagation; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; - import org.checkerframework.dataflow.analysis.FlowExpressions; import org.checkerframework.dataflow.analysis.Store; +import org.checkerframework.dataflow.cfg.CFGVisualizer; import org.checkerframework.dataflow.cfg.node.IntegerLiteralNode; import org.checkerframework.dataflow.cfg.node.LocalVariableNode; import org.checkerframework.dataflow.cfg.node.Node; import org.checkerframework.dataflow.constantpropagation.Constant.Type; -public class ConstantPropagationStore implements - Store<ConstantPropagationStore> { +public class ConstantPropagationStore implements Store<ConstantPropagationStore> { /** Information about variables gathered so far. */ Map<Node, Constant> contents; @@ -40,15 +39,13 @@ public class ConstantPropagationStore implements value = val; } // TODO: remove (only two nodes supported atm) - assert n instanceof IntegerLiteralNode - || n instanceof LocalVariableNode; + assert n instanceof IntegerLiteralNode || n instanceof LocalVariableNode; contents.put(n, value); } public void setInformation(Node n, Constant val) { // TODO: remove (only two nodes supported atm) - assert n instanceof IntegerLiteralNode - || n instanceof LocalVariableNode; + assert n instanceof IntegerLiteralNode || n instanceof LocalVariableNode; contents.put(n, val); } @@ -58,8 +55,7 @@ public class ConstantPropagationStore implements } @Override - public ConstantPropagationStore leastUpperBound( - ConstantPropagationStore other) { + public ConstantPropagationStore leastUpperBound(ConstantPropagationStore other) { Map<Node, Constant> newContents = new HashMap<>(); // go through all of the information of the other class @@ -88,18 +84,26 @@ public class ConstantPropagationStore implements } @Override + public ConstantPropagationStore widenedUpperBound(ConstantPropagationStore previous) { + return leastUpperBound(previous); + } + + @Override public boolean equals(Object o) { - if (o == null) + if (o == null) { return false; - if (!(o instanceof ConstantPropagationStore)) + } + if (!(o instanceof ConstantPropagationStore)) { return false; + } ConstantPropagationStore other = (ConstantPropagationStore) o; // go through all of the information of the other object for (Entry<Node, Constant> e : other.contents.entrySet()) { Node n = e.getKey(); Constant otherVal = e.getValue(); - if (otherVal.isBottom()) + if (otherVal.isBottom()) { continue; // no information + } if (contents.containsKey(n)) { if (!otherVal.equals(contents.get(n))) { return false; @@ -112,8 +116,9 @@ public class ConstantPropagationStore implements for (Entry<Node, Constant> e : contents.entrySet()) { Node n = e.getKey(); Constant thisVal = e.getValue(); - if (thisVal.isBottom()) + if (thisVal.isBottom()) { continue; // no information + } if (other.contents.containsKey(n)) { continue; } else { @@ -147,19 +152,12 @@ public class ConstantPropagationStore implements } @Override - public boolean canAlias(FlowExpressions.Receiver a, - FlowExpressions.Receiver b) { + public boolean canAlias(FlowExpressions.Receiver a, FlowExpressions.Receiver b) { return true; } @Override - public boolean hasDOToutput() { - return false; - } - - @Override - public String toDOToutput() { - return ""; + public void visualize(CFGVisualizer<?, ConstantPropagationStore, ?> viz) { + // Do nothing since ConstantPropagationStore doesn't support visualize } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationTransfer.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationTransfer.java index 434bb96433..d5da13a518 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationTransfer.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationTransfer.java @@ -1,7 +1,6 @@ package org.checkerframework.dataflow.constantpropagation; import java.util.List; - import org.checkerframework.dataflow.analysis.ConditionalTransferResult; import org.checkerframework.dataflow.analysis.RegularTransferResult; import org.checkerframework.dataflow.analysis.TransferFunction; @@ -15,39 +14,36 @@ import org.checkerframework.dataflow.cfg.node.IntegerLiteralNode; import org.checkerframework.dataflow.cfg.node.LocalVariableNode; import org.checkerframework.dataflow.cfg.node.Node; -import com.sun.tools.javac.tree.JCTree.JCIdent; - - public class ConstantPropagationTransfer - extends - AbstractNodeVisitor<TransferResult<Constant, ConstantPropagationStore>, TransferInput<Constant, ConstantPropagationStore>> + extends AbstractNodeVisitor< + TransferResult<Constant, ConstantPropagationStore>, + TransferInput<Constant, ConstantPropagationStore>> implements TransferFunction<Constant, ConstantPropagationStore> { @Override - public ConstantPropagationStore initialStore(UnderlyingAST underlyingAST, - List<LocalVariableNode> parameters) { + public ConstantPropagationStore initialStore( + UnderlyingAST underlyingAST, List<LocalVariableNode> parameters) { ConstantPropagationStore store = new ConstantPropagationStore(); return store; } @Override public TransferResult<Constant, ConstantPropagationStore> visitLocalVariable( - LocalVariableNode node, TransferInput<Constant, ConstantPropagationStore> before) { + LocalVariableNode node, TransferInput<Constant, ConstantPropagationStore> before) { ConstantPropagationStore store = before.getRegularStore(); Constant value = store.getInformation(node); return new RegularTransferResult<>(value, store); } @Override - public TransferResult<Constant, ConstantPropagationStore> visitNode(Node n, - TransferInput<Constant, ConstantPropagationStore> p) { + public TransferResult<Constant, ConstantPropagationStore> visitNode( + Node n, TransferInput<Constant, ConstantPropagationStore> p) { return new RegularTransferResult<>(null, p.getRegularStore()); } @Override public TransferResult<Constant, ConstantPropagationStore> visitAssignment( - AssignmentNode n, - TransferInput<Constant, ConstantPropagationStore> pi) { + AssignmentNode n, TransferInput<Constant, ConstantPropagationStore> pi) { ConstantPropagationStore p = pi.getRegularStore(); Node target = n.getTarget(); Constant info = null; @@ -61,8 +57,7 @@ public class ConstantPropagationTransfer @Override public TransferResult<Constant, ConstantPropagationStore> visitIntegerLiteral( - IntegerLiteralNode n, - TransferInput<Constant, ConstantPropagationStore> pi) { + IntegerLiteralNode n, TransferInput<Constant, ConstantPropagationStore> pi) { ConstantPropagationStore p = pi.getRegularStore(); Constant c = new Constant(n.getValue()); p.setInformation(n, c); @@ -87,5 +82,4 @@ public class ConstantPropagationTransfer p.setInformation(b, val); } } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Deterministic.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Deterministic.java index e79543fbb1..7e7d8b9cdc 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Deterministic.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Deterministic.java @@ -7,88 +7,81 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * A method is called <em>deterministic</em> if it returns the same value - * (according to <tt>==</tt>) every time it is called with the same - * parameters and in the same environment. The parameters include the - * receiver, and the environment includes all of the Java heap (that is, - * all fields of all objects and all static variables). - * <p> - * This annotation is important to pluggable type-checking because, after a - * call to a <tt>@Deterministic</tt> method, flow-sensitive type refinement - * can assume that anything learned about the first invocation is true - * about subsequent invocations (so long as no non-<tt>@</tt>{@link - * SideEffectFree} method call intervenes). For example, - * the following code never suffers a null pointer - * exception, so the Nullness Checker need not issue a warning: - * <pre><code> if (x.myDeterministicMethod() != null) { - x.myDeterministicMethod().hashCode(); - }</code></pre> - * <p> - * Note that <tt>@Deterministic</tt> guarantees that the result is - * identical according to <tt>==</tt>, <b>not</b> equal according to - * <tt>equals</tt>. This means that writing <tt>@Deterministic</tt> on a - * method that returns a reference is often erroneous unless the - * returned value is cached or interned. - * <p> - * Also see {@link Pure}, which means both deterministic and {@link - * SideEffectFree}. - * <p> - * <b>Analysis:</b> - * The Checker Framework performs a conservative analysis to verify a - * <tt>@Deterministic</tt> annotation. The Checker Framework issues a - * warning if the method uses any of the following Java constructs: + * A method is called <em>deterministic</em> if it returns the same value (according to {@code ==}) + * every time it is called with the same parameters and in the same environment. The parameters + * include the receiver, and the environment includes all of the Java heap (that is, all fields of + * all objects and all static variables). + * + * <p>Determinism refers to the return value during a non-exceptional execution. If a method throws + * an exception, the Throwable does not have to be exactly the same object on each invocation (and + * generally should not be, to capture the correct stack trace). + * + * <p>This annotation is important to pluggable type-checking because, after a call to a + * {@code @Deterministic} method, flow-sensitive type refinement can assume that anything learned + * about the first invocation is true about subsequent invocations (so long as no + * non-{@code @}{@link SideEffectFree} method call intervenes). For example, the following code + * never suffers a null pointer exception, so the Nullness Checker need not issue a warning: + * + * <pre>{@code + * if (x.myDeterministicMethod() != null) { + * x.myDeterministicMethod().hashCode(); + * } + * }</pre> + * + * <p>Note that {@code @Deterministic} guarantees that the result is identical according to {@code + * ==}, <b>not</b> just equal according to {@code equals()}. This means that writing <code> + * {@literal @}Deterministic</code> on a method that returns a reference (including a String) is + * often erroneous unless the returned value is cached or interned. + * + * <p>Also see {@link Pure}, which means both deterministic and {@link SideEffectFree}. + * + * <p><b>Analysis:</b> The Checker Framework performs a conservative analysis to verify a + * {@code @Deterministic} annotation. The Checker Framework issues a warning if the method uses any + * of the following Java constructs: + * * <ol> - * <li>Assignment to any expression, except for local variables (and method - * parameters). - * <li>A method invocation of a method that is not {@link Deterministic}. - * <li>Construction of a new object. - * <li>Catching any exceptions. This is to prevent a method to get a hold of - * newly created objects and using these objects (or some property thereof) - * to change their return value. For instance, the following method must be - * forbidden. - * <pre> - <code> - @Deterministic - int f() { - try { - int b = 0; - int a = 1/b; - } catch (Throwable t) { - return t.hashCode(); - } - return 0; - } - </code> -</pre> + * <li>Assignment to any expression, except for local variables (and method parameters). + * <li>A method invocation of a method that is not {@link Deterministic}. + * <li>Construction of a new object. + * <li>Catching any exceptions. This is to prevent a method to get a hold of newly created objects + * and using these objects (or some property thereof) to change their return value. For + * instance, the following method must be forbidden. + * <pre> + * {@code @Deterministic + * int f() { + * try { + * int b = 0; + * int a = 1/b; + * } catch (Throwable t) { + * return t.hashCode(); + * } + * return 0; + * } + * }</pre> * </ol> - * A constructor can be <tt>@Pure</tt>, but a constructor <em>invocation</em> is - * not deterministic since it returns a different new object each time. - * TODO: Side-effect-free constructors could be allowed to set their own fields. - * <p> * - * Note that the rules for checking currently imply that every {@code - * Deterministic} method is also {@link SideEffectFree}. This might change - * in the future; in general, a deterministic method does not need to be - * side-effect-free. - * <p> + * A constructor can be {@code @Pure}, but a constructor <em>invocation</em> is not deterministic + * since it returns a different new object each time. TODO: Side-effect-free constructors could be + * allowed to set their own fields. * - * These rules are conservative: any code that passes the checks is - * deterministic, but the Checker Framework may issue false positive - * warnings, for code that uses one of the forbidden constructs but is - * deterministic nonetheless. - * <p> + * <p>Note that the rules for checking currently imply that every {@code Deterministic} method is + * also {@link SideEffectFree}. This might change in the future; in general, a deterministic method + * does not need to be side-effect-free. * - * In fact, the rules are so conservative that checking is currently - * disabled by default, but can be enabled via the - * <tt>-AcheckPurityAnnotations</tt> command-line option. - * <p> + * <p>These rules are conservative: any code that passes the checks is deterministic, but the + * Checker Framework may issue false positive warnings, for code that uses one of the forbidden + * constructs but is deterministic nonetheless. * - * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and flow-sensitive analysis + * <p>In fact, the rules are so conservative that checking is currently disabled by default, but can + * be enabled via the {@code -AcheckPurityAnnotations} command-line option. + * + * <p> * + * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and + * flow-sensitive analysis * @author Stefan Heule */ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) -public @interface Deterministic { -} +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface Deterministic {} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/LockingFree.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/LockingFree.java deleted file mode 100644 index a210c1a857..0000000000 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/LockingFree.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.checkerframework.dataflow.qual; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Behaves identically to @SideEffectFree when running the Lock Checker. - * Ignored by all other checkers. - * - * @see SideEffectFree - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) -public @interface LockingFree { -} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Pure.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Pure.java index a559c1db27..5a00db7a1b 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Pure.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Pure.java @@ -7,31 +7,24 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * {@code Pure} is a method annotation that means both {@link - * SideEffectFree} and {@link Deterministic}. The more important of these, - * when performing pluggable type-checking, is usually {@link - * SideEffectFree}. - * - * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and flow-sensitive analysis + * {@code Pure} is a method annotation that means both {@link SideEffectFree} and {@link + * Deterministic}. The more important of these, when performing pluggable type-checking, is usually + * {@link SideEffectFree}. * + * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and + * flow-sensitive analysis * @author Stefan Heule - * */ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) public @interface Pure { - /** - * The type of purity. - */ + /** The type of purity. */ public static enum Kind { /** The method has no visible side-effects. */ SIDE_EFFECT_FREE, - /** - * The method returns exactly the same value when called in the same - * environment. - */ + /** The method returns exactly the same value when called in the same environment. */ DETERMINISTIC } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/SideEffectFree.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/SideEffectFree.java index 0521113bab..6a969c4364 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/SideEffectFree.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/SideEffectFree.java @@ -7,57 +7,48 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * A method is called <em>side-effect-free</em> if it has no visible - * side-effects, such as setting a field of an object that existed before - * the method was called. - * <p> - * Only the visible side-effects are important. The method is allowed to cache - * the answer to a computationally expensive query, for instance. It is also - * allowed to modify newly-created objects, and a constructor is - * side-effect-free if it does not modify any objects that existed before - * it was called. - * <p> - * This annotation is important to pluggable type-checking because if some - * fact about an object is known before a call to such a method, then the - * fact is still known afterwards, even if the fact is about some non-final - * field. When any non-<tt>@SideEffectFree</tt> method is called, then a - * pluggable type-checker must assume that any field of any accessible - * object might have been modified, which annuls the effect of - * flow-sensitive type refinement and prevents the pluggable type-checker - * from making conclusions that are obvious to a programmer. - * <p> - * Also see {@link Pure}, which means both side-effect-free and {@link - * Deterministic}. - * <p> - * <b>Analysis:</b> - * The Checker Framework performs a conservative analysis to verify a - * <tt>@SideEffectFree</tt> annotation. - * The Checker Framework issues a warning - * if the method uses any of the following Java constructs: + * A method is called <em>side-effect-free</em> if it has no visible side-effects, such as setting a + * field of an object that existed before the method was called. + * + * <p>Only the visible side-effects are important. The method is allowed to cache the answer to a + * computationally expensive query, for instance. It is also allowed to modify newly-created + * objects, and a constructor is side-effect-free if it does not modify any objects that existed + * before it was called. + * + * <p>This annotation is important to pluggable type-checking because if some fact about an object + * is known before a call to such a method, then the fact is still known afterwards, even if the + * fact is about some non-final field. When any non-{@code @SideEffectFree} method is called, then a + * pluggable type-checker must assume that any field of any accessible object might have been + * modified, which annuls the effect of flow-sensitive type refinement and prevents the pluggable + * type-checker from making conclusions that are obvious to a programmer. + * + * <p>Also see {@link Pure}, which means both side-effect-free and {@link Deterministic}. + * + * <p><b>Analysis:</b> The Checker Framework performs a conservative analysis to verify a + * {@code @SideEffectFree} annotation. The Checker Framework issues a warning if the method uses any + * of the following Java constructs: + * * <ol> - * <li>Assignment to any expression, except for local variables and method - * parameters. - * <li>A method invocation of a method that is not <tt>@SideEffectFree</tt>. - * <li>Construction of a new object where the constructor is not <tt>@SideEffectFree</tt>. + * <li>Assignment to any expression, except for local variables and method parameters. + * <li>A method invocation of a method that is not {@code @SideEffectFree}. + * <li>Construction of a new object where the constructor is not {@code @SideEffectFree}. * </ol> - * These rules are conservative: any code that passes the checks is - * side-effect-free, but the Checker Framework may issue false positive - * warnings, for code that uses one of the forbidden constructs but is - * side-effect-free nonetheless. In particular, a method that caches its - * result will be rejected. - * <p> * - * In fact, the rules are so conservative that checking is currently - * disabled by default, but can be enabled via the - * <tt>-AcheckPurityAnnotations</tt> command-line option. - * <p> + * These rules are conservative: any code that passes the checks is side-effect-free, but the + * Checker Framework may issue false positive warnings, for code that uses one of the forbidden + * constructs but is side-effect-free nonetheless. In particular, a method that caches its result + * will be rejected. + * + * <p>In fact, the rules are so conservative that checking is currently disabled by default, but can + * be enabled via the {@code -AcheckPurityAnnotations} command-line option. * - * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and flow-sensitive analysis + * <p> * + * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and + * flow-sensitive analysis * @author Stefan Heule */ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) -public @interface SideEffectFree { -} +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface SideEffectFree {} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/TerminatesExecution.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/TerminatesExecution.java index 93ff95c546..4e83dcdb64 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/TerminatesExecution.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/TerminatesExecution.java @@ -7,32 +7,28 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * {@code TerminatesExecution} is a method annotation that indicates that a - * method terminates the execution of the program. This can be used to - * annotate methods such as {@code System.exit()}. - * <p> - - * The annotation enables flow-sensitive type refinement to be more - * precise. For example, after + * {@code TerminatesExecution} is a method annotation that indicates that a method terminates the + * execution of the program. This can be used to annotate methods such as {@code System.exit()}. + * + * <p>The annotation enables flow-sensitive type refinement to be more precise. For example, after + * * <pre> * if (x == null) { * System.err.println("Bad value supplied"); * System.exit(1); * } * </pre> - * the Nullness Checker can determine that <tt>x</tt> is non-null. - * - * <p> - * The annotation is a <em>trusted</em> annotation, meaning that it is not - * checked whether the annotated method really does terminate the program. - * - * @checker_framework.manual #type-refinement Automatic type refinement (flow-sensitive type qualifier inference) * + * the Nullness Checker can determine that {@code x} is non-null. + * + * <p>The annotation is a <em>trusted</em> annotation, meaning that it is not checked whether the + * annotated method really does terminate the program. + * + * @checker_framework.manual #type-refinement Automatic type refinement (flow-sensitive type + * qualifier inference) * @author Stefan Heule - * */ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) -public @interface TerminatesExecution { -} +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface TerminatesExecution {} diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/HashCodeUtils.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/HashCodeUtils.java index 200f96dfa9..f898e3c9b4 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/HashCodeUtils.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/HashCodeUtils.java @@ -4,7 +4,6 @@ package org.checkerframework.dataflow.util; * Utility class to implement the {@code hashCode} method. * * @author Stefan Heule - * */ public class HashCodeUtils { @@ -47,8 +46,9 @@ public class HashCodeUtils { /** Add an object to a given hash. */ public static int hash(int hash, Object item) { - if (item == null) + if (item == null) { return hash * prime; + } return hash * prime + item.hashCode(); } @@ -85,8 +85,9 @@ public class HashCodeUtils { /** Hash an object. */ public static int hash(Object item) { - if (item == null) + if (item == null) { return 0; + } return item.hashCode(); } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/MostlySingleton.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/MostlySingleton.java index 44bdbd6f61..7e9172da91 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/MostlySingleton.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/MostlySingleton.java @@ -8,13 +8,14 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; -/** - * A set that is more efficient than HashSet for 0 and 1 elements. - */ -final public class MostlySingleton<T> implements Set<T> { +/** A set that is more efficient than HashSet for 0 and 1 elements. */ +public final class MostlySingleton<T> implements Set<T> { private enum State { - EMPTY, SINGLETON, ANY + EMPTY, + SINGLETON, + ANY } + private State state = State.EMPTY; private T value; private HashSet<T> set; @@ -22,14 +23,14 @@ final public class MostlySingleton<T> implements Set<T> { @Override public int size() { switch (state) { - case EMPTY: - return 0; - case SINGLETON: - return 1; - case ANY: - return set.size(); - default: - throw new AssertionError(); + case EMPTY: + return 0; + case SINGLETON: + return 1; + case ANY: + return set.size(); + default: + throw new AssertionError(); } } @@ -41,14 +42,14 @@ final public class MostlySingleton<T> implements Set<T> { @Override public boolean contains(Object o) { switch (state) { - case EMPTY: - return false; - case SINGLETON: - return Objects.equals(o, value); - case ANY: - return set.contains(o); - default: - throw new AssertionError(); + case EMPTY: + return false; + case SINGLETON: + return Objects.equals(o, value); + case ANY: + return set.contains(o); + default: + throw new AssertionError(); } } @@ -56,55 +57,55 @@ final public class MostlySingleton<T> implements Set<T> { @SuppressWarnings("fallthrough") public boolean add(T e) { switch (state) { - case EMPTY: - state = State.SINGLETON; - value = e; - return true; - case SINGLETON: - state = State.ANY; - set = new HashSet<T>(); - set.add(value); - value = null; - // fallthrough - case ANY: - return set.add(e); - default: - throw new AssertionError(); + case EMPTY: + state = State.SINGLETON; + value = e; + return true; + case SINGLETON: + state = State.ANY; + set = new HashSet<T>(); + set.add(value); + value = null; + // fallthrough + case ANY: + return set.add(e); + default: + throw new AssertionError(); } } @Override public Iterator<T> iterator() { switch (state) { - case EMPTY: - return Collections.emptyIterator(); - case SINGLETON: - return new Iterator<T>() { - private boolean hasNext = true; - - @Override - public boolean hasNext() { - return hasNext; - } - - @Override - public T next() { - if (hasNext) { - hasNext = false; - return value; + case EMPTY: + return Collections.emptyIterator(); + case SINGLETON: + return new Iterator<T>() { + private boolean hasNext = true; + + @Override + public boolean hasNext() { + return hasNext; + } + + @Override + public T next() { + if (hasNext) { + hasNext = false; + return value; + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); } - throw new NoSuchElementException(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - case ANY: - return set.iterator(); - default: - throw new AssertionError(); + }; + case ANY: + return set.iterator(); + default: + throw new AssertionError(); } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/NodeUtils.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/NodeUtils.java index d429d238e9..cf7a090024 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/NodeUtils.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/NodeUtils.java @@ -1,26 +1,24 @@ package org.checkerframework.dataflow.util; -import org.checkerframework.javacutil.TypesUtils; - -import org.checkerframework.dataflow.cfg.node.ConditionalOrNode; -import org.checkerframework.dataflow.cfg.node.Node; - import com.sun.source.tree.Tree; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; +import javax.lang.model.type.TypeKind; +import org.checkerframework.dataflow.cfg.node.ConditionalOrNode; +import org.checkerframework.dataflow.cfg.node.FieldAccessNode; +import org.checkerframework.dataflow.cfg.node.Node; +import org.checkerframework.javacutil.TypesUtils; /** * A utility class to operate on a given {@link Node}. * * @author Stefan Heule - * */ public class NodeUtils { /** - * @return true iff <code>node</code> corresponds to a boolean typed - * expression (either the primitive type <code>boolean</code>, or - * class type {@link java.lang.Boolean}) + * @return true iff {@code node} corresponds to a boolean typed expression (either the primitive + * type {@code boolean}, or class type {@link java.lang.Boolean}) */ public static boolean isBooleanTypeNode(Node node) { @@ -42,4 +40,17 @@ public class NodeUtils { return false; } + + /** + * @return true iff {@code node} is a {@link FieldAccessNode} that is an access to an array's + * length + */ + public static boolean isArrayLengthFieldAccess(Node node) { + if (!(node instanceof FieldAccessNode)) { + return false; + } + FieldAccessNode fieldAccess = (FieldAccessNode) node; + return fieldAccess.getFieldName().equals("length") + && fieldAccess.getReceiver().getType().getKind() == TypeKind.ARRAY; + } } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityChecker.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityChecker.java index 5fafb7a8a1..0b298f1955 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityChecker.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityChecker.java @@ -5,23 +5,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey; */ -import org.checkerframework.dataflow.qual.Deterministic; -import org.checkerframework.dataflow.qual.Pure; -import org.checkerframework.dataflow.qual.Pure.Kind; -import org.checkerframework.dataflow.qual.SideEffectFree; - -import org.checkerframework.javacutil.AnnotationProvider; -import org.checkerframework.javacutil.InternalUtils; -import org.checkerframework.javacutil.Pair; -import org.checkerframework.javacutil.TreeUtils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.List; - -import javax.lang.model.element.Element; - import com.sun.source.tree.ArrayAccessTree; import com.sun.source.tree.AssertTree; import com.sun.source.tree.AssignmentTree; @@ -64,39 +47,48 @@ import com.sun.source.tree.VariableTree; import com.sun.source.tree.WhileLoopTree; import com.sun.source.util.SimpleTreeVisitor; import com.sun.tools.javac.tree.TreeScanner; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import javax.lang.model.element.Element; +import org.checkerframework.dataflow.qual.Deterministic; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.dataflow.qual.Pure.Kind; +import org.checkerframework.dataflow.qual.SideEffectFree; +import org.checkerframework.javacutil.AnnotationProvider; +import org.checkerframework.javacutil.InternalUtils; +import org.checkerframework.javacutil.Pair; +import org.checkerframework.javacutil.TreeUtils; /** * A visitor that determines the purity (as defined by {@link - * org.checkerframework.dataflow.qual.SideEffectFree}, {@link org.checkerframework.dataflow.qual.Deterministic}, - * and {@link org.checkerframework.dataflow.qual.Pure}) of a statement or expression. The - * entry point is method {@link #checkPurity}. + * org.checkerframework.dataflow.qual.SideEffectFree}, {@link + * org.checkerframework.dataflow.qual.Deterministic}, and {@link + * org.checkerframework.dataflow.qual.Pure}) of a statement or expression. The entry point is method + * {@link #checkPurity}. * * @see SideEffectFree * @see Deterministic * @see Pure - * * @author Stefan Heule - * */ public class PurityChecker { /** - * Compute whether the given statement is - * side-effect-free, deterministic, or both. - * Returns a result that can be queried. + * Compute whether the given statement is side-effect-free, deterministic, or both. Returns a + * result that can be queried. */ - public static PurityResult checkPurity(Tree statement, - AnnotationProvider annoProvider, boolean assumeSideEffectFree) { + public static PurityResult checkPurity( + Tree statement, AnnotationProvider annoProvider, boolean assumeSideEffectFree) { PurityCheckerHelper helper = new PurityCheckerHelper(annoProvider, assumeSideEffectFree); PurityResult res = helper.scan(statement, new PurityResult()); return res; } /** - * Result of the {@link PurityChecker}. - * Can be queried queried regarding whether a given tree was - * side-effect-free, deterministic, or both; also gives reasons if - * the answer is "no". + * Result of the {@link PurityChecker}. Can be queried regarding whether a given tree was + * side-effect-free, deterministic, or both; also gives reasons if the answer is "no". */ public static class PurityResult { @@ -121,48 +113,38 @@ public class PurityChecker { return types.containsAll(kinds); } - /** - * Get the {@code reason}s why the method is not side-effect-free. - */ + /** Get the {@code reason}s why the method is not side-effect-free. */ public List<Pair<Tree, String>> getNotSeFreeReasons() { return notSeFreeReasons; } - /** - * Add {@code reason} as a reason why the method is not side-effect - * free. - */ + /** Add {@code reason} as a reason why the method is not side-effect free. */ public void addNotSeFreeReason(Tree t, String msgId) { notSeFreeReasons.add(Pair.of(t, msgId)); types.remove(Kind.SIDE_EFFECT_FREE); } - /** - * Get the {@code reason}s why the method is not deterministic. - */ + /** Get the {@code reason}s why the method is not deterministic. */ public List<Pair<Tree, String>> getNotDetReasons() { return notDetReasons; } - /** - * Add {@code reason} as a reason why the method is not deterministic. - */ + /** Add {@code reason} as a reason why the method is not deterministic. */ public void addNotDetReason(Tree t, String msgId) { notDetReasons.add(Pair.of(t, msgId)); types.remove(Kind.DETERMINISTIC); } /** - * Get the {@code reason}s why the method is not both side-effect-free - * and deterministic. + * Get the {@code reason}s why the method is not both side-effect-free and deterministic. */ public List<Pair<Tree, String>> getNotBothReasons() { return notBothReasons; } /** - * Add {@code reason} as a reason why the method is not both side-effect - * free and deterministic. + * Add {@code reason} as a reason why the method is not both side-effect free and + * deterministic. */ public void addNotBothReason(Tree t, String msgId) { notBothReasons.add(Pair.of(t, msgId)); @@ -172,19 +154,21 @@ public class PurityChecker { } /** - * Helper class to keep {@link PurityChecker}'s interface clean. The - * implementation is heavily based on {@link TreeScanner}, but some parts of - * the AST are skipped (such as types or modifiers). Furthermore, scanning - * works differently in that the input parameter (usually named {@code p}) - * gets "threaded through", instead of using {@code reduce}. + * Helper class to keep {@link PurityChecker}'s interface clean. The implementation is heavily + * based on {@link TreeScanner}, but some parts of the AST are skipped (such as types or + * modifiers). Furthermore, scanning works differently in that the input parameter (usually + * named {@code p}) gets "threaded through", instead of using {@code reduce}. */ protected static class PurityCheckerHelper extends SimpleTreeVisitor<PurityResult, PurityResult> { protected final AnnotationProvider annoProvider; - /** True if all methods should be assumed to be @SideEffectFree, - * for the purposes of org.checkerframework.dataflow analysis. */ + /** + * True if all methods should be assumed to be @SideEffectFree, for the purposes of + * org.checkerframework.dataflow analysis. + */ private final boolean assumeSideEffectFree; + protected /*@Nullable*/ List<Element> methodParameter; public PurityCheckerHelper(AnnotationProvider annoProvider, boolean assumeSideEffectFree) { @@ -192,16 +176,12 @@ public class PurityChecker { this.assumeSideEffectFree = assumeSideEffectFree; } - /** - * Scan a single node. - */ + /** Scan a single node. */ public PurityResult scan(Tree node, PurityResult p) { return node == null ? p : node.accept(this, p); } - /** - * Scan a list of nodes. - */ + /** Scan a list of nodes. */ public PurityResult scan(Iterable<? extends Tree> nodes, PurityResult p) { PurityResult r = p; if (nodes != null) { @@ -229,8 +209,7 @@ public class PurityChecker { } @Override - public PurityResult visitEmptyStatement(EmptyStatementTree node, - PurityResult p) { + public PurityResult visitEmptyStatement(EmptyStatementTree node, PurityResult p) { return p; } @@ -240,8 +219,7 @@ public class PurityChecker { } @Override - public PurityResult visitDoWhileLoop(DoWhileLoopTree node, - PurityResult p) { + public PurityResult visitDoWhileLoop(DoWhileLoopTree node, PurityResult p) { PurityResult r = scan(node.getStatement(), p); r = scan(node.getCondition(), r); return r; @@ -264,8 +242,7 @@ public class PurityChecker { } @Override - public PurityResult visitEnhancedForLoop(EnhancedForLoopTree node, - PurityResult p) { + public PurityResult visitEnhancedForLoop(EnhancedForLoopTree node, PurityResult p) { PurityResult r = scan(node.getVariable(), p); r = scan(node.getExpression(), r); r = scan(node.getStatement(), r); @@ -273,8 +250,7 @@ public class PurityChecker { } @Override - public PurityResult visitLabeledStatement(LabeledStatementTree node, - PurityResult p) { + public PurityResult visitLabeledStatement(LabeledStatementTree node, PurityResult p) { return scan(node.getStatement(), p); } @@ -293,8 +269,7 @@ public class PurityChecker { } @Override - public PurityResult visitSynchronized(SynchronizedTree node, - PurityResult p) { + public PurityResult visitSynchronized(SynchronizedTree node, PurityResult p) { PurityResult r = scan(node.getExpression(), p); r = scan(node.getBlock(), r); return r; @@ -335,8 +310,7 @@ public class PurityChecker { } @Override - public PurityResult visitExpressionStatement( - ExpressionStatementTree node, PurityResult p) { + public PurityResult visitExpressionStatement(ExpressionStatementTree node, PurityResult p) { return scan(node.getExpression(), p); } @@ -368,17 +342,15 @@ public class PurityChecker { } @Override - public PurityResult visitMethodInvocation(MethodInvocationTree node, - PurityResult p) { + public PurityResult visitMethodInvocation(MethodInvocationTree node, PurityResult p) { Element elt = TreeUtils.elementFromUse(node); String reason = "call"; if (!PurityUtils.hasPurityAnnotation(annoProvider, elt)) { p.addNotBothReason(node, reason); } else { boolean det = PurityUtils.isDeterministic(annoProvider, elt); - boolean seFree = (assumeSideEffectFree - || PurityUtils.isSideEffectFree(annoProvider, - elt)); + boolean seFree = + (assumeSideEffectFree || PurityUtils.isSideEffectFree(annoProvider, elt)); if (!det && !seFree) { p.addNotBothReason(node, reason); } else if (!det) { @@ -395,9 +367,9 @@ public class PurityChecker { @Override public PurityResult visitNewClass(NewClassTree node, PurityResult p) { Element methodElement = InternalUtils.symbol(node); - boolean sideEffectFree = (assumeSideEffectFree - || PurityUtils.isSideEffectFree(annoProvider, - methodElement)); + boolean sideEffectFree = + (assumeSideEffectFree + || PurityUtils.isSideEffectFree(annoProvider, methodElement)); if (sideEffectFree) { p.addNotDetReason(node, "object.creation"); } else { @@ -417,16 +389,14 @@ public class PurityChecker { } @Override - public PurityResult visitLambdaExpression(LambdaExpressionTree node, - PurityResult p) { + public PurityResult visitLambdaExpression(LambdaExpressionTree node, PurityResult p) { PurityResult r = scan(node.getParameters(), p); r = scan(node.getBody(), r); return r; } @Override - public PurityResult visitParenthesized(ParenthesizedTree node, - PurityResult p) { + public PurityResult visitParenthesized(ParenthesizedTree node, PurityResult p) { return scan(node.getExpression(), p); } @@ -439,8 +409,7 @@ public class PurityChecker { return r; } - protected PurityResult assignmentCheck(PurityResult p, - ExpressionTree variable) { + protected PurityResult assignmentCheck(PurityResult p, ExpressionTree variable) { if (TreeUtils.isFieldAccess(variable)) { // rhs is a field access p.addNotBothReason(variable, "assign.field"); @@ -455,13 +424,11 @@ public class PurityChecker { } protected boolean isLocalVariable(ExpressionTree variable) { - return variable instanceof IdentifierTree - && !TreeUtils.isFieldAccess(variable); + return variable instanceof IdentifierTree && !TreeUtils.isFieldAccess(variable); } @Override - public PurityResult visitCompoundAssignment( - CompoundAssignmentTree node, PurityResult p) { + public PurityResult visitCompoundAssignment(CompoundAssignmentTree node, PurityResult p) { ExpressionTree variable = node.getVariable(); p = assignmentCheck(p, variable); PurityResult r = scan(variable, p); @@ -494,22 +461,19 @@ public class PurityChecker { } @Override - public PurityResult visitArrayAccess(ArrayAccessTree node, - PurityResult p) { + public PurityResult visitArrayAccess(ArrayAccessTree node, PurityResult p) { PurityResult r = scan(node.getExpression(), p); r = scan(node.getIndex(), r); return r; } @Override - public PurityResult visitMemberSelect(MemberSelectTree node, - PurityResult p) { + public PurityResult visitMemberSelect(MemberSelectTree node, PurityResult p) { return scan(node.getExpression(), p); } @Override - public PurityResult visitMemberReference(MemberReferenceTree node, - PurityResult p) { + public PurityResult visitMemberReference(MemberReferenceTree node, PurityResult p) { assert false : "this type of tree is unexpected here"; return null; } @@ -524,5 +488,4 @@ public class PurityChecker { return p; } } - } diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityUtils.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityUtils.java index 6b19543c91..c175877a24 100644 --- a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityUtils.java +++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityUtils.java @@ -1,94 +1,79 @@ package org.checkerframework.dataflow.util; +import com.sun.source.tree.MethodTree; import java.util.ArrayList; import java.util.List; - import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; - import org.checkerframework.dataflow.qual.Deterministic; import org.checkerframework.dataflow.qual.Pure; -import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.Pure.Kind; - +import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.javacutil.AnnotationProvider; import org.checkerframework.javacutil.InternalUtils; -import com.sun.source.tree.MethodTree; - /** - * An utility class for working with the {@link SideEffectFree}, {@link - * Deterministic}, and {@link Pure} annotations. + * An utility class for working with the {@link SideEffectFree}, {@link Deterministic}, and {@link + * Pure} annotations. * * @see SideEffectFree * @see Deterministic * @see Pure - * * @author Stefan Heule - * */ public class PurityUtils { /** Does the method {@code tree} have any purity annotation? */ - public static boolean hasPurityAnnotation(AnnotationProvider provider, - MethodTree tree) { + public static boolean hasPurityAnnotation(AnnotationProvider provider, MethodTree tree) { return !getPurityKinds(provider, tree).isEmpty(); } /** Does the method {@code methodElement} have any purity annotation? */ - public static boolean hasPurityAnnotation(AnnotationProvider provider, - Element methodElement) { + public static boolean hasPurityAnnotation(AnnotationProvider provider, Element methodElement) { return !getPurityKinds(provider, methodElement).isEmpty(); } /** Is the method {@code tree} deterministic? */ - public static boolean isDeterministic(AnnotationProvider provider, - MethodTree tree) { + public static boolean isDeterministic(AnnotationProvider provider, MethodTree tree) { Element methodElement = InternalUtils.symbol(tree); return isDeterministic(provider, methodElement); } /** Is the method {@code methodElement} deterministic? */ - public static boolean isDeterministic(AnnotationProvider provider, - Element methodElement) { + public static boolean isDeterministic(AnnotationProvider provider, Element methodElement) { List<Kind> kinds = getPurityKinds(provider, methodElement); return kinds.contains(Kind.DETERMINISTIC); } /** Is the method {@code tree} side-effect-free? */ - public static boolean isSideEffectFree(AnnotationProvider provider, - MethodTree tree) { + public static boolean isSideEffectFree(AnnotationProvider provider, MethodTree tree) { Element methodElement = InternalUtils.symbol(tree); return isSideEffectFree(provider, methodElement); } /** Is the method {@code methodElement} side-effect-free? */ - public static boolean isSideEffectFree(AnnotationProvider provider, - Element methodElement) { + public static boolean isSideEffectFree(AnnotationProvider provider, Element methodElement) { List<Kind> kinds = getPurityKinds(provider, methodElement); return kinds.contains(Kind.SIDE_EFFECT_FREE); } - /** - * @return The types of purity of the method {@code tree}. - */ - public static List<Pure.Kind> getPurityKinds(AnnotationProvider provider, - MethodTree tree) { + /** @return the types of purity of the method {@code tree}. */ + public static List<Pure.Kind> getPurityKinds(AnnotationProvider provider, MethodTree tree) { Element methodElement = InternalUtils.symbol(tree); return getPurityKinds(provider, methodElement); } /** - * @return The types of purity of the method {@code methodElement}. + * @return the types of purity of the method {@code methodElement}. TODO: should the return type + * be an EnumSet? */ - public static List<Pure.Kind> getPurityKinds(AnnotationProvider provider, - Element methodElement) { - AnnotationMirror pureAnnotation = provider.getDeclAnnotation( - methodElement, Pure.class); - AnnotationMirror sefAnnotation = provider.getDeclAnnotation( - methodElement, SideEffectFree.class); - AnnotationMirror detAnnotation = provider.getDeclAnnotation( - methodElement, Deterministic.class); + public static List<Pure.Kind> getPurityKinds( + AnnotationProvider provider, Element methodElement) { + AnnotationMirror pureAnnotation = provider.getDeclAnnotation(methodElement, Pure.class); + AnnotationMirror sefAnnotation = + provider.getDeclAnnotation(methodElement, SideEffectFree.class); + AnnotationMirror detAnnotation = + provider.getDeclAnnotation(methodElement, Deterministic.class); List<Pure.Kind> kinds = new ArrayList<>(); if (pureAnnotation != null) { |