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