aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/checker_framework_dataflow
diff options
context:
space:
mode:
authorGravatar Damien Martin-Guillerez <dmarting@google.com>2016-06-29 14:24:16 +0200
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-06-30 15:11:05 +0200
commita18add1613574a15c81f60bde847c5d7b2bedcb5 (patch)
tree103fa812c66403f5e8caefb898ea3f18107865dc /third_party/checker_framework_dataflow
parent14c7964b8bc0da2bab65b1c28aa8c302846d7720 (diff)
Adds the source of the checker framework
This needs to predate the rest of the changes to the checker framework to keep the build green. Also add the source of javacutil part of the checker framework, that will be included in the next change. Change-Id: Ie18d0e8e21035ce5141416e552a83d893f71b88b
Diffstat (limited to 'third_party/checker_framework_dataflow')
-rw-r--r--third_party/checker_framework_dataflow/LICENSE.txt417
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AbstractValue.java27
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Analysis.java718
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AnalysisResult.java237
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/ConditionalTransferResult.java137
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java940
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/RegularTransferResult.java130
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Store.java73
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferFunction.java49
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferInput.java283
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferResult.java116
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java4448
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGVisualizer.java181
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/ControlFlowGraph.java254
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/DOTCFGVisualizer.java508
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/JavaSource2CFGDOT.java275
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/UnderlyingAST.java136
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/Block.java37
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/BlockImpl.java63
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlock.java48
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlockImpl.java87
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlock.java38
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlockImpl.java76
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlock.java38
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlockImpl.java67
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlock.java32
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlockImpl.java49
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlock.java34
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlockImpl.java24
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java382
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayAccessNode.java87
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayCreationNode.java135
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayTypeNode.java65
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssertionErrorNode.java85
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentContext.java140
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentNode.java95
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseAndNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseComplementNode.java73
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseOrNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseXorNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BooleanLiteralNode.java51
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CaseNode.java86
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CharacterLiteralNode.java53
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java117
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalAndNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalNotNode.java73
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalOrNode.java82
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/DoubleLiteralNode.java52
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/EqualToNode.java82
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ExplicitThisLiteralNode.java44
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FieldAccessNode.java113
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatLiteralNode.java52
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingDivisionNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingRemainderNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FunctionalInterfaceNode.java93
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanOrEqualNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ImplicitThisLiteralNode.java33
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java92
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerDivisionNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerLiteralNode.java53
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerRemainderNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LeftShiftNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanNode.java85
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanOrEqualNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LocalVariableNode.java106
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LongLiteralNode.java52
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MarkerNode.java89
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodAccessNode.java84
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodInvocationNode.java129
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NarrowingConversionNode.java80
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/Node.java171
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java163
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NotEqualNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullChkNode.java72
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullLiteralNode.java51
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalAdditionNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMinusNode.java73
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMultiplicationNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalPlusNode.java73
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalSubtractionNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ObjectCreationNode.java106
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PackageNameNode.java110
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ParameterizedTypeNode.java73
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PrimitiveTypeNode.java65
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ReturnNode.java101
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ShortLiteralNode.java57
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SignedRightShiftNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateAssignmentNode.java60
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConversionNode.java82
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringLiteralNode.java55
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SuperNode.java71
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SynchronizedNode.java91
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TernaryExpressionNode.java94
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThisLiteralNode.java48
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThrowNode.java74
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TypeCastNode.java76
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnsignedRightShiftNode.java83
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ValueLiteralNode.java80
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/VariableDeclarationNode.java76
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/WideningConversionNode.java80
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/playground/ConstantPropagationPlayground.java32
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/Constant.java101
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationStore.java165
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationTransfer.java88
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Deterministic.java92
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Pure.java37
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/SideEffectFree.java63
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/TerminatesExecution.java38
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/HashCodeUtils.java103
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/MostlySingleton.java150
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/NodeUtils.java45
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityChecker.java528
-rw-r--r--third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityUtils.java107
115 files changed, 17413 insertions, 0 deletions
diff --git a/third_party/checker_framework_dataflow/LICENSE.txt b/third_party/checker_framework_dataflow/LICENSE.txt
new file mode 100644
index 0000000000..c4b232d9bc
--- /dev/null
+++ b/third_party/checker_framework_dataflow/LICENSE.txt
@@ -0,0 +1,417 @@
+Most of the Checker Framework is licensed under the GNU General Public
+License, version 2 (GPL2), with the classpath exception. The text of this
+license appears below. This is the same license used for OpenJDK.
+
+A few parts of the Checker Framework have more permissive licenses.
+
+ * The annotations are licensed under the MIT License. (The text of this
+ license appears below.) More specifically, all the parts of the Checker
+ Framework that you might want to include with your own program use the
+ MIT License. This is the checker-qual.jar file and all the files that
+ appear in it: every file in a qual/ directory, plus utility files such
+ as NullnessUtils.java, RegexUtil.java, UnsignednessUtil.java, etc.
+ In addition, the cleanroom implementations of third-party annotations,
+ which the Checker Framework recognizes as aliases for its own
+ annotations, are licensed under the MIT License.
+
+ * The Maven plugin is dual-licensed (you may use whichever you prefer)
+ under GPL2 and the Apache License, version 2.0 (Apache2). The text of
+ Apache2 appears in file maven-plugin/LICENSE.txt. Maven itself uses
+ Apache2.
+
+ * The Eclipse plugin is dual-licensed (you may use whichever you prefer)
+ under GPL2 and the Eclipse Public License Version 1.0 (EPL). EPL
+ appears http://www.eclipse.org/org/documents/epl-v10.php. Eclipse
+ itself uses EPL.
+
+Some external libraries that are included with the Checker Framework have
+different licenses.
+
+ * javaparser is licensed under the LGPL. (The javaparser source code
+ contains a file with the text of the GPL, but it is not clear why, since
+ javaparser does not use the GPL.) See file javaparser/COPYING.LESSER
+ and the source code of all its files.
+
+ * JUnit is licensed under the Common Public License v1.0 (see
+ http://www.junit.org/license), with parts (Hamcrest) licensed under the
+ BSD License (see http://hamcrest.org/JavaHamcrest/).
+
+ * plume-lib is licensed under the MIT License.
+
+The Checker Framework includes annotations for several libraries, in
+directory checker/jdk/. Each annotated library uses the same license as
+the unannotated version of the library.
+
+===========================================================================
+
+The GNU General Public License (GPL)
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to
+most of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you
+can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must
+make sure that they, too, receive or can get the source code. And you must
+show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced
+by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We
+wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program proprietary.
+To prevent this, we have made it clear that any patent must be licensed for
+everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or
+translated into another language. (Hereinafter, translation is included
+without limitation in the term "modification".) Each licensee is addressed as
+"you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is
+not restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the
+Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may
+at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such modifications
+or work under the terms of Section 1 above, provided that you also meet all of
+these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole or
+ in part contains or is derived from the Program or any part thereof, to be
+ licensed as a whole at no charge to all third parties under the terms of
+ this License.
+
+ c) If the modified program normally reads commands interactively when run,
+ you must cause it, when started running for such interactive use in the
+ most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a copy
+ of this License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on the Program is
+ not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License, and
+its terms, do not apply to those sections when you distribute them as separate
+works. But when you distribute the same sections as part of a whole which is a
+work based on the Program, the distribution of the whole must be on the terms
+of this License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based on
+the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and
+2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above
+ on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections 1
+ and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all
+the source code for all modules it contains, plus any associated interface
+definition files, plus the scripts used to control compilation and installation
+of the executable. However, as a special exception, the source code
+distributed need not include anything that is normally distributed (in either
+source or binary form) with the major components (compiler, kernel, and so on)
+of the operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so
+long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not
+accept this License. Therefore, by modifying or distributing the Program (or
+any work based on the Program), you indicate your acceptance of this License to
+do so, and all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to
+copy, distribute or modify the Program subject to these terms and conditions.
+You may not impose any further restrictions on the recipients' exercise of the
+rights granted herein. You are not responsible for enforcing compliance by
+third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution
+of the Program by all those who receive copies directly or indirectly through
+you, then the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and
+the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original
+copyright holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In
+such case, this License incorporates the limitation as if written in the body
+of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems
+or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any later
+version", you have the option of following the terms and conditions either of
+that version or of any later version published by the Free Software Foundation.
+If the Program does not specify a version number of this License, you may
+choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE
+PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
+ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE
+PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
+OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively convey the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+ One line to give the program's name and a brief idea of what it does.
+
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it
+starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
+ with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free
+ software, and you are welcome to redistribute it under certain conditions;
+ type 'show c' for details.
+
+The hypothetical commands 'show w' and 'show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may be
+called something other than 'show w' and 'show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school,
+if any, to sign a "copyright disclaimer" for the program, if necessary. Here
+is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ 'Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ signature of Ty Coon, 1 April 1989
+
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General Public
+License instead of this License.
+
+
+"CLASSPATH" EXCEPTION TO THE GPL
+
+Certain source files distributed by Oracle America and/or its affiliates are
+subject to the following clarification and special exception to the GPL, but
+only where Oracle has expressly included in the particular source file's header
+the words "Oracle designates this particular file as subject to the "Classpath"
+exception as provided by Oracle in the LICENSE file that accompanied this code."
+
+ Linking this library statically or dynamically with other modules is making
+ a combined work based on this library. Thus, the terms and conditions of
+ the GNU General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent modules,
+ and to copy and distribute the resulting executable under terms of your
+ choice, provided that you also meet, for each linked independent module,
+ the terms and conditions of the license of that module. An independent
+ module is a module which is not derived from or based on this library. If
+ you modify this library, you may extend this exception to your version of
+ the library, but you are not obligated to do so. If you do not wish to do
+ so, delete this exception statement from your version.
+
+===========================================================================
+
+MIT License:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===========================================================================
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AbstractValue.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AbstractValue.java
new file mode 100644
index 0000000000..2dbcbd4b03
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AbstractValue.java
@@ -0,0 +1,27 @@
+package org.checkerframework.dataflow.analysis;
+
+/**
+ * An abstract value used in the org.checkerframework.dataflow analysis.
+ *
+ * @author Stefan Heule
+ *
+ */
+public interface AbstractValue<V extends AbstractValue<V>> {
+
+ /**
+ * Compute the least upper bound of two stores.
+ *
+ * <p>
+ *
+ * <em>Important</em>: This method must fulfill the following contract:
+ * <ul>
+ * <li>Does not change {@code this}.</li>
+ * <li>Does not change {@code other}.</li>
+ * <li>Returns a fresh object which is not aliased yet.</li>
+ * <li>Returns an object of the same (dynamic) type as {@code this}, even if
+ * the signature is more permissive.</li>
+ * <li>Is commutative.</li>
+ * </ul>
+ */
+ V leastUpperBound(V other);
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Analysis.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Analysis.java
new file mode 100644
index 0000000000..2d0e7595de
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Analysis.java
@@ -0,0 +1,718 @@
+package org.checkerframework.dataflow.analysis;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import com.sun.source.tree.LambdaExpressionTree;
+import org.checkerframework.dataflow.cfg.ControlFlowGraph;
+import org.checkerframework.dataflow.cfg.UnderlyingAST;
+import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGLambda;
+import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGMethod;
+import org.checkerframework.dataflow.cfg.UnderlyingAST.Kind;
+import org.checkerframework.dataflow.cfg.block.Block;
+import org.checkerframework.dataflow.cfg.block.ConditionalBlock;
+import org.checkerframework.dataflow.cfg.block.ExceptionBlock;
+import org.checkerframework.dataflow.cfg.block.RegularBlock;
+import org.checkerframework.dataflow.cfg.block.SpecialBlock;
+import org.checkerframework.dataflow.cfg.node.AssignmentNode;
+import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
+import org.checkerframework.dataflow.cfg.node.Node;
+import org.checkerframework.dataflow.cfg.node.ReturnNode;
+
+import org.checkerframework.javacutil.ElementUtils;
+import org.checkerframework.javacutil.Pair;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+
+/**
+ * An implementation of an iterative algorithm to solve a org.checkerframework.dataflow problem,
+ * given a control flow graph and a transfer function.
+ *
+ * @author Stefan Heule
+ *
+ * @param <A>
+ * The abstract value type to be tracked by the analysis.
+ * @param <S>
+ * The store type used in the analysis.
+ * @param <T>
+ * The transfer function type that is used to approximated runtime
+ * behavior.
+ */
+public class Analysis<A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>> {
+
+ /** Is the analysis currently running? */
+ protected boolean isRunning = false;
+
+ /** The transfer function for regular nodes. */
+ protected T transferFunction;
+
+ /** The control flow graph to perform the analysis on. */
+ protected ControlFlowGraph cfg;
+
+ /** The associated processing environment */
+ protected final ProcessingEnvironment env;
+
+ /** Instance of the types utility. */
+ protected final Types types;
+
+ /**
+ * Then stores before every basic block (assumed to be 'no information' if
+ * not present).
+ */
+ protected IdentityHashMap<Block, S> thenStores;
+
+ /**
+ * Else stores before every basic block (assumed to be 'no information' if
+ * not present).
+ */
+ protected IdentityHashMap<Block, S> elseStores;
+
+ /**
+ * The transfer inputs before every basic block (assumed to be 'no information' if
+ * not present).
+ */
+ protected IdentityHashMap<Block, TransferInput<A, S>> inputs;
+
+ /**
+ * The stores after every return statement.
+ */
+ protected IdentityHashMap<ReturnNode, TransferResult<A, S>> storesAtReturnStatements;
+
+ /** The worklist used for the fix-point iteration. */
+ protected Worklist worklist;
+
+ /** Abstract values of nodes. */
+ protected IdentityHashMap<Node, A> nodeValues;
+
+ /** Map from (effectively final) local variable elements to their abstract value. */
+ public HashMap<Element, A> finalLocalValues;
+
+ /**
+ * The node that is currently handled in the analysis (if it is running).
+ * The following invariant holds:
+ *
+ * <pre>
+ * !isRunning &rArr; (currentNode == null)
+ * </pre>
+ */
+ protected Node currentNode;
+
+ /**
+ * The tree that is currently being looked at. The transfer function can set
+ * this tree to make sure that calls to {@code getValue} will not return
+ * information for this given tree.
+ */
+ protected Tree currentTree;
+
+ /**
+ * The current transfer input when the analysis is running.
+ */
+ protected TransferInput<A, S> currentInput;
+
+ public Tree getCurrentTree() {
+ return currentTree;
+ }
+
+ public void setCurrentTree(Tree currentTree) {
+ this.currentTree = currentTree;
+ }
+
+ /**
+ * Construct an object that can perform a org.checkerframework.dataflow analysis over a control
+ * flow graph. The transfer function is set later using
+ * {@code setTransferFunction}.
+ */
+ public Analysis(ProcessingEnvironment env) {
+ this.env = env;
+ types = env.getTypeUtils();
+ }
+
+ /**
+ * Construct an object that can perform a org.checkerframework.dataflow analysis over a control
+ * flow graph, given a transfer function.
+ */
+ public Analysis(ProcessingEnvironment env, T transfer) {
+ this(env);
+ this.transferFunction = transfer;
+ }
+
+ public void setTransferFunction(T transfer) {
+ this.transferFunction = transfer;
+ }
+
+ public T getTransferFunction() {
+ return transferFunction;
+ }
+
+ public Types getTypes() {
+ return types;
+ }
+
+ public ProcessingEnvironment getEnv() {
+ return env;
+ }
+
+ /**
+ * Perform the actual analysis. Should only be called once after the object
+ * has been created.
+ */
+ public void performAnalysis(ControlFlowGraph cfg) {
+ assert isRunning == false;
+ isRunning = true;
+
+ init(cfg);
+
+ while (!worklist.isEmpty()) {
+ Block b = worklist.poll();
+
+ switch (b.getType()) {
+ case REGULAR_BLOCK: {
+ RegularBlock rb = (RegularBlock) b;
+
+ // apply transfer function to contents
+ TransferInput<A, S> inputBefore = getInputBefore(rb);
+ currentInput = inputBefore.copy();
+ TransferResult<A, S> transferResult = null;
+ Node lastNode = null;
+ boolean addToWorklistAgain = false;
+ for (Node n : rb.getContents()) {
+ transferResult = callTransferFunction(n, currentInput);
+ addToWorklistAgain |= updateNodeValues(n, transferResult);
+ currentInput = new TransferInput<>(n, this, transferResult);
+ lastNode = n;
+ }
+ // loop will run at least one, making transferResult non-null
+
+ // propagate store to successors
+ Block succ = rb.getSuccessor();
+ assert succ != null : "regular basic block without non-exceptional successor unexpected";
+ propagateStoresTo(succ, lastNode, currentInput, rb.getFlowRule(), addToWorklistAgain);
+ break;
+ }
+
+ case EXCEPTION_BLOCK: {
+ ExceptionBlock eb = (ExceptionBlock) b;
+
+ // apply transfer function to content
+ TransferInput<A, S> inputBefore = getInputBefore(eb);
+ currentInput = inputBefore.copy();
+ Node node = eb.getNode();
+ TransferResult<A, S> transferResult = callTransferFunction(
+ node, currentInput);
+ boolean addToWorklistAgain = updateNodeValues(node, transferResult);
+
+ // propagate store to successor
+ Block succ = eb.getSuccessor();
+ if (succ != null) {
+ currentInput = new TransferInput<>(node, this, transferResult);
+ // TODO? Variable wasn't used.
+ // Store.FlowRule storeFlow = eb.getFlowRule();
+ propagateStoresTo(succ, node, currentInput, eb.getFlowRule(), addToWorklistAgain);
+ }
+
+ // propagate store to exceptional successors
+ for (Entry<TypeMirror, Set<Block>> e : eb.getExceptionalSuccessors()
+ .entrySet()) {
+ TypeMirror cause = e.getKey();
+ S exceptionalStore = transferResult
+ .getExceptionalStore(cause);
+ if (exceptionalStore != null) {
+ for (Block exceptionSucc : e.getValue()) {
+ addStoreBefore(exceptionSucc, node, exceptionalStore, Store.Kind.BOTH,
+ addToWorklistAgain);
+ }
+ } else {
+ for (Block exceptionSucc : e.getValue()) {
+ addStoreBefore(exceptionSucc, node, inputBefore.copy().getRegularStore(),
+ Store.Kind.BOTH, addToWorklistAgain);
+ }
+ }
+ }
+ break;
+ }
+
+ case CONDITIONAL_BLOCK: {
+ ConditionalBlock cb = (ConditionalBlock) b;
+
+ // get store before
+ TransferInput<A, S> inputBefore = getInputBefore(cb);
+ TransferInput<A, S> input = inputBefore.copy();
+
+ // propagate store to successor
+ Block thenSucc = cb.getThenSuccessor();
+ Block elseSucc = cb.getElseSuccessor();
+
+ propagateStoresTo(thenSucc, null, input, cb.getThenFlowRule(), false);
+ propagateStoresTo(elseSucc, null, input, cb.getElseFlowRule(), false);
+ break;
+ }
+
+ case SPECIAL_BLOCK: {
+ // special basic blocks are empty and cannot throw exceptions,
+ // thus there is no need to perform any analysis.
+ SpecialBlock sb = (SpecialBlock) b;
+ Block succ = sb.getSuccessor();
+ if (succ != null) {
+ propagateStoresTo(succ, null, getInputBefore(b), sb.getFlowRule(), false);
+ }
+ break;
+ }
+
+ default:
+ assert false;
+ break;
+ }
+ }
+
+ assert isRunning == true;
+ isRunning = false;
+ }
+
+ /**
+ * Propagate the stores in currentInput to the successor block, succ, according to the
+ * flowRule.
+ */
+ protected void propagateStoresTo(Block succ, Node node, TransferInput<A, S> currentInput,
+ Store.FlowRule flowRule, boolean addToWorklistAgain) {
+ switch (flowRule) {
+ case EACH_TO_EACH:
+ if (currentInput.containsTwoStores()) {
+ addStoreBefore(succ, node, currentInput.getThenStore(), Store.Kind.THEN,
+ addToWorklistAgain);
+ addStoreBefore(succ, node, currentInput.getElseStore(), Store.Kind.ELSE,
+ addToWorklistAgain);
+ } else {
+ addStoreBefore(succ, node, currentInput.getRegularStore(), Store.Kind.BOTH,
+ addToWorklistAgain);
+ }
+ break;
+ case THEN_TO_BOTH:
+ addStoreBefore(succ, node, currentInput.getThenStore(), Store.Kind.BOTH,
+ addToWorklistAgain);
+ break;
+ case ELSE_TO_BOTH:
+ addStoreBefore(succ, node, currentInput.getElseStore(), Store.Kind.BOTH,
+ addToWorklistAgain);
+ break;
+ case THEN_TO_THEN:
+ addStoreBefore(succ, node, currentInput.getThenStore(), Store.Kind.THEN,
+ addToWorklistAgain);
+ break;
+ case ELSE_TO_ELSE:
+ addStoreBefore(succ, node, currentInput.getElseStore(), Store.Kind.ELSE,
+ addToWorklistAgain);
+ break;
+ }
+ }
+
+ /**
+ * Updates the value of node {@code node} to the value of the
+ * {@code transferResult}. Returns true if the node's value changed, or a
+ * store was updated.
+ */
+ protected boolean updateNodeValues(Node node, TransferResult<A, S> transferResult) {
+ A newVal = transferResult.getResultValue();
+ boolean nodeValueChanged = false;
+
+ if (newVal != null) {
+ A oldVal = nodeValues.get(node);
+ nodeValues.put(node, newVal);
+ nodeValueChanged = !Objects.equals(oldVal, newVal);
+ }
+
+ return nodeValueChanged || transferResult.storeChanged();
+ }
+
+ /**
+ * Call the transfer function for node {@code node}, and set that node as
+ * current node first.
+ */
+ protected TransferResult<A, S> callTransferFunction(Node node,
+ TransferInput<A, S> store) {
+
+ if (node.isLValue()) {
+ // TODO: should the default behavior be to return either a regular
+ // transfer result or a conditional transfer result (depending on
+ // store.hasTwoStores()), or is the following correct?
+ return new RegularTransferResult<A, S>(null,
+ store.getRegularStore());
+ }
+ store.node = node;
+ currentNode = node;
+ TransferResult<A, S> transferResult = node.accept(transferFunction,
+ store);
+ currentNode = null;
+ if (node instanceof ReturnNode) {
+ // save a copy of the store to later check if some property held at
+ // a given return statement
+ storesAtReturnStatements.put((ReturnNode) node, transferResult);
+ }
+ if (node instanceof AssignmentNode) {
+ // store the flow-refined value for effectively final local variables
+ AssignmentNode assignment = (AssignmentNode) node;
+ Node lhst = assignment.getTarget();
+ if (lhst instanceof LocalVariableNode) {
+ LocalVariableNode lhs = (LocalVariableNode) lhst;
+ Element elem = lhs.getElement();
+ if (ElementUtils.isEffectivelyFinal(elem)) {
+ finalLocalValues.put(elem, transferResult.getResultValue());
+ }
+ }
+ }
+ return transferResult;
+ }
+
+ /** Initialize the analysis with a new control flow graph. */
+ protected void init(ControlFlowGraph cfg) {
+ this.cfg = cfg;
+ thenStores = new IdentityHashMap<>();
+ elseStores = new IdentityHashMap<>();
+ inputs = new IdentityHashMap<>();
+ storesAtReturnStatements = new IdentityHashMap<>();
+ worklist = new Worklist(cfg);
+ nodeValues = new IdentityHashMap<>();
+ finalLocalValues = new HashMap<>();
+ worklist.add(cfg.getEntryBlock());
+
+ List<LocalVariableNode> parameters = null;
+ UnderlyingAST underlyingAST = cfg.getUnderlyingAST();
+ if (underlyingAST.getKind() == Kind.METHOD) {
+ MethodTree tree = ((CFGMethod) underlyingAST).getMethod();
+ parameters = new ArrayList<>();
+ for (VariableTree p : tree.getParameters()) {
+ LocalVariableNode var = new LocalVariableNode(p);
+ parameters.add(var);
+ // TODO: document that LocalVariableNode has no block that it
+ // belongs to
+ }
+ } else if (underlyingAST.getKind() == Kind.LAMBDA) {
+ LambdaExpressionTree lambda = ((CFGLambda) underlyingAST).getLambdaTree();
+ parameters = new ArrayList<>();
+ for (VariableTree p : lambda.getParameters()) {
+ LocalVariableNode var = new LocalVariableNode(p);
+ parameters.add(var);
+ // TODO: document that LocalVariableNode has no block that it
+ // belongs to
+ }
+
+ } else {
+ // nothing to do
+ }
+ S initialStore = transferFunction.initialStore(underlyingAST, parameters);
+ Block entry = cfg.getEntryBlock();
+ thenStores.put(entry, initialStore);
+ elseStores.put(entry, initialStore);
+ inputs.put(entry, new TransferInput<>(null, this, initialStore));
+ }
+
+ /**
+ * Add a basic block to the worklist. If {@code b} is already present,
+ * the method does nothing.
+ */
+ protected void addToWorklist(Block b) {
+ // TODO: use a more efficient way to check if b is already present
+ if (!worklist.contains(b)) {
+ worklist.add(b);
+ }
+ }
+
+ /**
+ * Add a store before the basic block {@code b} by merging with the
+ * existing stores for that location.
+ */
+ protected void addStoreBefore(Block b, Node node, S s, Store.Kind kind,
+ boolean addBlockToWorklist) {
+ S thenStore = getStoreBefore(b, Store.Kind.THEN);
+ S elseStore = getStoreBefore(b, Store.Kind.ELSE);
+
+ switch (kind) {
+ case THEN: {
+ // Update the then store
+ S newThenStore = (thenStore != null) ?
+ thenStore.leastUpperBound(s) : s;
+ if (!newThenStore.equals(thenStore)) {
+ thenStores.put(b, newThenStore);
+ if (elseStore != null) {
+ inputs.put(b, new TransferInput<>(node, this, newThenStore, elseStore));
+ addBlockToWorklist = true;
+ }
+ }
+ break;
+ }
+ case ELSE: {
+ // Update the else store
+ S newElseStore = (elseStore != null) ?
+ elseStore.leastUpperBound(s) : s;
+ if (!newElseStore.equals(elseStore)) {
+ elseStores.put(b, newElseStore);
+ if (thenStore != null) {
+ inputs.put(b, new TransferInput<>(node, this, thenStore, newElseStore));
+ addBlockToWorklist = true;
+ }
+ }
+ break;
+ }
+ case BOTH:
+ if (thenStore == elseStore) {
+ // Currently there is only one regular store
+ S newStore = (thenStore != null) ?
+ thenStore.leastUpperBound(s) : s;
+ if (!newStore.equals(thenStore)) {
+ thenStores.put(b, newStore);
+ elseStores.put(b, newStore);
+ inputs.put(b, new TransferInput<>(node, this, newStore));
+ addBlockToWorklist = true;
+ }
+ } else {
+ boolean storeChanged = false;
+
+ S newThenStore = (thenStore != null) ?
+ thenStore.leastUpperBound(s) : s;
+ if (!newThenStore.equals(thenStore)) {
+ thenStores.put(b, newThenStore);
+ storeChanged = true;
+ }
+
+ S newElseStore = (elseStore != null) ?
+ elseStore.leastUpperBound(s) : s;
+ if (!newElseStore.equals(elseStore)) {
+ elseStores.put(b, newElseStore);
+ storeChanged = true;
+ }
+
+ if (storeChanged) {
+ inputs.put(b, new TransferInput<>(node, this, newThenStore, newElseStore));
+ addBlockToWorklist = true;
+ }
+ }
+ }
+
+ if (addBlockToWorklist) {
+ addToWorklist(b);
+ }
+ }
+
+ /**
+ * A worklist is a priority queue of blocks in which the order is given
+ * by depth-first ordering to place non-loop predecessors ahead of successors.
+ */
+ protected static class Worklist {
+
+ /** Map all blocks in the CFG to their depth-first order. */
+ protected IdentityHashMap<Block, Integer> depthFirstOrder;
+
+ /** Comparator to allow priority queue to order blocks by their depth-first
+ order. */
+ public class DFOComparator implements Comparator<Block> {
+ @Override
+ public int compare(Block b1, Block b2) {
+ return depthFirstOrder.get(b1) - depthFirstOrder.get(b2);
+ }
+ }
+
+ /** The backing priority queue. */
+ protected PriorityQueue<Block> queue;
+
+
+ public Worklist(ControlFlowGraph cfg) {
+ depthFirstOrder = new IdentityHashMap<>();
+ int count = 1;
+ for (Block b : cfg.getDepthFirstOrderedBlocks()) {
+ depthFirstOrder.put(b, count++);
+ }
+
+ queue = new PriorityQueue<Block>(11, new DFOComparator());
+ }
+
+ public boolean isEmpty() {
+ return queue.isEmpty();
+ }
+
+ public boolean contains(Block block) {
+ return queue.contains(block);
+ }
+
+ public void add(Block block) {
+ queue.add(block);
+ }
+
+ public Block poll() {
+ return queue.poll();
+ }
+
+ @Override
+ public String toString() {
+ return "Worklist(" + queue + ")";
+ }
+ }
+
+ /**
+ * Read the {@link TransferInput} for a particular basic block (or {@code null} if
+ * none exists yet).
+ */
+ public /*@Nullable*/ TransferInput<A, S> getInput(Block b) {
+ return getInputBefore(b);
+ }
+
+ /**
+ * @return the transfer input corresponding to the location right before the basic
+ * block {@code b}.
+ */
+ protected /*@Nullable*/ TransferInput<A, S> getInputBefore(Block b) {
+ return inputs.get(b);
+ }
+
+ /**
+ * @return the store corresponding to the location right before the basic
+ * block {@code b}.
+ */
+ protected /*@Nullable*/ S getStoreBefore(Block b, Store.Kind kind) {
+ switch (kind) {
+ case THEN:
+ return readFromStore(thenStores, b);
+ case ELSE:
+ return readFromStore(elseStores, b);
+ default:
+ assert false;
+ return null;
+ }
+ }
+
+ /**
+ * Read the {@link Store} for a particular basic block from a map of stores
+ * (or {@code null} if none exists yet).
+ */
+ protected static <S> /*@Nullable*/ S readFromStore(Map<Block, S> stores,
+ Block b) {
+ return stores.get(b);
+ }
+
+ /** Is the analysis currently running? */
+ public boolean isRunning() {
+ return isRunning;
+ }
+
+ /**
+ * @return the abstract value for {@link Node} {@code n}, or {@code null} if
+ * no information is available. Note that if the analysis has not
+ * finished yet, this value might not represent the final value for
+ * this node.
+ */
+ public /*@Nullable*/ A getValue(Node n) {
+ if (isRunning) {
+ // we do not yet have a org.checkerframework.dataflow fact about the current node
+ if (currentNode == n
+ || (currentTree != null && currentTree == n.getTree())) {
+ return null;
+ }
+ // check that 'n' is a subnode of 'node'. Check immediate operands
+ // first for efficiency.
+ assert currentNode != null;
+ assert !n.isLValue() : "Did not expect an lvalue, but got " + n;
+ if (!(currentNode != n && (currentNode.getOperands().contains(n) || currentNode
+ .getTransitiveOperands().contains(n)))) {
+ return null;
+ }
+ return nodeValues.get(n);
+ }
+ return nodeValues.get(n);
+ }
+
+ /**
+ * @return the abstract value for {@link Tree} {@code t}, or {@code null} if
+ * no information is available. Note that if the analysis has not
+ * finished yet, this value might not represent the final value for
+ * this node.
+ */
+ public /*@Nullable*/ A getValue(Tree t) {
+ // we do not yet have a org.checkerframework.dataflow fact about the current node
+ if (t == currentTree) {
+ return null;
+ }
+ Node nodeCorrespondingToTree = getNodeForTree(t);
+ if (nodeCorrespondingToTree == null || nodeCorrespondingToTree.isLValue()) {
+ return null;
+ }
+ return getValue(nodeCorrespondingToTree);
+ }
+
+ /**
+ * Get the {@link Node} for a given {@link Tree}.
+ */
+ public Node getNodeForTree(Tree t) {
+ return cfg.getNodeCorrespondingToTree(t);
+ }
+
+ /**
+ * Get the {@link MethodTree} of the current CFG if the argument {@link Tree} maps
+ * to a {@link Node} in the CFG or null otherwise.
+ */
+ public /*@Nullable*/ MethodTree getContainingMethod(Tree t) {
+ return cfg.getContainingMethod(t);
+ }
+
+ /**
+ * Get the {@link ClassTree} of the current CFG if the argument {@link Tree} maps
+ * to a {@link Node} in the CFG or null otherwise.
+ */
+ public /*@Nullable*/ ClassTree getContainingClass(Tree t) {
+ return cfg.getContainingClass(t);
+ }
+
+ public List<Pair<ReturnNode, TransferResult<A, S>>> getReturnStatementStores() {
+ List<Pair<ReturnNode, TransferResult<A, S>>> result = new ArrayList<>();
+ for (ReturnNode returnNode : cfg.getReturnNodes()) {
+ TransferResult<A, S> store = storesAtReturnStatements
+ .get(returnNode);
+ result.add(Pair.of(returnNode, store));
+ }
+ return result;
+ }
+
+ public AnalysisResult<A, S> getResult() {
+ assert !isRunning;
+ IdentityHashMap<Tree, Node> treeLookup = cfg.getTreeLookup();
+ return new AnalysisResult<>(nodeValues, inputs, treeLookup, finalLocalValues);
+ }
+
+ /**
+ * @return the regular exit store, or {@code null}, if there is no such
+ * store (because the method cannot exit through the regular exit
+ * block).
+ */
+ public /*@Nullable*/ S getRegularExitStore() {
+ SpecialBlock regularExitBlock = cfg.getRegularExitBlock();
+ if (inputs.containsKey(regularExitBlock)) {
+ S regularExitStore = inputs.get(regularExitBlock).getRegularStore();
+ return regularExitStore;
+ } else {
+ return null;
+ }
+ }
+
+ public S getExceptionalExitStore() {
+ S exceptionalExitStore = inputs.get(cfg.getExceptionalExitBlock())
+ .getRegularStore();
+ return exceptionalExitStore;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AnalysisResult.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AnalysisResult.java
new file mode 100644
index 0000000000..f4d6d10451
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/AnalysisResult.java
@@ -0,0 +1,237 @@
+package org.checkerframework.dataflow.analysis;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.cfg.block.Block;
+import org.checkerframework.dataflow.cfg.block.ExceptionBlock;
+import org.checkerframework.dataflow.cfg.block.RegularBlock;
+import org.checkerframework.dataflow.cfg.node.Node;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.lang.model.element.Element;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * An {@link AnalysisResult} represents the result of a org.checkerframework.dataflow analysis by
+ * providing the abstract values given a node or a tree. Note that it does not
+ * keep track of custom results computed by some analysis.
+ *
+ * @author Stefan Heule
+ *
+ * @param <A>
+ * type of the abstract value that is tracked.
+ */
+public class AnalysisResult<A extends AbstractValue<A>, S extends Store<S>> {
+
+ /** Abstract values of nodes. */
+ protected final IdentityHashMap<Node, A> nodeValues;
+
+ /** Map from AST {@link Tree}s to {@link Node}s. */
+ protected final IdentityHashMap<Tree, Node> treeLookup;
+
+ /** Map from (effectively final) local variable elements to their abstract value. */
+ protected final HashMap<Element, A> finalLocalValues;
+
+ /**
+ * The stores before every method call.
+ */
+ protected final IdentityHashMap<Block, TransferInput<A, S>> stores;
+
+ /**
+ * Initialize with a given node-value mapping.
+ */
+ public AnalysisResult(Map<Node, A> nodeValues,
+ IdentityHashMap<Block, TransferInput<A, S>> stores,
+ IdentityHashMap<Tree, Node> treeLookup, HashMap<Element, A> finalLocalValues) {
+ this.nodeValues = new IdentityHashMap<>(nodeValues);
+ this.treeLookup = new IdentityHashMap<>(treeLookup);
+ this.stores = stores;
+ this.finalLocalValues = finalLocalValues;
+ }
+
+ /**
+ * Initialize empty result.
+ */
+ public AnalysisResult() {
+ nodeValues = new IdentityHashMap<>();
+ treeLookup = new IdentityHashMap<>();
+ stores = new IdentityHashMap<>();
+ finalLocalValues = new HashMap<>();
+ }
+
+ /**
+ * Combine with another analysis result.
+ */
+ public void combine(AnalysisResult<A, S> other) {
+ for (Entry<Node, A> e : other.nodeValues.entrySet()) {
+ nodeValues.put(e.getKey(), e.getValue());
+ }
+ for (Entry<Tree, Node> e : other.treeLookup.entrySet()) {
+ treeLookup.put(e.getKey(), e.getValue());
+ }
+ for (Entry<Block, TransferInput<A, S>> e : other.stores.entrySet()) {
+ stores.put(e.getKey(), e.getValue());
+ }
+ for (Entry<Element, A> e : other.finalLocalValues.entrySet()) {
+ finalLocalValues.put(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * @return the value of effectively final local variables
+ */
+ public HashMap<Element, A> getFinalLocalValues() {
+ return finalLocalValues;
+ }
+
+ /**
+ * @return the abstract value for {@link Node} {@code n}, or {@code null} if
+ * no information is available.
+ */
+ public /*@Nullable*/ A getValue(Node n) {
+ return nodeValues.get(n);
+ }
+
+ /**
+ * @return the abstract value for {@link Tree} {@code t}, or {@code null} if
+ * no information is available.
+ */
+ public /*@Nullable*/ A getValue(Tree t) {
+ A val = getValue(treeLookup.get(t));
+ return val;
+ }
+
+ /**
+ * @return the {@link Node} for a given {@link Tree}.
+ */
+ public /*@Nullable*/ Node getNodeForTree(Tree tree) {
+ return treeLookup.get(tree);
+ }
+
+ /**
+ * @return the store immediately before a given {@link Tree}.
+ */
+ public S getStoreBefore(Tree tree) {
+ Node node = getNodeForTree(tree);
+ if (node == null) {
+ return null;
+ }
+ return getStoreBefore(node);
+ }
+
+ /**
+ * @return the store immediately before a given {@link Node}.
+ */
+ public S getStoreBefore(Node node) {
+ return runAnalysisFor(node, true);
+ }
+
+ /**
+ * @return the store immediately after a given {@link Tree}.
+ */
+ public S getStoreAfter(Tree tree) {
+ Node node = getNodeForTree(tree);
+ if (node == null) {
+ return null;
+ }
+ return runAnalysisFor(node, false);
+ }
+
+ /**
+ * Runs the analysis again within the block of {@code node} and returns the
+ * store at the location of {@code node}. If {@code before} is true, then
+ * the store immediately before the {@link Node} {@code node} is returned.
+ * Otherwise, the store after {@code node} is returned.
+ *
+ * <p>
+ * If the given {@link Node} cannot be reached (in the control flow graph),
+ * then {@code null} is returned.
+ */
+ protected S runAnalysisFor(Node node, boolean before) {
+ Block block = node.getBlock();
+ TransferInput<A, S> transferInput = stores.get(block);
+ if (transferInput == null) {
+ return null;
+ }
+ return runAnalysisFor(node, before, transferInput);
+ }
+
+ /**
+ * Runs the analysis again within the block of {@code node} and returns the
+ * store at the location of {@code node}. If {@code before} is true, then
+ * the store immediately before the {@link Node} {@code node} is returned.
+ * Otherwise, the store after {@code node} is returned.
+ */
+ public static <A extends AbstractValue<A>, S extends Store<S>> S runAnalysisFor(
+ Node node, boolean before, TransferInput<A, S> transferInput) {
+ assert node != null;
+ Block block = node.getBlock();
+ assert transferInput != null;
+ Analysis<A, S, ?> analysis = transferInput.analysis;
+ Node oldCurrentNode = analysis.currentNode;
+
+ if (analysis.isRunning) {
+ return analysis.currentInput.getRegularStore();
+ }
+ analysis.isRunning = true;
+ try {
+ switch (block.getType()) {
+ case REGULAR_BLOCK: {
+ RegularBlock rb = (RegularBlock) block;
+
+ // Apply transfer function to contents until we found the node
+ // we
+ // are looking for.
+ TransferInput<A, S> store = transferInput;
+ TransferResult<A, S> transferResult = null;
+ for (Node n : rb.getContents()) {
+ analysis.currentNode = n;
+ if (n == node && before) {
+ return store.getRegularStore();
+ }
+ transferResult = analysis.callTransferFunction(n, store);
+ if (n == node) {
+ return transferResult.getRegularStore();
+ }
+ store = new TransferInput<>(n, analysis, transferResult);
+ }
+ // This point should never be reached. If the block of 'node' is
+ // 'block', then 'node' must be part of the contents of 'block'.
+ assert false;
+ return null;
+ }
+
+ case EXCEPTION_BLOCK: {
+ ExceptionBlock eb = (ExceptionBlock) block;
+
+ // apply transfer function to content
+ assert eb.getNode() == node;
+ if (before) {
+ return transferInput.getRegularStore();
+ }
+ analysis.currentNode = node;
+ TransferResult<A, S> transferResult = analysis
+ .callTransferFunction(node, transferInput);
+ return transferResult.getRegularStore();
+ }
+
+ default:
+ // Only regular blocks and exceptional blocks can hold nodes.
+ assert false;
+ break;
+ }
+
+ return null;
+ } finally {
+ analysis.currentNode = oldCurrentNode;
+ analysis.isRunning = false;
+ }
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/ConditionalTransferResult.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/ConditionalTransferResult.java
new file mode 100644
index 0000000000..c49357a3ff
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/ConditionalTransferResult.java
@@ -0,0 +1,137 @@
+package org.checkerframework.dataflow.analysis;
+
+import java.util.Map;
+
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Implementation of a {@link TransferResult} with two non-exceptional store;
+ * one for the 'then' edge and one for 'else'. The result of
+ * {@code getRegularStore} will be the least upper bound of the two underlying
+ * stores.
+ *
+ * @author Stefan Heule
+ *
+ * @param <S>
+ * The {@link Store} used to keep track of intermediate results.
+ */
+public class ConditionalTransferResult<A extends AbstractValue<A>, S extends Store<S>>
+ extends TransferResult<A, S> {
+
+ private final boolean storeChanged;
+
+ /** The 'then' result store. */
+ protected S thenStore;
+
+ /** The 'else' result store. */
+ protected S elseStore;
+
+ /**
+ * Create a {@code ConditionalTransferResult} with {@code thenStore} as the
+ * resulting store if the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} evaluates to
+ * {@code true} and {@code elseStore} otherwise.
+ *
+ * For the meaning of storeChanged, see
+ * {@link org.checkerframework.dataflow.analysis.TransferResult#storeChanged}.
+ *
+ * <p>
+ *
+ * <em>Exceptions</em>: If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} throws an
+ * exception, then it is assumed that no special handling is necessary and
+ * the store before the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed along any
+ * exceptional edge.
+ *
+ * <p>
+ *
+ * <em>Aliasing</em>: {@code thenStore} and {@code elseStore} are not
+ * allowed to be used anywhere outside of this class (including use through
+ * aliases). Complete control over the objects is transfered to this class.
+ */
+ public ConditionalTransferResult(A value, S thenStore, S elseStore, boolean storeChanged) {
+ super(value);
+ this.thenStore = thenStore;
+ this.elseStore = elseStore;
+ this.storeChanged = storeChanged;
+ }
+
+ public ConditionalTransferResult(A value, S thenStore, S elseStore) {
+ this(value, thenStore, elseStore, false);
+ }
+
+ /**
+ * Create a {@code ConditionalTransferResult} with {@code thenStore} as the
+ * resulting store if the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} evaluates to
+ * {@code true} and {@code elseStore} otherwise.
+ *
+ * <p>
+ *
+ * <em>Exceptions</em>: If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} throws an
+ * exception, then the corresponding store in {@code exceptionalStores} is
+ * used. If no exception is found in {@code exceptionalStores}, then it is
+ * assumed that no special handling is necessary and the store before the
+ * corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed along any exceptional edge.
+ *
+ * <p>
+ *
+ * <em>Aliasing</em>: {@code thenStore}, {@code elseStore}, and any store in
+ * {@code exceptionalStores} are not allowed to be used anywhere outside of
+ * this class (including use through aliases). Complete control over the
+ * objects is transfered to this class.
+ */
+ public ConditionalTransferResult(A value, S thenStore, S elseStore,
+ Map<TypeMirror, S> exceptionalStores, boolean storeChanged) {
+ super(value);
+ this.exceptionalStores = exceptionalStores;
+ this.thenStore = thenStore;
+ this.elseStore = elseStore;
+ this.storeChanged = storeChanged;
+ }
+
+ public ConditionalTransferResult(A value, S thenStore, S elseStore,
+ Map<TypeMirror, S> exceptionalStores) {
+ this(value, thenStore, elseStore, exceptionalStores, false);
+ }
+
+ @Override
+ public S getRegularStore() {
+ return thenStore.leastUpperBound(elseStore);
+ }
+
+ @Override
+ public S getThenStore() {
+ return thenStore;
+ }
+
+ @Override
+ public S getElseStore() {
+ return elseStore;
+ }
+
+ @Override
+ public boolean containsTwoStores() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append("RegularTransferResult(");
+ result.append(System.getProperty("line.separator"));
+ result.append("resultValue = " + resultValue);
+ result.append(System.getProperty("line.separator"));
+ result.append("thenStore = " + thenStore);
+ result.append("elseStore = " + elseStore);
+ result.append(System.getProperty("line.separator"));
+ result.append(")");
+ return result.toString();
+ }
+
+ /**
+ * @see org.checkerframework.dataflow.analysis.TransferResult#storeChanged()
+ */
+ @Override
+ public boolean storeChanged() {
+ return storeChanged;
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java
new file mode 100644
index 0000000000..fcf6a61beb
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/FlowExpressions.java
@@ -0,0 +1,940 @@
+package org.checkerframework.dataflow.analysis;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.cfg.node.ArrayAccessNode;
+import org.checkerframework.dataflow.cfg.node.ArrayCreationNode;
+import org.checkerframework.dataflow.cfg.node.ClassNameNode;
+import org.checkerframework.dataflow.cfg.node.ExplicitThisLiteralNode;
+import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
+import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
+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.StringConversionNode;
+import org.checkerframework.dataflow.cfg.node.SuperNode;
+import org.checkerframework.dataflow.cfg.node.ThisLiteralNode;
+import org.checkerframework.dataflow.cfg.node.ValueLiteralNode;
+import org.checkerframework.dataflow.cfg.node.WideningConversionNode;
+import org.checkerframework.dataflow.util.HashCodeUtils;
+import org.checkerframework.dataflow.util.PurityUtils;
+import org.checkerframework.javacutil.AnnotationProvider;
+import org.checkerframework.javacutil.ElementUtils;
+import org.checkerframework.javacutil.TreeUtils;
+import org.checkerframework.javacutil.TypesUtils;
+
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+
+/**
+ * Collection of classes and helper functions to represent Java expressions
+ * about which the org.checkerframework.dataflow analysis can possibly infer facts. Expressions
+ * include:
+ * <ul>
+ * <li>Field accesses (e.g., <em>o.f</em>)</li>
+ * <li>Local variables (e.g., <em>l</em>)</li>
+ * <li>This reference (e.g., <em>this</em>)</li>
+ * <li>Pure method calls (e.g., <em>o.m()</em>)</li>
+ * <li>Unknown other expressions to mark that something else was present.</li>
+ * </ul>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class FlowExpressions {
+
+ /**
+ * @return the internal representation (as {@link FieldAccess}) of a
+ * {@link FieldAccessNode}. Can contain {@link Unknown} as receiver.
+ */
+ public static FieldAccess internalReprOfFieldAccess(
+ AnnotationProvider provider, FieldAccessNode node) {
+ Receiver receiver;
+ Node receiverNode = node.getReceiver();
+ if (node.isStatic()) {
+ receiver = new ClassName(receiverNode.getType());
+ } else {
+ receiver = internalReprOf(provider, receiverNode);
+ }
+ return new FieldAccess(receiver, node);
+ }
+
+ /**
+ * @return the internal representation (as {@link FieldAccess}) of a
+ * {@link FieldAccessNode}. Can contain {@link Unknown} as receiver.
+ */
+ public static ArrayAccess internalReprOfArrayAccess(
+ AnnotationProvider provider, ArrayAccessNode node) {
+ Receiver receiver = internalReprOf(provider, node.getArray());
+ Receiver index = internalReprOf(provider, node.getIndex());
+ return new ArrayAccess(node.getType(), receiver, index);
+ }
+
+ /**
+ * We ignore operations such as widening and
+ * narrowing when computing the internal representation.
+ *
+ * @return the internal representation (as {@link Receiver}) of any
+ * {@link Node}. Might contain {@link Unknown}.
+ */
+ public static Receiver internalReprOf(AnnotationProvider provider,
+ Node receiverNode) {
+ return internalReprOf(provider, receiverNode, false);
+ }
+
+ /**
+ * We ignore operations such as widening and
+ * narrowing when computing the internal representation.
+ *
+ * @return the internal representation (as {@link Receiver}) of any
+ * {@link Node}. Might contain {@link Unknown}.
+ */
+ public static Receiver internalReprOf(AnnotationProvider provider,
+ Node receiverNode, boolean allowNonDeterministic) {
+ Receiver receiver = null;
+ if (receiverNode instanceof FieldAccessNode) {
+ FieldAccessNode fan = (FieldAccessNode) receiverNode;
+
+ if (fan.getFieldName().equals("this")) {
+ // For some reason, "className.this" is considered a field access.
+ // We right this wrong here.
+ receiver = new ThisReference(fan.getReceiver().getType());
+ } else if (fan.getFieldName().equals("class")) {
+ // "className.class" is considered a field access. This makes sense,
+ // since .class is similar to a field access which is the equivalent
+ // of a call to getClass(). However for the purposes of dataflow
+ // analysis, and value stores, this is the equivalent of a ClassNameNode.
+ receiver = new ClassName(fan.getReceiver().getType());
+ } else {
+ receiver = internalReprOfFieldAccess(provider, fan);
+ }
+ } else if (receiverNode instanceof ExplicitThisLiteralNode) {
+ receiver = new ThisReference(receiverNode.getType());
+ } else if (receiverNode instanceof ThisLiteralNode) {
+ receiver = new ThisReference(receiverNode.getType());
+ } else if (receiverNode instanceof SuperNode) {
+ receiver = new ThisReference(receiverNode.getType());
+ } else if (receiverNode instanceof LocalVariableNode) {
+ LocalVariableNode lv = (LocalVariableNode) receiverNode;
+ receiver = new LocalVariable(lv);
+ } else if (receiverNode instanceof ArrayAccessNode) {
+ ArrayAccessNode a = (ArrayAccessNode) receiverNode;
+ receiver = internalReprOfArrayAccess(provider, a);
+ } else if (receiverNode instanceof StringConversionNode) {
+ // ignore string conversion
+ return internalReprOf(provider,
+ ((StringConversionNode) receiverNode).getOperand());
+ } else if (receiverNode instanceof WideningConversionNode) {
+ // ignore widening
+ return internalReprOf(provider,
+ ((WideningConversionNode) receiverNode).getOperand());
+ } else if (receiverNode instanceof NarrowingConversionNode) {
+ // ignore narrowing
+ return internalReprOf(provider,
+ ((NarrowingConversionNode) receiverNode).getOperand());
+ } else if (receiverNode instanceof ClassNameNode) {
+ ClassNameNode cn = (ClassNameNode) receiverNode;
+ receiver = new ClassName(cn.getType());
+ } else if (receiverNode instanceof ValueLiteralNode) {
+ ValueLiteralNode vn = (ValueLiteralNode) receiverNode;
+ receiver = new ValueLiteral(vn.getType(), vn);
+ } else if (receiverNode instanceof ArrayCreationNode) {
+ ArrayCreationNode an = (ArrayCreationNode)receiverNode;
+ receiver = new ArrayCreation(an.getType(), an.getDimensions(), an.getInitializers());
+ } else if (receiverNode instanceof MethodInvocationNode) {
+ MethodInvocationNode mn = (MethodInvocationNode) receiverNode;
+ ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn
+ .getTree());
+
+ // check if this represents a boxing operation of a constant, in which
+ // case we treat the method call as deterministic, because there is no way
+ // to behave differently in two executions where two constants are being used.
+ boolean considerDeterministic = false;
+ if (invokedMethod.toString().equals("valueOf(long)")
+ && mn.getTarget().getReceiver().toString().equals("Long")) {
+ Node arg = mn.getArgument(0);
+ if (arg instanceof ValueLiteralNode) {
+ considerDeterministic = true;
+ }
+ }
+
+ if (PurityUtils.isDeterministic(provider, invokedMethod) || allowNonDeterministic || considerDeterministic) {
+ List<Receiver> parameters = new ArrayList<>();
+ for (Node p : mn.getArguments()) {
+ parameters.add(internalReprOf(provider, p));
+ }
+ Receiver methodReceiver;
+ if (ElementUtils.isStatic(invokedMethod)) {
+ methodReceiver = new ClassName(mn.getTarget().getReceiver()
+ .getType());
+ } else {
+ methodReceiver = internalReprOf(provider, mn.getTarget()
+ .getReceiver());
+ }
+ receiver = new MethodCall(mn.getType(), invokedMethod,
+ methodReceiver, parameters);
+ }
+ }
+
+ if (receiver == null) {
+ receiver = new Unknown(receiverNode.getType());
+ }
+ return receiver;
+ }
+
+ public static abstract class Receiver {
+ protected final TypeMirror type;
+
+ public Receiver(TypeMirror type) {
+ assert type != null;
+ this.type = type;
+ }
+
+ public TypeMirror getType() {
+ return type;
+ }
+
+ public abstract boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz);
+
+ public boolean containsUnknown() {
+ return containsOfClass(Unknown.class);
+ }
+
+ /**
+ * Returns true if and only if the value this expression stands for
+ * cannot be changed by a method call. This is the case for local
+ * variables, the self reference as well as final field accesses for
+ * whose receiver {@link #isUnmodifiableByOtherCode} is true.
+ */
+ public abstract boolean isUnmodifiableByOtherCode();
+
+ /**
+ * @return true if and only if the two receiver are syntactically
+ * identical
+ */
+ public boolean syntacticEquals(Receiver other) {
+ return other == this;
+ }
+
+ /**
+ * @return true if and only if this receiver contains a receiver that is
+ * syntactically equal to {@code other}.
+ */
+ public boolean containsSyntacticEqualReceiver(Receiver other) {
+ return syntacticEquals(other);
+ }
+
+ /**
+ * Returns true if and only if {@code other} appears anywhere in this
+ * receiver or an expression appears in this receiver such that
+ * {@code other} might alias this expression, and that expression is
+ * modifiable.
+ *
+ * <p>
+ * This is always true, except for cases where the Java type information
+ * prevents aliasing and none of the subexpressions can alias 'other'.
+ */
+ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
+ return this.equals(other) || store.canAlias(this, other);
+ }
+ }
+
+ public static class FieldAccess extends Receiver {
+ protected Receiver receiver;
+ protected VariableElement field;
+
+ public Receiver getReceiver() {
+ return receiver;
+ }
+
+ public VariableElement getField() {
+ return field;
+ }
+
+ public FieldAccess(Receiver receiver, FieldAccessNode node) {
+ super(node.getType());
+ this.receiver = receiver;
+ this.field = node.getElement();
+ }
+
+ public FieldAccess(Receiver receiver, TypeMirror type,
+ VariableElement fieldElement) {
+ super(type);
+ this.receiver = receiver;
+ this.field = fieldElement;
+ }
+
+ public boolean isFinal() {
+ return ElementUtils.isFinal(field);
+ }
+
+ public boolean isStatic() {
+ return ElementUtils.isStatic(field);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof FieldAccess)) {
+ return false;
+ }
+ FieldAccess fa = (FieldAccess) obj;
+ return fa.getField().equals(getField())
+ && fa.getReceiver().equals(getReceiver());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getField(), getReceiver());
+ }
+
+ @Override
+ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
+ return super.containsModifiableAliasOf(store, other)
+ || receiver.containsModifiableAliasOf(store, other);
+ }
+
+ @Override
+ public boolean containsSyntacticEqualReceiver(Receiver other) {
+ return syntacticEquals(other)
+ || receiver.containsSyntacticEqualReceiver(other);
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ if (!(other instanceof FieldAccess)) {
+ return false;
+ }
+ FieldAccess fa = (FieldAccess) other;
+ return super.syntacticEquals(other)
+ || fa.getField().equals(getField())
+ && fa.getReceiver().syntacticEquals(getReceiver());
+ }
+
+ @Override
+ public String toString() {
+ return receiver + "." + field;
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz) {
+ return getClass().equals(clazz) || receiver.containsOfClass(clazz);
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return isFinal() && getReceiver().isUnmodifiableByOtherCode();
+ }
+ }
+
+ public static class ThisReference extends Receiver {
+ public ThisReference(TypeMirror type) {
+ super(type);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null && obj instanceof ThisReference;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(0);
+ }
+
+ @Override
+ public String toString() {
+ return "this";
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz) {
+ return getClass().equals(clazz);
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ return other instanceof ThisReference;
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return true;
+ }
+
+ @Override
+ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
+ return false; // 'this' is not modifiable
+ }
+ }
+
+ /**
+ * A ClassName represents the occurrence of a class as part of a static
+ * field access or method invocation.
+ */
+ public static class ClassName extends Receiver {
+ public ClassName(TypeMirror type) {
+ super(type);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ClassName)) {
+ return false;
+ }
+ ClassName other = (ClassName) obj;
+ return getType().toString().equals(other.getType().toString());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getType().toString());
+ }
+
+ @Override
+ public String toString() {
+ return getType().toString();
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz) {
+ return getClass().equals(clazz);
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ return this.equals(other);
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return true;
+ }
+
+ @Override
+ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
+ return false; // not modifiable
+ }
+ }
+
+ public static class Unknown extends Receiver {
+ public Unknown(TypeMirror type) {
+ super(type);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj == this;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "?";
+ }
+
+ @Override
+ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
+ return true;
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz) {
+ return getClass().equals(clazz);
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return false;
+ }
+
+ }
+
+ public static class LocalVariable extends Receiver {
+ protected Element element;
+
+ public LocalVariable(LocalVariableNode localVar) {
+ super(localVar.getType());
+ this.element = localVar.getElement();
+ }
+
+ public LocalVariable(Element elem) {
+ super(ElementUtils.getType(elem));
+ this.element = elem;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof LocalVariable)) {
+ return false;
+ }
+ LocalVariable other = (LocalVariable) obj;
+ VarSymbol vs = (VarSymbol) element;
+ VarSymbol vsother = (VarSymbol) other.element;
+ // Use type.unannotatedType().toString().equals(...) instead of Types.isSameType(...)
+ // because Types requires a processing environment, and FlowExpressions is
+ // designed to be independent of processing environment. See also
+ // calls to getType().toString() in FlowExpressions.
+ return vsother.name.contentEquals(vs.name) &&
+ vsother.type.unannotatedType().toString().equals(vs.type.unannotatedType().toString()) &&
+ vsother.owner.toString().equals(vs.owner.toString());
+ }
+
+ public Element getElement() {
+ return element;
+ }
+
+ @Override
+ public int hashCode() {
+ VarSymbol vs = (VarSymbol) element;
+ return HashCodeUtils.hash(vs.name.toString(),
+ vs.type.unannotatedType().toString(),
+ vs.owner.toString());
+ }
+
+ @Override
+ public String toString() {
+ return element.toString();
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz) {
+ return getClass().equals(clazz);
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ if (!(other instanceof LocalVariable)) {
+ return false;
+ }
+ LocalVariable l = (LocalVariable) other;
+ return l.equals(this);
+ }
+
+ @Override
+ public boolean containsSyntacticEqualReceiver(Receiver other) {
+ return syntacticEquals(other);
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return true;
+ }
+ }
+
+ public static class ValueLiteral extends Receiver {
+
+ protected final Object value;
+
+ public ValueLiteral(TypeMirror type, ValueLiteralNode node) {
+ super(type);
+ value = node.getValue();
+ }
+
+ public ValueLiteral(TypeMirror type, Object value) {
+ super(type);
+ this.value = value;
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz) {
+ return getClass().equals(clazz);
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ValueLiteral)) {
+ return false;
+ }
+ ValueLiteral other = (ValueLiteral) obj;
+ if (value == null) {
+ return type.toString().equals(other.type.toString())
+ && other.value == null;
+ }
+ return type.toString().equals(other.type.toString())
+ && value.equals(other.value);
+ }
+
+ @Override
+ public String toString() {
+ if (TypesUtils.isString(type)) {
+ return "\"" + value + "\"";
+ } else if (type.getKind() == TypeKind.LONG) {
+ return value.toString() + "L";
+ }
+ return value == null ? "null" : value.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(value, type.toString());
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ return this.equals(other);
+ }
+
+ @Override
+ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
+ return false; // not modifiable
+ }
+ }
+
+ /**
+ * A method call.
+ */
+ public static class MethodCall extends Receiver {
+
+ protected final Receiver receiver;
+ protected final List<Receiver> parameters;
+ protected final ExecutableElement method;
+
+ public MethodCall(TypeMirror type, ExecutableElement method,
+ Receiver receiver, List<Receiver> parameters) {
+ super(type);
+ this.receiver = receiver;
+ this.parameters = parameters;
+ this.method = method;
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz) {
+ if (getClass().equals(clazz)) {
+ return true;
+ }
+ if (receiver.containsOfClass(clazz)) {
+ return true;
+ }
+ for (Receiver p : parameters) {
+ if (p.containsOfClass(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return the method call receiver (for inspection only - do not modify)
+ */
+ public Receiver getReceiver() {
+ return receiver;
+ }
+
+ /**
+ * @return the method call parameters (for inspection only - do not modify any of the parameters)
+ */
+ public List<Receiver> getParameters() {
+ return Collections.unmodifiableList(parameters);
+ }
+
+ /**
+ * @return the ExecutableElement for the method call
+ */
+ public ExecutableElement getElement() {
+ return method;
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return false;
+ }
+
+ @Override
+ public boolean containsSyntacticEqualReceiver(Receiver other) {
+ return syntacticEquals(other) || receiver.syntacticEquals(other);
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ if (!(other instanceof MethodCall)) {
+ return false;
+ }
+ MethodCall otherMethod = (MethodCall) other;
+ if (!receiver.syntacticEquals(otherMethod.receiver)) {
+ return false;
+ }
+ if (parameters.size() != otherMethod.parameters.size()) {
+ return false;
+ }
+ int i = 0;
+ for (Receiver p : parameters) {
+ if (!p.syntacticEquals(otherMethod.parameters.get(i))) {
+ return false;
+ }
+ i++;
+ }
+ return method.equals(otherMethod.method);
+ }
+
+ public boolean containsSyntacticEqualParameter(LocalVariable var) {
+ for (Receiver p : parameters) {
+ if (p.containsSyntacticEqualReceiver(var)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
+ if (receiver.containsModifiableAliasOf(store, other)) {
+ return true;
+ }
+ for (Receiver p : parameters) {
+ if (p.containsModifiableAliasOf(store, other)) {
+ return true;
+ }
+ }
+ return false; // the method call itself is not modifiable
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof MethodCall)) {
+ return false;
+ }
+ MethodCall other = (MethodCall) obj;
+ int i = 0;
+ for (Receiver p : parameters) {
+ if (!p.equals(other.parameters.get(i))) {
+ return false;
+ }
+ i++;
+ }
+ return receiver.equals(other.receiver)
+ && method.equals(other.method);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = HashCodeUtils.hash(method, receiver);
+ for (Receiver p : parameters) {
+ hash = HashCodeUtils.hash(hash, p);
+ }
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append(receiver.toString());
+ result.append(".");
+ String methodName = method.getSimpleName().toString();
+ result.append(methodName);
+ result.append("(");
+ boolean first = true;
+ for (Receiver p : parameters) {
+ if (!first) {
+ result.append(", ");
+ }
+ result.append(p.toString());
+ first = false;
+ }
+ result.append(")");
+ return result.toString();
+ }
+ }
+
+ /**
+ * A deterministic method call.
+ */
+ public static class ArrayAccess extends Receiver {
+
+ protected final Receiver receiver;
+ protected final Receiver index;
+
+ public ArrayAccess(TypeMirror type, Receiver receiver, Receiver index) {
+ super(type);
+ this.receiver = receiver;
+ this.index = index;
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends FlowExpressions.Receiver> clazz) {
+ if (getClass().equals(clazz)) {
+ return true;
+ }
+ if (receiver.containsOfClass(clazz)) {
+ return true;
+ }
+ return index.containsOfClass(clazz);
+ }
+
+ public Receiver getReceiver() {
+ return receiver;
+ }
+
+ public Receiver getIndex() {
+ return index;
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return false;
+ }
+
+ @Override
+ public boolean containsSyntacticEqualReceiver(Receiver other) {
+ return syntacticEquals(other) || receiver.syntacticEquals(other)
+ || index.syntacticEquals(other);
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ if (!(other instanceof ArrayAccess)) {
+ return false;
+ }
+ ArrayAccess otherArrayAccess = (ArrayAccess) other;
+ if (!receiver.syntacticEquals(otherArrayAccess.receiver)) {
+ return false;
+ }
+ return index.syntacticEquals(otherArrayAccess.index);
+ }
+
+ @Override
+ public boolean containsModifiableAliasOf(Store<?> store, Receiver other) {
+ if (receiver.containsModifiableAliasOf(store, other)) {
+ return true;
+ }
+ return index.containsModifiableAliasOf(store, other);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ArrayAccess)) {
+ return false;
+ }
+ ArrayAccess other = (ArrayAccess) obj;
+ return receiver.equals(other.receiver) && index.equals(other.index);
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(receiver, index);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append(receiver.toString());
+ result.append("[");
+ result.append(index.toString());
+ result.append("]");
+ return result.toString();
+ }
+ }
+
+ public static class ArrayCreation extends Receiver {
+
+ protected List<Node> dimensions;
+ protected List<Node> initializers;
+
+ public ArrayCreation(TypeMirror type, List<Node> dimensions, List<Node> initializers) {
+ super(type);
+ this.dimensions = dimensions;
+ this.initializers = initializers;
+ }
+
+ public List<Node> getDimensions() {
+ return dimensions;
+ }
+
+ public List<Node> getInitializers() {
+ return initializers;
+ }
+
+ @Override
+ public boolean containsOfClass(Class<? extends Receiver> clazz) {
+ for (Node n : dimensions) {
+ if (n.getClass().equals(clazz)) return true;
+ }
+ for (Node n : initializers) {
+ if (n.getClass().equals(clazz)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isUnmodifiableByOtherCode() {
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((dimensions == null) ? 0 : dimensions.hashCode());
+ result = prime * result + ((initializers == null) ? 0 : initializers.hashCode());
+ result = prime * result + HashCodeUtils.hash(getType().toString());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ArrayCreation)) {
+ return false;
+ }
+ ArrayCreation other = (ArrayCreation) obj;
+ return this.dimensions.equals(other.getDimensions())
+ && this.initializers.equals(other.getInitializers())
+ && getType().toString().equals(other.getType().toString());
+ }
+
+ @Override
+ public boolean syntacticEquals(Receiver other) {
+ return this.equals(other);
+ }
+
+ @Override
+ public boolean containsSyntacticEqualReceiver(Receiver other) {
+ return syntacticEquals(other);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("new " + type);
+ if (!dimensions.isEmpty()) {
+ boolean needComma = false;
+ sb.append(" (");
+ for (Node dim : dimensions) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(dim);
+ needComma = true;
+ }
+ sb.append(")");
+ }
+ if (!initializers.isEmpty()) {
+ boolean needComma = false;
+ sb.append(" = {");
+ for (Node init : initializers) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(init);
+ needComma = true;
+ }
+ sb.append("}");
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/RegularTransferResult.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/RegularTransferResult.java
new file mode 100644
index 0000000000..8872e7f808
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/RegularTransferResult.java
@@ -0,0 +1,130 @@
+package org.checkerframework.dataflow.analysis;
+
+import java.util.Map;
+
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Implementation of a {@link TransferResult} with just one non-exceptional
+ * store. The result of {@code getThenStore} and {@code getElseStore} is equal
+ * to the only underlying store.
+ *
+ * @author Stefan Heule
+ *
+ * @param <S>
+ * The {@link Store} used to keep track of intermediate results.
+ */
+public class RegularTransferResult<A extends AbstractValue<A>, S extends Store<S>>
+ extends TransferResult<A, S> {
+
+ /** The regular result store. */
+ protected S store;
+ final private boolean storeChanged;
+
+ /**
+ * Create a {@code TransferResult} with {@code resultStore} as the resulting
+ * store. If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} is a boolean node, then
+ * {@code resultStore} is used for both the 'then' and 'else' edge.
+ *
+ * <p>
+ *
+ * <em>Exceptions</em>: If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} throws an
+ * exception, then it is assumed that no special handling is necessary and
+ * the store before the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed along any
+ * exceptional edge.
+ *
+ * <p>
+ *
+ * <em>Aliasing</em>: {@code resultStore} is not allowed to be used anywhere
+ * outside of this class (including use through aliases). Complete control
+ * over the object is transfered to this class.
+ */
+ public RegularTransferResult(A value, S resultStore, boolean storeChanged) {
+ super(value);
+ this.store = resultStore;
+ this.storeChanged = storeChanged;
+ }
+
+ public RegularTransferResult(A value, S resultStore) {
+ this(value, resultStore, false);
+ }
+
+ /**
+ * Create a {@code TransferResult} with {@code resultStore} as the resulting
+ * store. If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} is a boolean node, then
+ * {@code resultStore} is used for both the 'then' and 'else' edge.
+ *
+ * For the meaning of storeChanged, see
+ * {@link org.checkerframework.dataflow.analysis.TransferResult#storeChanged}.
+ *
+ * <p>
+ *
+ * <em>Exceptions</em>: If the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} throws an
+ * exception, then the corresponding store in {@code exceptionalStores} is
+ * used. If no exception is found in {@code exceptionalStores}, then it is
+ * assumed that no special handling is necessary and the store before the
+ * corresponding {@link org.checkerframework.dataflow.cfg.node.Node} will be passed along any exceptional edge.
+ *
+ * <p>
+ *
+ * <em>Aliasing</em>: {@code resultStore} and any store in
+ * {@code exceptionalStores} are not allowed to be used anywhere outside of
+ * this class (including use through aliases). Complete control over the
+ * objects is transfered to this class.
+ */
+ public RegularTransferResult(A value, S resultStore,
+ Map<TypeMirror, S> exceptionalStores, boolean storeChanged) {
+ super(value);
+ this.store = resultStore;
+ this.storeChanged = storeChanged;
+ this.exceptionalStores = exceptionalStores;
+ }
+
+ public RegularTransferResult(A value, S resultStore,
+ Map<TypeMirror, S> exceptionalStores) {
+ this(value, resultStore, exceptionalStores, false);
+ }
+
+ @Override
+ public S getRegularStore() {
+ return store;
+ }
+
+ @Override
+ public S getThenStore() {
+ return store;
+ }
+
+ @Override
+ public S getElseStore() {
+ // copy the store such that it is the same as the result of getThenStore
+ // (that is, identical according to equals), but two different objects.
+ return store.copy();
+ }
+
+ @Override
+ public boolean containsTwoStores() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append("RegularTransferResult(");
+ result.append(System.getProperty("line.separator"));
+ result.append("resultValue = " + resultValue);
+ result.append(System.getProperty("line.separator"));
+ result.append("store = " + store);
+ result.append(System.getProperty("line.separator"));
+ result.append(")");
+ return result.toString();
+ }
+
+ /**
+ * @see org.checkerframework.dataflow.analysis.TransferResult#storeChanged()
+ */
+ @Override
+ public boolean storeChanged() {
+ return storeChanged;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Store.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Store.java
new file mode 100644
index 0000000000..7d1b8f9259
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/Store.java
@@ -0,0 +1,73 @@
+package org.checkerframework.dataflow.analysis;
+
+import org.checkerframework.dataflow.cfg.CFGVisualizer;
+
+/**
+ * A store is used to keep track of the information that the org.checkerframework.dataflow analysis
+ * has accumulated at any given point in time.
+ *
+ * @author Stefan Heule
+ *
+ * @param <S>
+ * The type of the store returned by {@code copy} and that is used in
+ * {@code leastUpperBound}. Usually it is the implementing class
+ * itself, e.g. in {@code T extends Store<T>}.
+ */
+public interface Store<S extends Store<S>> {
+
+ // We maintain a then store and an else store before each basic block.
+ // When they are identical (by reference equality), they can be treated
+ // as a regular unconditional store.
+ // Once we have some information for both the then and else store, we
+ // create a TransferInput for the block and allow it to be analyzed.
+ public static enum Kind {
+ THEN,
+ ELSE,
+ BOTH
+ }
+
+ /** A flow rule describes how stores flow along one edge between basic blocks. */
+ public static enum FlowRule {
+ EACH_TO_EACH, // The normal case, then store flows to the then store
+ // and else store flows to the else store.
+ THEN_TO_BOTH, // Then store flows to both then and else of successor.
+ ELSE_TO_BOTH, // Else store flows to both then and else of successor.
+ THEN_TO_THEN, // Then store flows to the then of successor. Else store is ignored.
+ ELSE_TO_ELSE, // Else store flows to the else of successor. Then store is ignored.
+ }
+
+ /** @return an exact copy of this store. */
+ S copy();
+
+ /**
+ * Compute the least upper bound of two stores.
+ *
+ * <p>
+ *
+ * <em>Important</em>: This method must fulfill the following contract:
+ * <ul>
+ * <li>Does not change {@code this}.</li>
+ * <li>Does not change {@code other}.</li>
+ * <li>Returns a fresh object which is not aliased yet.</li>
+ * <li>Returns an object of the same (dynamic) type as {@code this}, even if
+ * the signature is more permissive.</li>
+ * <li>Is commutative.</li>
+ * </ul>
+ */
+ S leastUpperBound(S other);
+
+ /**
+ * Can the objects {@code a} and {@code b} be aliases? Returns a
+ * conservative answer (i.e., returns {@code true} if not enough information
+ * is available to determine aliasing).
+ */
+ boolean canAlias(FlowExpressions.Receiver a,
+ FlowExpressions.Receiver b);
+
+ /**
+ * Delegate visualization responsibility to a visualizer.
+ *
+ * @param viz the visualizer to visualize this store
+ */
+ void visualize(CFGVisualizer<?, S, ?> viz);
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferFunction.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferFunction.java
new file mode 100644
index 0000000000..f4a539398a
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferFunction.java
@@ -0,0 +1,49 @@
+package org.checkerframework.dataflow.analysis;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.cfg.UnderlyingAST;
+import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
+import org.checkerframework.dataflow.cfg.node.Node;
+import org.checkerframework.dataflow.cfg.node.NodeVisitor;
+
+import java.util.List;
+
+/**
+ * Interface of a transfer function for the abstract interpretation used for the
+ * flow analysis.
+ *
+ * <p>
+ *
+ * A transfer function consists of the following components:
+ * <ul>
+ * <li>A method {@code initialStore} that determines which initial store should
+ * be used in the org.checkerframework.dataflow analysis.</li>
+ * <li>A function for every {@link Node} type that determines the behavior of
+ * the org.checkerframework.dataflow analysis in that case. This method takes a {@link Node} and an
+ * incoming store, and produces a {@link RegularTransferResult}.</li>
+ * </ul>
+ *
+ * <p>
+ *
+ * <em>Important</em>: The individual transfer functions ( {@code visit*}) are
+ * allowed to use (and modify) the stores contained in the argument passed; the
+ * ownership is transfered from the caller to that function.
+ *
+ * @author Stefan Heule
+ *
+ * @param <S>
+ * The {@link Store} used to keep track of intermediate results.
+ */
+public interface TransferFunction<A extends AbstractValue<A>, S extends Store<S>>
+ extends NodeVisitor<TransferResult<A, S>, TransferInput<A, S>> {
+
+ /**
+ * @return the initial store to be used by the org.checkerframework.dataflow analysis.
+ * {@code parameters} is only set if the underlying AST is a method.
+ */
+ S initialStore(UnderlyingAST underlyingAST,
+ /*@Nullable*/ List<LocalVariableNode> parameters);
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferInput.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferInput.java
new file mode 100644
index 0000000000..fd707ae59d
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferInput.java
@@ -0,0 +1,283 @@
+package org.checkerframework.dataflow.analysis;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.cfg.node.Node;
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+/**
+ * {@code TransferInput} is used as the input type of the individual transfer
+ * functions of a {@link TransferFunction}. It also contains a reference to the
+ * node for which the transfer function will be applied.
+ *
+ * <p>
+ *
+ * A {@code TransferInput} contains one or two stores. If two stores are
+ * present, one belongs to 'then', and the other to 'else'.
+ *
+ * @author Stefan Heule
+ *
+ * @param <S>
+ * The {@link Store} used to keep track of intermediate results.
+ */
+public class TransferInput<A extends AbstractValue<A>, S extends Store<S>> {
+
+ /**
+ * The corresponding node.
+ */
+ protected Node node;
+
+ /**
+ * The regular result store (or {@code null} if none is present). The
+ * following invariant is maintained:
+ *
+ * <pre>{@code
+ * store == null &hArr; thenStore != null &amp;&amp; elseStore != null
+ * }</pre>
+ */
+ protected final /*@Nullable*/ S store;
+
+ /**
+ * The 'then' result store (or {@code null} if none is present). The
+ * following invariant is maintained:
+ *
+ * <pre>{@code
+ * store == null &hArr; thenStore != null &amp;&amp; elseStore != null
+ * }</pre>
+ */
+ protected final /*@Nullable*/ S thenStore;
+
+ /**
+ * The 'else' result store (or {@code null} if none is present). The
+ * following invariant is maintained:
+ *
+ * <pre>{@code
+ * store == null &hArr; thenStore != null &amp;&amp; elseStore != null
+ * }</pre>
+ */
+ protected final /*@Nullable*/ S elseStore;
+
+ /**
+ * The corresponding analysis class to get intermediate flow results.
+ */
+ protected final Analysis<A, S, ?> analysis;
+
+ /**
+ * Create a {@link TransferInput}, given a {@link TransferResult} and a
+ * node-value mapping.
+ *
+ * <p>
+ *
+ * <em>Aliasing</em>: The stores returned by any methods of {@code to} will
+ * be stored internally and are not allowed to be used elsewhere. Full
+ * control of them is transfered to this object.
+ *
+ * <p>
+ *
+ * The node-value mapping {@code nodeValues} is provided by the analysis and
+ * is only read from within this {@link TransferInput}.
+ */
+ public TransferInput(Node n, Analysis<A, S, ?> analysis,
+ TransferResult<A, S> to) {
+ node = n;
+ this.analysis = analysis;
+ if (to.containsTwoStores()) {
+ thenStore = to.getThenStore();
+ elseStore = to.getElseStore();
+ store = null;
+ } else {
+ store = to.getRegularStore();
+ thenStore = elseStore = null;
+ }
+ }
+
+ /**
+ * Create a {@link TransferInput}, given a store and a node-value mapping.
+ *
+ * <p>
+ *
+ * <em>Aliasing</em>: The store {@code s} will be stored internally and is
+ * not allowed to be used elsewhere. Full control over {@code s} is
+ * transfered to this object.
+ *
+ * <p>
+ *
+ * The node-value mapping {@code nodeValues} is provided by the analysis and
+ * is only read from within this {@link TransferInput}.
+ */
+ public TransferInput(Node n, Analysis<A, S, ?> analysis, S s) {
+ node = n;
+ this.analysis = analysis;
+ store = s;
+ thenStore = elseStore = null;
+ }
+
+ /**
+ * Create a {@link TransferInput}, given two stores and a node-value
+ * mapping.
+ *
+ * <p>
+ *
+ * <em>Aliasing</em>: The two stores {@code s1} and {@code s2} will be
+ * stored internally and are not allowed to be used elsewhere. Full control
+ * of them is transfered to this object.
+ */
+ public TransferInput(Node n, Analysis<A, S, ?> analysis, S s1, S s2) {
+ node = n;
+ this.analysis = analysis;
+ thenStore = s1;
+ elseStore = s2;
+ store = null;
+ }
+
+ /**
+ * Copy constructor.
+ */
+ protected TransferInput(TransferInput<A, S> from) {
+ this.node = from.node;
+ this.analysis = from.analysis;
+ if (from.store == null) {
+ thenStore = from.thenStore.copy();
+ elseStore = from.elseStore.copy();
+ store = null;
+ } else {
+ store = from.store.copy();
+ thenStore = elseStore = null;
+ }
+ }
+
+ /**
+ * @return the {@link Node} for this {@link TransferInput}.
+ */
+ public Node getNode() {
+ return node;
+ }
+
+ /**
+ * @return the abstract value of {@link Node} {@code n}, which is required
+ * to be a 'sub-node' (that is, a direct or indirect child) of the
+ * node this transfer input is associated with. Furthermore,
+ * {@code n} cannot be a l-value node. Returns {@code null} if no
+ * value if available.
+ */
+ public /*@Nullable*/ A getValueOfSubNode(Node n) {
+ return analysis.getValue(n);
+ }
+
+ /**
+ * @return the regular result store produced if no exception is thrown by
+ * the {@link Node} corresponding to this transfer function result.
+ */
+ public S getRegularStore() {
+ if (store == null) {
+ return thenStore.leastUpperBound(elseStore);
+ } else {
+ return store;
+ }
+ }
+
+ /**
+ * @return the result store produced if the {@link Node} this result belongs
+ * to evaluates to {@code true}.
+ */
+ public S getThenStore() {
+ if (store == null) {
+ return thenStore;
+ }
+ return store;
+ }
+
+ /**
+ * @return the result store produced if the {@link Node} this result belongs
+ * to evaluates to {@code false}.
+ */
+ public S getElseStore() {
+ if (store == null) {
+ return elseStore;
+ }
+ // copy the store such that it is the same as the result of getThenStore
+ // (that is, identical according to equals), but two different objects.
+ return store.copy();
+ }
+
+ /**
+ * @return {@code true} if and only if this transfer input contains two
+ * stores that are potentially not equal. Note that the result
+ * {@code true} does not imply that {@code getRegularStore} cannot
+ * be called (or vice versa for {@code false}). Rather, it indicates
+ * that {@code getThenStore} or {@code getElseStore} can be used to
+ * give more precise results. Otherwise, if the result is
+ * {@code false}, then all three methods {@code getRegularStore},
+ * {@code getThenStore}, and {@code getElseStore} return equivalent
+ * stores.
+ */
+ public boolean containsTwoStores() {
+ return (thenStore != null && elseStore != null);
+ }
+
+ /** @return an exact copy of this store. */
+ public TransferInput<A, S> copy() {
+ return new TransferInput<>(this);
+ }
+
+ /**
+ * Compute the least upper bound of two stores.
+ *
+ * <p>
+ *
+ * <em>Important</em>: This method must fulfill the same contract as
+ * {@code leastUpperBound} of {@link Store}.
+ */
+ public TransferInput<A, S> leastUpperBound(TransferInput<A, S> other) {
+ if (store == null) {
+ S newThenStore = thenStore.leastUpperBound(other.getThenStore());
+ S newElseStore = elseStore.leastUpperBound(other.getElseStore());
+ return new TransferInput<>(node, analysis, newThenStore,
+ newElseStore);
+ } else {
+ if (other.store == null) {
+ // make sure we do not lose precision and keep two stores if at
+ // least one of the two TransferInput's has two stores.
+ return other.leastUpperBound(this);
+ }
+ return new TransferInput<>(node, analysis,
+ store.leastUpperBound(other.getRegularStore()));
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o instanceof TransferInput) {
+ @SuppressWarnings("unchecked")
+ TransferInput<A, S> other = (TransferInput<A, S>) o;
+ if (containsTwoStores()) {
+ if (other.containsTwoStores()) {
+ return getThenStore().equals(other.getThenStore()) &&
+ getElseStore().equals(other.getElseStore());
+ }
+ } else {
+ if (!other.containsTwoStores()) {
+ return getRegularStore().equals(other.getRegularStore());
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(this.analysis, this.node, this.store, this.thenStore, this.elseStore);
+ }
+
+ @Override
+ public String toString() {
+ if (store == null) {
+ return "[then=" + thenStore + ", else=" + elseStore + "]";
+ } else {
+ return "[" + store + "]";
+ }
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferResult.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferResult.java
new file mode 100644
index 0000000000..fabba9c511
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/analysis/TransferResult.java
@@ -0,0 +1,116 @@
+package org.checkerframework.dataflow.analysis;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import java.util.Map;
+
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * {@code TransferResult} is used as the result type of the individual transfer
+ * functions of a {@link TransferFunction}. It always belongs to the result of
+ * the individual transfer function for a particular {@link org.checkerframework.dataflow.cfg.node.Node}, even though
+ * that {@code org.checkerframework.dataflow.cfg.node.Node} is not explicitly store in {@code TransferResult}.
+ *
+ * <p>
+ *
+ * A {@code TransferResult} contains one or two stores (for 'then' and 'else'),
+ * and zero or more stores with a cause ({@link TypeMirror}).
+ *
+ * @author Stefan Heule
+ *
+ * @param <S>
+ * The {@link Store} used to keep track of intermediate results.
+ */
+abstract public class TransferResult<A extends AbstractValue<A>, S extends Store<S>> {
+
+ /**
+ * The stores in case the basic block throws an exception (or {@code null}
+ * if the corresponding {@link org.checkerframework.dataflow.cfg.node.Node} does not throw any exceptions). Does
+ * not necessarily contain a store for every exception, in which case the
+ * in-store will be used.
+ */
+ protected /*@Nullable*/ Map<TypeMirror, S> exceptionalStores;
+
+ /**
+ * The abstract value of the {@link org.checkerframework.dataflow.cfg.node.Node} associated with this
+ * {@link TransferResult}, or {@code null} if no value has been produced.
+ */
+ protected /*@Nullable*/ A resultValue;
+
+ public TransferResult(/*@Nullable*/ A resultValue) {
+ this.resultValue = resultValue;
+ }
+
+ /**
+ * @return the abstract value produced by the transfer function
+ */
+ public A getResultValue() {
+ return resultValue;
+ }
+
+ public void setResultValue(A resultValue) {
+ this.resultValue = resultValue;
+ }
+
+ /**
+ * @return the regular result store produced if no exception is thrown by
+ * the {@link org.checkerframework.dataflow.cfg.node.Node} corresponding to this transfer function result.
+ */
+ abstract public S getRegularStore();
+
+ /**
+ * @return the result store produced if the {@link org.checkerframework.dataflow.cfg.node.Node} this result belongs
+ * to evaluates to {@code true}.
+ */
+ abstract public S getThenStore();
+
+ /**
+ * @return the result store produced if the {@link org.checkerframework.dataflow.cfg.node.Node} this result belongs
+ * to evaluates to {@code false}.
+ */
+ abstract public S getElseStore();
+
+ /**
+ * @return the store that flows along the outgoing exceptional edge labeled
+ * with {@code exception} (or {@code null} if no special handling is
+ * required for exceptional edges).
+ */
+ public /*@Nullable*/ S getExceptionalStore(
+ TypeMirror exception) {
+ if (exceptionalStores == null) {
+ return null;
+ }
+ return exceptionalStores.get(exception);
+ }
+
+ /**
+ * @return a Map of {@link TypeMirror} to {@link Store}.
+ *
+ * @see TransferResult#getExceptionalStore(TypeMirror)
+ */
+ public Map<TypeMirror, S> getExceptionalStores() {
+ return exceptionalStores;
+ }
+
+ /**
+ * @return {@code true} if and only if this transfer result contains two
+ * stores that are potentially not equal. Note that the result
+ * {@code true} does not imply that {@code getRegularStore} cannot
+ * be called (or vice versa for {@code false}). Rather, it indicates
+ * that {@code getThenStore} or {@code getElseStore} can be used to
+ * give more precise results. Otherwise, if the result is
+ * {@code false}, then all three methods {@code getRegularStore},
+ * {@code getThenStore}, and {@code getElseStore} return equivalent
+ * stores.
+ */
+ abstract public boolean containsTwoStores();
+
+ /**
+ * @return {@code true} if and only if the transfer function returning this
+ * transfer result changed the regularStore, elseStore, or thenStore.
+ */
+ abstract public boolean storeChanged();
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java
new file mode 100644
index 0000000000..2cff0f2802
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGBuilder.java
@@ -0,0 +1,4448 @@
+package org.checkerframework.dataflow.cfg;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.analysis.Store;
+import org.checkerframework.dataflow.cfg.CFGBuilder.ExtendedNode.ExtendedNodeType;
+import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGMethod;
+import org.checkerframework.dataflow.cfg.block.Block;
+import org.checkerframework.dataflow.cfg.block.Block.BlockType;
+import org.checkerframework.dataflow.cfg.block.BlockImpl;
+import org.checkerframework.dataflow.cfg.block.ConditionalBlockImpl;
+import org.checkerframework.dataflow.cfg.block.ExceptionBlockImpl;
+import org.checkerframework.dataflow.cfg.block.RegularBlockImpl;
+import org.checkerframework.dataflow.cfg.block.SingleSuccessorBlockImpl;
+import org.checkerframework.dataflow.cfg.block.SpecialBlock.SpecialBlockType;
+import org.checkerframework.dataflow.cfg.block.SpecialBlockImpl;
+import org.checkerframework.dataflow.cfg.node.*;
+import org.checkerframework.dataflow.qual.TerminatesExecution;
+import org.checkerframework.dataflow.util.MostlySingleton;
+import org.checkerframework.javacutil.AnnotationProvider;
+import org.checkerframework.javacutil.BasicAnnotationProvider;
+import org.checkerframework.javacutil.ElementUtils;
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.Pair;
+import org.checkerframework.javacutil.TreeUtils;
+import org.checkerframework.javacutil.TypesUtils;
+import org.checkerframework.javacutil.trees.TreeBuilder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.ReferenceType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.UnionType;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import com.sun.source.tree.*;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Context;
+
+/**
+ * Builds the control flow graph of some Java code (either a method, or an
+ * arbitrary statement).
+ *
+ * <p>
+ *
+ * The translation of the AST to the CFG is split into three phases:
+ * <ol>
+ * <li><em>Phase one.</em> In the first phase, the AST is translated into a
+ * sequence of {@link org.checkerframework.dataflow.cfg.CFGBuilder.ExtendedNode}s. An extended node can either be a
+ * {@link Node}, or one of several meta elements such as a conditional or
+ * unconditional jump or a node with additional information about exceptions.
+ * Some of the extended nodes contain labels (e.g., for the jump target), and
+ * phase one additionally creates a mapping from labels to extended nodes.
+ * Finally, the list of leaders is computed: A leader is an extended node which
+ * will give rise to a basic block in phase two.</li>
+ * <li><em>Phase two.</em> In this phase, the sequence of extended nodes is
+ * translated to a graph of control flow blocks that contain nodes. The meta
+ * elements from phase one are translated into the correct edges.</li>
+ * <li><em>Phase three.</em> The control flow graph generated in phase two can
+ * contain degenerate basic blocks such as empty regular basic blocks or
+ * conditional basic blocks that have the same block as both 'then' and 'else'
+ * successor. This phase removes these cases while preserving the control flow
+ * structure.</li>
+ * </ol>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class CFGBuilder {
+
+ /** Can assertions be assumed to be disabled? */
+ protected final boolean assumeAssertionsDisabled;
+
+ /** Can assertions be assumed to be enabled? */
+ protected final boolean assumeAssertionsEnabled;
+
+ public CFGBuilder(boolean assumeAssertionsEnabled, boolean assumeAssertionsDisabled) {
+ assert !(assumeAssertionsDisabled && assumeAssertionsEnabled);
+ this.assumeAssertionsEnabled = assumeAssertionsEnabled;
+ this.assumeAssertionsDisabled = assumeAssertionsDisabled;
+ }
+
+ /**
+ * Class declarations that have been encountered when building the
+ * control-flow graph for a method.
+ */
+ protected final List<ClassTree> declaredClasses = new LinkedList<>();
+
+ public List<ClassTree> getDeclaredClasses() {
+ return declaredClasses;
+ }
+
+ /**
+ * Lambdas encountered when building the control-flow graph for
+ * a method, variable initializer, or initializer.
+ */
+ protected final List<LambdaExpressionTree> declaredLambdas = new LinkedList<>();
+
+ public List<LambdaExpressionTree> getDeclaredLambdas() {
+ return declaredLambdas;
+ }
+
+ /**
+ * Build the control flow graph of some code.
+ */
+ public static ControlFlowGraph build(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST, boolean assumeAssertionsEnabled, boolean assumeAssertionsDisabled) {
+ return new CFGBuilder(assumeAssertionsEnabled, assumeAssertionsDisabled).run(root, env, underlyingAST);
+ }
+
+ /**
+ * Build the control flow graph of some code (method, initializer block, ...).
+ * bodyPath is the TreePath to the body of that code.
+ */
+ public static ControlFlowGraph build(
+ TreePath bodyPath, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST, boolean assumeAssertionsEnabled, boolean assumeAssertionsDisabled) {
+ return new CFGBuilder(assumeAssertionsEnabled, assumeAssertionsDisabled).run(bodyPath, env, underlyingAST);
+ }
+
+ /**
+ * Build the control flow graph of a method.
+ */
+ public static ControlFlowGraph build(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ MethodTree tree, ClassTree classTree, boolean assumeAssertionsEnabled, boolean assumeAssertionsDisabled) {
+ return new CFGBuilder(assumeAssertionsEnabled, assumeAssertionsDisabled).run(root, env, tree, classTree);
+ }
+
+ /**
+ * Build the control flow graph of some code.
+ */
+ public static ControlFlowGraph build(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST) {
+ return new CFGBuilder(false, false).run(root, env, underlyingAST);
+ }
+
+ /**
+ * Build the control flow graph of a method.
+ */
+ public static ControlFlowGraph build(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ MethodTree tree, ClassTree classTree) {
+ return new CFGBuilder(false, false).run(root, env, tree, classTree);
+ }
+
+ /**
+ * Build the control flow graph of some code.
+ */
+ public ControlFlowGraph run(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST) {
+ declaredClasses.clear();
+ declaredLambdas.clear();
+
+ TreeBuilder builder = new TreeBuilder(env);
+ AnnotationProvider annotationProvider = new BasicAnnotationProvider();
+ PhaseOneResult phase1result = new CFGTranslationPhaseOne().process(
+ root, env, underlyingAST, exceptionalExitLabel, builder, annotationProvider);
+ ControlFlowGraph phase2result = new CFGTranslationPhaseTwo()
+ .process(phase1result);
+ ControlFlowGraph phase3result = CFGTranslationPhaseThree
+ .process(phase2result);
+ return phase3result;
+ }
+
+ /**
+ * Build the control flow graph of some code (method, initializer block, ...).
+ * bodyPath is the TreePath to the body of that code.
+ */
+ public ControlFlowGraph run(
+ TreePath bodyPath, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST) {
+ declaredClasses.clear();
+ TreeBuilder builder = new TreeBuilder(env);
+ AnnotationProvider annotationProvider = new BasicAnnotationProvider();
+ PhaseOneResult phase1result = new CFGTranslationPhaseOne().process(
+ bodyPath, env, underlyingAST, exceptionalExitLabel, builder, annotationProvider);
+ ControlFlowGraph phase2result = new CFGTranslationPhaseTwo()
+ .process(phase1result);
+ ControlFlowGraph phase3result = CFGTranslationPhaseThree
+ .process(phase2result);
+ return phase3result;
+ }
+
+ /**
+ * Build the control flow graph of a method.
+ */
+ public ControlFlowGraph run(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ MethodTree tree, ClassTree classTree) {
+ UnderlyingAST underlyingAST = new CFGMethod(tree, classTree);
+ return run(root, env, underlyingAST);
+ }
+
+ /* --------------------------------------------------------- */
+ /* Extended Node Types and Labels */
+ /* --------------------------------------------------------- */
+
+ /** Special label to identify the exceptional exit. */
+ protected final Label exceptionalExitLabel = new Label();
+
+ /** Special label to identify the regular exit. */
+ protected final Label regularExitLabel = new Label();
+
+ /**
+ * An extended node can be one of several things (depending on its
+ * {@code type}):
+ * <ul>
+ * <li><em>NODE</em>. An extended node of this type is just a wrapper for a
+ * {@link Node} (that cannot throw exceptions).</li>
+ * <li><em>EXCEPTION_NODE</em>. A wrapper for a {@link Node} which can throw
+ * exceptions. It contains a label for every possible exception type the
+ * node might throw.</li>
+ * <li><em>UNCONDITIONAL_JUMP</em>. An unconditional jump to a label.</li>
+ * <li><em>TWO_TARGET_CONDITIONAL_JUMP</em>. A conditional jump with two
+ * targets for both the 'then' and 'else' branch.</li>
+ * </ul>
+ */
+ protected static abstract class ExtendedNode {
+
+ /**
+ * The basic block this extended node belongs to (as determined in phase
+ * two).
+ */
+ protected BlockImpl block;
+
+ /** Type of this node. */
+ protected ExtendedNodeType type;
+
+ /** Does this node terminate the execution? (e.g., "System.exit()") */
+ protected boolean terminatesExecution = false;
+
+ public ExtendedNode(ExtendedNodeType type) {
+ this.type = type;
+ }
+
+ /** Extended node types (description see above). */
+ public enum ExtendedNodeType {
+ NODE, EXCEPTION_NODE, UNCONDITIONAL_JUMP, CONDITIONAL_JUMP
+ }
+
+ public ExtendedNodeType getType() {
+ return type;
+ }
+
+ public boolean getTerminatesExecution() {
+ return terminatesExecution;
+ }
+
+ public void setTerminatesExecution(boolean terminatesExecution) {
+ this.terminatesExecution = terminatesExecution;
+ }
+
+ /**
+ * @return the node contained in this extended node (only applicable if
+ * the type is {@code NODE} or {@code EXCEPTION_NODE}).
+ */
+ public Node getNode() {
+ assert false;
+ return null;
+ }
+
+ /**
+ * @return the label associated with this extended node (only applicable
+ * if type is {@link ExtendedNodeType#CONDITIONAL_JUMP} or
+ * {@link ExtendedNodeType#UNCONDITIONAL_JUMP}).
+ */
+ public Label getLabel() {
+ assert false;
+ return null;
+ }
+
+ public BlockImpl getBlock() {
+ return block;
+ }
+
+ public void setBlock(BlockImpl b) {
+ this.block = b;
+ }
+
+ @Override
+ public String toString() {
+ return "ExtendedNode(" + type + ")";
+ }
+ }
+
+ /**
+ * An extended node of type {@code NODE}.
+ */
+ protected static class NodeHolder extends ExtendedNode {
+
+ protected Node node;
+
+ public NodeHolder(Node node) {
+ super(ExtendedNodeType.NODE);
+ this.node = node;
+ }
+
+ @Override
+ public Node getNode() {
+ return node;
+ }
+
+ @Override
+ public String toString() {
+ return "NodeHolder(" + node + ")";
+ }
+
+ }
+
+ /**
+ * An extended node of type {@code EXCEPTION_NODE}.
+ */
+ protected static class NodeWithExceptionsHolder extends ExtendedNode {
+
+ protected Node node;
+ /**
+ * Map from exception type to labels of successors that may
+ * be reached as a result of that exception.
+ */
+ protected Map<TypeMirror, Set<Label>> exceptions;
+
+ public NodeWithExceptionsHolder(Node node,
+ Map<TypeMirror, Set<Label>> exceptions) {
+ super(ExtendedNodeType.EXCEPTION_NODE);
+ this.node = node;
+ this.exceptions = exceptions;
+ }
+
+ public Map<TypeMirror, Set<Label>> getExceptions() {
+ return exceptions;
+ }
+
+ @Override
+ public Node getNode() {
+ return node;
+ }
+
+ @Override
+ public String toString() {
+ return "NodeWithExceptionsHolder(" + node + ")";
+ }
+
+ }
+
+ /**
+ * An extended node of type {@link ExtendedNodeType#CONDITIONAL_JUMP}.
+ *
+ * <p>
+ *
+ * <em>Important:</em> In the list of extended nodes, there should not be
+ * any labels that point to a conditional jump. Furthermore, the node
+ * directly ahead of any conditional jump has to be a
+ * {@link NodeWithExceptionsHolder} or {@link NodeHolder}, and the node held
+ * by that extended node is required to be of boolean type.
+ */
+ protected static class ConditionalJump extends ExtendedNode {
+
+ protected Label trueSucc;
+ protected Label falseSucc;
+
+ protected Store.FlowRule trueFlowRule;
+ protected Store.FlowRule falseFlowRule;
+
+ public ConditionalJump(Label trueSucc, Label falseSucc) {
+ super(ExtendedNodeType.CONDITIONAL_JUMP);
+ this.trueSucc = trueSucc;
+ this.falseSucc = falseSucc;
+ }
+
+ public Label getThenLabel() {
+ return trueSucc;
+ }
+
+ public Label getElseLabel() {
+ return falseSucc;
+ }
+
+ public Store.FlowRule getTrueFlowRule() {
+ return trueFlowRule;
+ }
+
+ public Store.FlowRule getFalseFlowRule() {
+ return falseFlowRule;
+ }
+
+ public void setTrueFlowRule(Store.FlowRule rule) {
+ trueFlowRule = rule;
+ }
+
+ public void setFalseFlowRule(Store.FlowRule rule) {
+ falseFlowRule = rule;
+ }
+
+ @Override
+ public String toString() {
+ return "TwoTargetConditionalJump(" + getThenLabel() + ","
+ + getElseLabel() + ")";
+ }
+ }
+
+ /**
+ * An extended node of type {@link ExtendedNodeType#UNCONDITIONAL_JUMP}.
+ */
+ protected static class UnconditionalJump extends ExtendedNode {
+
+ protected Label jumpTarget;
+
+ public UnconditionalJump(Label jumpTarget) {
+ super(ExtendedNodeType.UNCONDITIONAL_JUMP);
+ this.jumpTarget = jumpTarget;
+ }
+
+ @Override
+ public Label getLabel() {
+ return jumpTarget;
+ }
+
+ @Override
+ public String toString() {
+ return "JumpMarker(" + getLabel() + ")";
+ }
+ }
+
+ /**
+ * A label is used to refer to other extended nodes using a mapping from
+ * labels to extended nodes. Labels get their names either from labeled
+ * statements in the source code or from internally generated unique names.
+ */
+ protected static class Label {
+ private static int uid = 0;
+
+ protected String name;
+
+ public Label(String name) {
+ this.name = name;
+ }
+
+ public Label() {
+ this.name = uniqueName();
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * Return a new unique label name that cannot be confused with a Java
+ * source code label.
+ *
+ * @return a new unique label name
+ */
+ private static String uniqueName() {
+ return "%L" + uid++;
+ }
+ }
+
+ /**
+ * A TryFrame takes a thrown exception type and maps it to a set
+ * of possible control-flow successors.
+ */
+ protected static interface TryFrame {
+ /**
+ * Given a type of thrown exception, add the set of possible control
+ * flow successor {@link Label}s to the argument set. Return true
+ * if the exception is known to be caught by one of those labels and
+ * false if it may propagate still further.
+ */
+ public boolean possibleLabels(TypeMirror thrown, Set<Label> labels);
+ }
+
+ /**
+ * A TryCatchFrame contains an ordered list of catch labels that apply
+ * to exceptions with specific types.
+ */
+ protected static class TryCatchFrame implements TryFrame {
+ protected Types types;
+
+ /** An ordered list of pairs because catch blocks are ordered. */
+ protected List<Pair<TypeMirror, Label>> catchLabels;
+
+ public TryCatchFrame(Types types, List<Pair<TypeMirror, Label>> catchLabels) {
+ this.types = types;
+ this.catchLabels = catchLabels;
+ }
+
+ /**
+ * Given a type of thrown exception, add the set of possible control
+ * flow successor {@link Label}s to the argument set. Return true
+ * if the exception is known to be caught by one of those labels and
+ * false if it may propagate still further.
+ */
+ @Override
+ public boolean possibleLabels(TypeMirror thrown, Set<Label> labels) {
+ // A conservative approach would be to say that every catch block
+ // might execute for any thrown exception, but we try to do better.
+ //
+ // We rely on several assumptions that seem to hold as of Java 7.
+ // 1) An exception parameter in a catch block must be either
+ // a declared type or a union composed of declared types,
+ // all of which are subtypes of Throwable.
+ // 2) A thrown type must either be a declared type or a variable
+ // that extends a declared type, which is a subtype of Throwable.
+ //
+ // Under those assumptions, if the thrown type (or its bound) is
+ // a subtype of the caught type (or one of its alternatives), then
+ // the catch block must apply and none of the later ones can apply.
+ // Otherwise, if the thrown type (or its bound) is a supertype
+ // of the caught type (or one of its alternatives), then the catch
+ // block may apply, but so may later ones.
+ // Otherwise, the thrown type and the caught type are unrelated
+ // declared types, so they do not overlap on any non-null value.
+
+ while (!(thrown instanceof DeclaredType)) {
+ assert thrown instanceof TypeVariable :
+ "thrown type must be a variable or a declared type";
+ thrown = ((TypeVariable)thrown).getUpperBound();
+ }
+ DeclaredType declaredThrown = (DeclaredType)thrown;
+ assert thrown != null : "thrown type must be bounded by a declared type";
+
+ for (Pair<TypeMirror, Label> pair : catchLabels) {
+ TypeMirror caught = pair.first;
+ boolean canApply = false;
+
+ if (caught instanceof DeclaredType) {
+ DeclaredType declaredCaught = (DeclaredType)caught;
+ if (types.isSubtype(declaredThrown, declaredCaught)) {
+ // No later catch blocks can apply.
+ labels.add(pair.second);
+ return true;
+ } else if (types.isSubtype(declaredCaught, declaredThrown)) {
+ canApply = true;
+ }
+ } else {
+ assert caught instanceof UnionType :
+ "caught type must be a union or a declared type";
+ UnionType caughtUnion = (UnionType)caught;
+ for (TypeMirror alternative : caughtUnion.getAlternatives()) {
+ assert alternative instanceof DeclaredType :
+ "alternatives of an caught union type must be declared types";
+ DeclaredType declaredAlt = (DeclaredType)alternative;
+ if (types.isSubtype(declaredThrown, declaredAlt)) {
+ // No later catch blocks can apply.
+ labels.add(pair.second);
+ return true;
+ } else if (types.isSubtype(declaredAlt, declaredThrown)) {
+ canApply = true;
+ }
+ }
+ }
+
+ if (canApply) {
+ labels.add(pair.second);
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * A TryFinallyFrame applies to exceptions of any type
+ */
+ protected class TryFinallyFrame implements TryFrame {
+ protected Label finallyLabel;
+
+ public TryFinallyFrame(Label finallyLabel) {
+ this.finallyLabel = finallyLabel;
+ }
+
+ @Override
+ public boolean possibleLabels(TypeMirror thrown, Set<Label> labels) {
+ labels.add(finallyLabel);
+ return true;
+ }
+ }
+
+ /**
+ * An exception stack represents the set of all try-catch blocks
+ * in effect at a given point in a program. It maps an exception
+ * type to a set of Labels and it maps a block exit (via return or
+ * fall-through) to a single Label.
+ */
+ protected static class TryStack {
+ protected Label exitLabel;
+ protected LinkedList<TryFrame> frames;
+
+ public TryStack(Label exitLabel) {
+ this.exitLabel = exitLabel;
+ this.frames = new LinkedList<>();
+ }
+
+ public void pushFrame(TryFrame frame) {
+ frames.addFirst(frame);
+ }
+
+ public void popFrame() {
+ frames.removeFirst();
+ }
+
+ /**
+ * Returns the set of possible {@link Label}s where control may
+ * transfer when an exception of the given type is thrown.
+ */
+ public Set<Label> possibleLabels(TypeMirror thrown) {
+ // Work up from the innermost frame until the exception is known to
+ // be caught.
+ Set<Label> labels = new MostlySingleton<>();
+ for (TryFrame frame : frames) {
+ if (frame.possibleLabels(thrown, labels)) {
+ return labels;
+ }
+ }
+ labels.add(exitLabel);
+ return labels;
+ }
+ }
+
+ /* --------------------------------------------------------- */
+ /* Phase Three */
+ /* --------------------------------------------------------- */
+
+ /**
+ * Class that performs phase three of the translation process. In
+ * particular, the following degenerate cases of basic blocks are removed:
+ *
+ * <ol>
+ * <li>Empty regular basic blocks: These blocks will be removed and their
+ * predecessors linked directly to the successor.</li>
+ * <li>Conditional basic blocks that have the same basic block as the 'then'
+ * and 'else' successor: The conditional basic block will be removed in this
+ * case.</li>
+ * <li>Two consecutive, non-empty, regular basic blocks where the second
+ * block has exactly one predecessor (namely the other of the two blocks):
+ * In this case, the two blocks are merged.</li>
+ * <li>Some basic blocks might not be reachable from the entryBlock. These
+ * basic blocks are removed, and the list of predecessors (in the
+ * doubly-linked structure of basic blocks) are adapted correctly.</li>
+ * </ol>
+ *
+ * Eliminating the second type of degenerate cases might introduce cases of
+ * the third problem. These are also removed.
+ */
+ public static class CFGTranslationPhaseThree {
+
+ /**
+ * A simple wrapper object that holds a basic block and allows to set
+ * one of its successors.
+ */
+ protected interface PredecessorHolder {
+ void setSuccessor(BlockImpl b);
+
+ BlockImpl getBlock();
+ }
+
+ /**
+ * Perform phase three on the control flow graph {@code cfg}.
+ *
+ * @param cfg
+ * The control flow graph. Ownership is transfered to this
+ * method and the caller is not allowed to read or modify
+ * {@code cfg} after the call to {@code process} any more.
+ * @return the resulting control flow graph
+ */
+ public static ControlFlowGraph process(ControlFlowGraph cfg) {
+ Set<Block> worklist = cfg.getAllBlocks();
+ Set<Block> dontVisit = new HashSet<>();
+
+ // note: this method has to be careful when relinking basic blocks
+ // to not forget to adjust the predecessors, too
+
+ // fix predecessor lists by removing any unreachable predecessors
+ for (Block c : worklist) {
+ BlockImpl cur = (BlockImpl) c;
+ for (BlockImpl pred : new HashSet<>(cur.getPredecessors())) {
+ if (!worklist.contains(pred)) {
+ cur.removePredecessor(pred);
+ }
+ }
+ }
+
+ // remove empty blocks
+ for (Block cur : worklist) {
+ if (dontVisit.contains(cur)) {
+ continue;
+ }
+
+ if (cur.getType() == BlockType.REGULAR_BLOCK) {
+ RegularBlockImpl b = (RegularBlockImpl) cur;
+ if (b.isEmpty()) {
+ Set<RegularBlockImpl> empty = new HashSet<>();
+ Set<PredecessorHolder> predecessors = new HashSet<>();
+ BlockImpl succ = computeNeighborhoodOfEmptyBlock(b,
+ empty, predecessors);
+ for (RegularBlockImpl e : empty) {
+ succ.removePredecessor(e);
+ dontVisit.add(e);
+ }
+ for (PredecessorHolder p : predecessors) {
+ BlockImpl block = p.getBlock();
+ dontVisit.add(block);
+ succ.removePredecessor(block);
+ p.setSuccessor(succ);
+ }
+ }
+ }
+ }
+
+ // remove useless conditional blocks
+ worklist = cfg.getAllBlocks();
+ for (Block c : worklist) {
+ BlockImpl cur = (BlockImpl) c;
+
+ if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
+ ConditionalBlockImpl cb = (ConditionalBlockImpl) cur;
+ assert cb.getPredecessors().size() == 1;
+ if (cb.getThenSuccessor() == cb.getElseSuccessor()) {
+ BlockImpl pred = cb.getPredecessors().iterator().next();
+ PredecessorHolder predecessorHolder = getPredecessorHolder(
+ pred, cb);
+ BlockImpl succ = (BlockImpl) cb.getThenSuccessor();
+ succ.removePredecessor(cb);
+ predecessorHolder.setSuccessor(succ);
+ }
+ }
+ }
+
+ // merge consecutive basic blocks if possible
+ worklist = cfg.getAllBlocks();
+ for (Block cur : worklist) {
+ if (cur.getType() == BlockType.REGULAR_BLOCK) {
+ RegularBlockImpl b = (RegularBlockImpl) cur;
+ Block succ = b.getRegularSuccessor();
+ if (succ.getType() == BlockType.REGULAR_BLOCK) {
+ RegularBlockImpl rs = (RegularBlockImpl) succ;
+ if (rs.getPredecessors().size() == 1) {
+ b.setSuccessor(rs.getRegularSuccessor());
+ b.addNodes(rs.getContents());
+ rs.getRegularSuccessor().removePredecessor(rs);
+ }
+ }
+ }
+ }
+
+ return cfg;
+ }
+
+ /**
+ * Compute the set of empty regular basic blocks {@code empty}, starting
+ * at {@code start} and going both forward and backwards. Furthermore,
+ * compute the predecessors of these empty blocks ({@code predecessors}
+ * ), and their single successor (return value).
+ *
+ * @param start
+ * The starting point of the search (an empty, regular basic
+ * block).
+ * @param empty
+ * An empty set to be filled by this method with all empty
+ * basic blocks found (including {@code start}).
+ * @param predecessors
+ * An empty set to be filled by this method with all
+ * predecessors.
+ * @return the single successor of the set of the empty basic blocks
+ */
+ protected static BlockImpl computeNeighborhoodOfEmptyBlock(
+ RegularBlockImpl start, Set<RegularBlockImpl> empty,
+ Set<PredecessorHolder> predecessors) {
+
+ // get empty neighborhood that come before 'start'
+ computeNeighborhoodOfEmptyBlockBackwards(start, empty, predecessors);
+
+ // go forward
+ BlockImpl succ = (BlockImpl) start.getSuccessor();
+ while (succ.getType() == BlockType.REGULAR_BLOCK) {
+ RegularBlockImpl cur = (RegularBlockImpl) succ;
+ if (cur.isEmpty()) {
+ computeNeighborhoodOfEmptyBlockBackwards(cur, empty,
+ predecessors);
+ assert empty.contains(cur) : "cur ought to be in empty";
+ succ = (BlockImpl) cur.getSuccessor();
+ if (succ == cur) {
+ // An infinite loop, making exit block unreachable
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return succ;
+ }
+
+ /**
+ * Compute the set of empty regular basic blocks {@code empty}, starting
+ * at {@code start} and looking only backwards in the control flow
+ * graph. Furthermore, compute the predecessors of these empty blocks (
+ * {@code predecessors}).
+ *
+ * @param start
+ * The starting point of the search (an empty, regular basic
+ * block).
+ * @param empty
+ * A set to be filled by this method with all empty basic
+ * blocks found (including {@code start}).
+ * @param predecessors
+ * A set to be filled by this method with all predecessors.
+ */
+ protected static void computeNeighborhoodOfEmptyBlockBackwards(
+ RegularBlockImpl start, Set<RegularBlockImpl> empty,
+ Set<PredecessorHolder> predecessors) {
+
+ RegularBlockImpl cur = start;
+ empty.add(cur);
+ for (final BlockImpl pred : cur.getPredecessors()) {
+ switch (pred.getType()) {
+ case SPECIAL_BLOCK:
+ // add pred correctly to predecessor list
+ predecessors.add(getPredecessorHolder(pred, cur));
+ break;
+ case CONDITIONAL_BLOCK:
+ // add pred correctly to predecessor list
+ predecessors.add(getPredecessorHolder(pred, cur));
+ break;
+ case EXCEPTION_BLOCK:
+ // add pred correctly to predecessor list
+ predecessors.add(getPredecessorHolder(pred, cur));
+ break;
+ case REGULAR_BLOCK:
+ RegularBlockImpl r = (RegularBlockImpl) pred;
+ if (r.isEmpty()) {
+ // recursively look backwards
+ if (!empty.contains(r)) {
+ computeNeighborhoodOfEmptyBlockBackwards(r, empty,
+ predecessors);
+ }
+ } else {
+ // add pred correctly to predecessor list
+ predecessors.add(getPredecessorHolder(pred, cur));
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Return a predecessor holder that can be used to set the successor of
+ * {@code pred} in the place where previously the edge pointed to
+ * {@code cur}. Additionally, the predecessor holder also takes care of
+ * unlinking (i.e., removing the {@code pred} from {@code cur's}
+ * predecessors).
+ */
+ protected static PredecessorHolder getPredecessorHolder(
+ final BlockImpl pred, final BlockImpl cur) {
+ switch (pred.getType()) {
+ case SPECIAL_BLOCK:
+ SingleSuccessorBlockImpl s = (SingleSuccessorBlockImpl) pred;
+ return singleSuccessorHolder(s, cur);
+ case CONDITIONAL_BLOCK:
+ // add pred correctly to predecessor list
+ final ConditionalBlockImpl c = (ConditionalBlockImpl) pred;
+ if (c.getThenSuccessor() == cur) {
+ return new PredecessorHolder() {
+ @Override
+ public void setSuccessor(BlockImpl b) {
+ c.setThenSuccessor(b);
+ cur.removePredecessor(pred);
+ }
+
+ @Override
+ public BlockImpl getBlock() {
+ return c;
+ }
+ };
+ } else {
+ assert c.getElseSuccessor() == cur;
+ return new PredecessorHolder() {
+ @Override
+ public void setSuccessor(BlockImpl b) {
+ c.setElseSuccessor(b);
+ cur.removePredecessor(pred);
+ }
+
+ @Override
+ public BlockImpl getBlock() {
+ return c;
+ }
+ };
+ }
+ case EXCEPTION_BLOCK:
+ // add pred correctly to predecessor list
+ final ExceptionBlockImpl e = (ExceptionBlockImpl) pred;
+ if (e.getSuccessor() == cur) {
+ return singleSuccessorHolder(e, cur);
+ } else {
+ Set<Entry<TypeMirror, Set<Block>>> entrySet = e
+ .getExceptionalSuccessors().entrySet();
+ for (final Entry<TypeMirror, Set<Block>> entry : entrySet) {
+ if (entry.getValue().contains(cur)) {
+ return new PredecessorHolder() {
+ @Override
+ public void setSuccessor(BlockImpl b) {
+ e.addExceptionalSuccessor(b, entry.getKey());
+ cur.removePredecessor(pred);
+ }
+
+ @Override
+ public BlockImpl getBlock() {
+ return e;
+ }
+ };
+ }
+ }
+ }
+ assert false;
+ break;
+ case REGULAR_BLOCK:
+ RegularBlockImpl r = (RegularBlockImpl) pred;
+ return singleSuccessorHolder(r, cur);
+ }
+ return null;
+ }
+
+ /**
+ * @return a {@link PredecessorHolder} that sets the successor of a
+ * single successor block {@code s}.
+ */
+ protected static PredecessorHolder singleSuccessorHolder(
+ final SingleSuccessorBlockImpl s, final BlockImpl old) {
+ return new PredecessorHolder() {
+ @Override
+ public void setSuccessor(BlockImpl b) {
+ s.setSuccessor(b);
+ old.removePredecessor(s);
+ }
+
+ @Override
+ public BlockImpl getBlock() {
+ return s;
+ }
+ };
+ }
+ }
+
+ /* --------------------------------------------------------- */
+ /* Phase Two */
+ /* --------------------------------------------------------- */
+
+ /** Tuple class with up to three members. */
+ protected static class Tuple<A, B, C> {
+ public A a;
+ public B b;
+ public C c;
+
+ public Tuple(A a, B b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ public Tuple(A a, B b, C c) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }
+ }
+
+ /**
+ * Class that performs phase two of the translation process.
+ */
+ public class CFGTranslationPhaseTwo {
+
+ public CFGTranslationPhaseTwo() {
+ }
+
+ /**
+ * Perform phase two of the translation.
+ *
+ * @param in
+ * The result of phase one.
+ * @return a control flow graph that might still contain degenerate
+ * basic block (such as empty regular basic blocks or
+ * conditional blocks with the same block as 'then' and 'else'
+ * sucessor)
+ */
+ public ControlFlowGraph process(PhaseOneResult in) {
+
+ Map<Label, Integer> bindings = in.bindings;
+ ArrayList<ExtendedNode> nodeList = in.nodeList;
+ Set<Integer> leaders = in.leaders;
+
+ assert in.nodeList.size() > 0;
+
+ // exit blocks
+ SpecialBlockImpl regularExitBlock = new SpecialBlockImpl(
+ SpecialBlockType.EXIT);
+ SpecialBlockImpl exceptionalExitBlock = new SpecialBlockImpl(
+ SpecialBlockType.EXCEPTIONAL_EXIT);
+
+ // record missing edges that will be added later
+ Set<Tuple<? extends SingleSuccessorBlockImpl, Integer, ?>> missingEdges = new MostlySingleton<>();
+
+ // missing exceptional edges
+ Set<Tuple<ExceptionBlockImpl, Integer, TypeMirror>> missingExceptionalEdges = new HashSet<>();
+
+ // create start block
+ SpecialBlockImpl startBlock = new SpecialBlockImpl(
+ SpecialBlockType.ENTRY);
+ missingEdges.add(new Tuple<>(startBlock, 0));
+
+ // loop through all 'leaders' (while dynamically detecting the
+ // leaders)
+ RegularBlockImpl block = new RegularBlockImpl();
+ int i = 0;
+ for (ExtendedNode node : nodeList) {
+ switch (node.getType()) {
+ case NODE:
+ if (leaders.contains(i)) {
+ RegularBlockImpl b = new RegularBlockImpl();
+ block.setSuccessor(b);
+ block = b;
+ }
+ block.addNode(node.getNode());
+ node.setBlock(block);
+
+ // does this node end the execution (modeled as an edge to
+ // the exceptional exit block)
+ boolean terminatesExecution = node.getTerminatesExecution();
+ if (terminatesExecution) {
+ block.setSuccessor(exceptionalExitBlock);
+ block = new RegularBlockImpl();
+ }
+ break;
+ case CONDITIONAL_JUMP: {
+ ConditionalJump cj = (ConditionalJump) node;
+ // Exception nodes may fall through to conditional jumps,
+ // so we set the block which is required for the insertion
+ // of missing edges.
+ node.setBlock(block);
+ assert block != null;
+ final ConditionalBlockImpl cb = new ConditionalBlockImpl();
+ if (cj.getTrueFlowRule() != null) {
+ cb.setThenFlowRule(cj.getTrueFlowRule());
+ }
+ if (cj.getFalseFlowRule() != null) {
+ cb.setElseFlowRule(cj.getFalseFlowRule());
+ }
+ block.setSuccessor(cb);
+ block = new RegularBlockImpl();
+ // use two anonymous SingleSuccessorBlockImpl that set the
+ // 'then' and 'else' successor of the conditional block
+ final Label thenLabel = cj.getThenLabel();
+ final Label elseLabel = cj.getElseLabel();
+ missingEdges.add(new Tuple<>(
+ new SingleSuccessorBlockImpl() {
+ @Override
+ public void setSuccessor(BlockImpl successor) {
+ cb.setThenSuccessor(successor);
+ }
+ }, bindings.get(thenLabel)));
+ missingEdges.add(new Tuple<>(
+ new SingleSuccessorBlockImpl() {
+ @Override
+ public void setSuccessor(BlockImpl successor) {
+ cb.setElseSuccessor(successor);
+ }
+ }, bindings.get(elseLabel)));
+ break;
+ }
+ case UNCONDITIONAL_JUMP:
+ if (leaders.contains(i)) {
+ RegularBlockImpl b = new RegularBlockImpl();
+ block.setSuccessor(b);
+ block = b;
+ }
+ node.setBlock(block);
+ if (node.getLabel() == regularExitLabel) {
+ block.setSuccessor(regularExitBlock);
+ } else if (node.getLabel() == exceptionalExitLabel) {
+ block.setSuccessor(exceptionalExitBlock);
+ } else {
+ missingEdges.add(new Tuple<>(block, bindings.get(node
+ .getLabel())));
+ }
+ block = new RegularBlockImpl();
+ break;
+ case EXCEPTION_NODE:
+ NodeWithExceptionsHolder en = (NodeWithExceptionsHolder) node;
+ // create new exception block and link with previous block
+ ExceptionBlockImpl e = new ExceptionBlockImpl();
+ Node nn = en.getNode();
+ e.setNode(nn);
+ node.setBlock(e);
+ block.setSuccessor(e);
+ block = new RegularBlockImpl();
+
+ // ensure linking between e and next block (normal edge)
+ // Note: do not link to the next block for throw statements
+ // (these throw exceptions for sure)
+ if (!node.getTerminatesExecution()) {
+ missingEdges.add(new Tuple<>(e, i + 1));
+ }
+
+ // exceptional edges
+ for (Entry<TypeMirror, Set<Label>> entry : en.getExceptions()
+ .entrySet()) {
+ TypeMirror cause = entry.getKey();
+ for (Label label : entry.getValue()) {
+ Integer target = bindings.get(label);
+ missingExceptionalEdges
+ .add(new Tuple<ExceptionBlockImpl, Integer, TypeMirror>(
+ e, target, cause));
+ }
+ }
+ break;
+ }
+ i++;
+ }
+
+ // add missing edges
+ for (Tuple<? extends SingleSuccessorBlockImpl, Integer, ?> p : missingEdges) {
+ Integer index = p.b;
+ ExtendedNode extendedNode = nodeList.get(index);
+ BlockImpl target = extendedNode.getBlock();
+ SingleSuccessorBlockImpl source = p.a;
+ source.setSuccessor(target);
+ }
+
+ // add missing exceptional edges
+ for (Tuple<ExceptionBlockImpl, Integer, ?> p : missingExceptionalEdges) {
+ Integer index = p.b;
+ TypeMirror cause = (TypeMirror) p.c;
+ ExceptionBlockImpl source = p.a;
+ if (index == null) {
+ // edge to exceptional exit
+ source.addExceptionalSuccessor(exceptionalExitBlock, cause);
+ } else {
+ // edge to specific target
+ ExtendedNode extendedNode = nodeList.get(index);
+ BlockImpl target = extendedNode.getBlock();
+ source.addExceptionalSuccessor(target, cause);
+ }
+ }
+
+ return new ControlFlowGraph(startBlock, regularExitBlock, exceptionalExitBlock, in.underlyingAST,
+ in.treeLookupMap, in.convertedTreeLookupMap, in.returnNodes);
+ }
+ }
+
+ /* --------------------------------------------------------- */
+ /* Phase One */
+ /* --------------------------------------------------------- */
+
+ /**
+ * A wrapper object to pass around the result of phase one. For a
+ * documentation of the fields see {@link CFGTranslationPhaseOne}.
+ */
+ protected static class PhaseOneResult {
+
+ private final IdentityHashMap<Tree, Node> treeLookupMap;
+ private final IdentityHashMap<Tree, Node> convertedTreeLookupMap;
+ private final UnderlyingAST underlyingAST;
+ private final Map<Label, Integer> bindings;
+ private final ArrayList<ExtendedNode> nodeList;
+ private final Set<Integer> leaders;
+ private final List<ReturnNode> returnNodes;
+
+ public PhaseOneResult(UnderlyingAST underlyingAST,
+ IdentityHashMap<Tree, Node> treeLookupMap,
+ IdentityHashMap<Tree, Node> convertedTreeLookupMap,
+ ArrayList<ExtendedNode> nodeList, Map<Label, Integer> bindings,
+ Set<Integer> leaders, List<ReturnNode> returnNodes) {
+ this.underlyingAST = underlyingAST;
+ this.treeLookupMap = treeLookupMap;
+ this.convertedTreeLookupMap = convertedTreeLookupMap;
+ this.nodeList = nodeList;
+ this.bindings = bindings;
+ this.leaders = leaders;
+ this.returnNodes = returnNodes;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (ExtendedNode n : nodeList) {
+ sb.append(nodeToString(n));
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ protected String nodeToString(ExtendedNode n) {
+ if (n.getType() == ExtendedNodeType.CONDITIONAL_JUMP) {
+ ConditionalJump t = (ConditionalJump) n;
+ return "TwoTargetConditionalJump("
+ + resolveLabel(t.getThenLabel()) + ","
+ + resolveLabel(t.getElseLabel()) + ")";
+ } else if (n.getType() == ExtendedNodeType.UNCONDITIONAL_JUMP) {
+ return "UnconditionalJump(" + resolveLabel(n.getLabel()) + ")";
+ } else {
+ return n.toString();
+ }
+ }
+
+ private String resolveLabel(Label label) {
+ Integer index = bindings.get(label);
+ if (index == null) {
+ return "null";
+ }
+ return nodeToString(nodeList.get(index));
+ }
+
+ }
+
+ /**
+ * Class that performs phase one of the translation process. It generates
+ * the following information:
+ * <ul>
+ * <li>A sequence of extended nodes.</li>
+ * <li>A set of bindings from {@link Label}s to positions in the node
+ * sequence.</li>
+ * <li>A set of leader nodes that give rise to basic blocks in phase two.</li>
+ * <li>A lookup map that gives the mapping from AST tree nodes to
+ * {@link Node}s.</li>
+ * </ul>
+ *
+ * <p>
+ *
+ * The return type of this scanner is {@link Node}. For expressions, the
+ * corresponding node is returned to allow linking between different nodes.
+ *
+ * However, for statements there is usually no single {@link Node} that is
+ * created, and thus no node is returned (rather, null is returned).
+ *
+ * <p>
+ *
+ * Every {@code visit*} method is assumed to add at least one extended node
+ * to the list of nodes (which might only be a jump).
+ *
+ */
+ public class CFGTranslationPhaseOne extends TreePathScanner<Node, Void> {
+
+ public CFGTranslationPhaseOne() {
+ }
+
+ /**
+ * Annotation processing environment and its associated type and tree
+ * utilities.
+ */
+ protected ProcessingEnvironment env;
+ protected Elements elements;
+ protected Types types;
+ protected Trees trees;
+ protected TreeBuilder treeBuilder;
+ protected AnnotationProvider annotationProvider;
+
+ /**
+ * Current {@link Label} to which a break statement with no label should
+ * jump, or null if there is no valid destination.
+ */
+ protected /*@Nullable*/ Label breakTargetL;
+
+ /**
+ * Map from AST label Names to CFG {@link Label}s for breaks. Each
+ * labeled statement creates two CFG {@link Label}s, one for break and
+ * one for continue.
+ */
+ protected Map<Name, Label> breakLabels;
+
+ /**
+ * Current {@link Label} to which a continue statement with no label
+ * should jump, or null if there is no valid destination.
+ */
+ protected /*@Nullable*/ Label continueTargetL;
+
+ /**
+ * Map from AST label Names to CFG {@link Label}s for continues. Each
+ * labeled statement creates two CFG {@link Label}s, one for break and
+ * one for continue.
+ */
+ protected Map<Name, Label> continueLabels;
+
+ /**
+ * Maps from AST {@link Tree}s to {@link Node}s. Every Tree that produces
+ * a value will have at least one corresponding Node. Trees
+ * that undergo conversions, such as boxing or unboxing, can map to two
+ * distinct Nodes. The Node for the pre-conversion value is stored
+ * in the treeLookupMap, while the Node for the post-conversion value
+ * is stored in the convertedTreeLookupMap.
+ */
+ protected IdentityHashMap<Tree, Node> treeLookupMap;
+
+ /** Map from AST {@link Tree}s to post-conversion {@link Node}s. */
+ protected IdentityHashMap<Tree, Node> convertedTreeLookupMap;
+
+ /** The list of extended nodes. */
+ protected ArrayList<ExtendedNode> nodeList;
+
+ /**
+ * The bindings of labels to positions (i.e., indices) in the
+ * {@code nodeList}.
+ */
+ protected Map<Label, Integer> bindings;
+
+ /** The set of leaders (represented as indices into {@code nodeList}). */
+ protected Set<Integer> leaders;
+
+ /**
+ * All return nodes (if any) encountered. Only includes return
+ * statements that actually return something
+ */
+ private List<ReturnNode> returnNodes;
+
+ /**
+ * Nested scopes of try-catch blocks in force at the current
+ * program point.
+ */
+ private TryStack tryStack;
+
+ /**
+ * Performs the actual work of phase one.
+ *
+ * @param bodyPath
+ * path to the body of the underlying AST's method
+ * @param env
+ * annotation processing environment containing type
+ * utilities
+ * @param underlyingAST
+ * the AST for which the CFG is to be built
+ * @param exceptionalExitLabel
+ * the label for exceptional exits from the CFG
+ * @param treeBuilder
+ * builder for new AST nodes
+ * @param annotationProvider
+ * extracts annotations from AST nodes
+ * @return the result of phase one
+ */
+ public PhaseOneResult process(
+ TreePath bodyPath, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST, Label exceptionalExitLabel,
+ TreeBuilder treeBuilder, AnnotationProvider annotationProvider) {
+ this.env = env;
+ this.tryStack = new TryStack(exceptionalExitLabel);
+ this.treeBuilder = treeBuilder;
+ this.annotationProvider = annotationProvider;
+ elements = env.getElementUtils();
+ types = env.getTypeUtils();
+
+ // initialize lists and maps
+ treeLookupMap = new IdentityHashMap<>();
+ convertedTreeLookupMap = new IdentityHashMap<>();
+ nodeList = new ArrayList<>();
+ bindings = new HashMap<>();
+ leaders = new HashSet<>();
+ breakLabels = new HashMap<>();
+ continueLabels = new HashMap<>();
+ returnNodes = new ArrayList<>();
+
+ // traverse AST of the method body
+ scan(bodyPath, null);
+
+ // add marker to indicate that the next block will be the exit block
+ // Note: if there is a return statement earlier in the method (which
+ // is always the case for non-void methods), then this is not
+ // strictly necessary. However, it is also not a problem, as it will
+ // just generate a degenerated control graph case that will be
+ // removed in a later phase.
+ nodeList.add(new UnconditionalJump(regularExitLabel));
+
+ return new PhaseOneResult(underlyingAST, treeLookupMap,
+ convertedTreeLookupMap, nodeList,
+ bindings, leaders, returnNodes);
+ }
+
+ public PhaseOneResult process(
+ CompilationUnitTree root, ProcessingEnvironment env,
+ UnderlyingAST underlyingAST, Label exceptionalExitLabel,
+ TreeBuilder treeBuilder, AnnotationProvider annotationProvider) {
+ trees = Trees.instance(env);
+ TreePath bodyPath = trees.getPath(root, underlyingAST.getCode());
+ return process(bodyPath, env, underlyingAST, exceptionalExitLabel, treeBuilder, annotationProvider);
+ }
+
+ /**
+ * Perform any actions required when CFG translation creates a
+ * new Tree that is not part of the original AST.
+ *
+ * @param tree the newly created Tree
+ */
+ public void handleArtificialTree(Tree tree) {}
+
+ /* --------------------------------------------------------- */
+ /* Nodes and Labels Management */
+ /* --------------------------------------------------------- */
+
+ /**
+ * Add a node to the lookup map if it not already present.
+ *
+ * @param node
+ * The node to add to the lookup map.
+ */
+ protected void addToLookupMap(Node node) {
+ Tree tree = node.getTree();
+ if (tree == null) {
+ return;
+ }
+ if (!treeLookupMap.containsKey(tree)) {
+ treeLookupMap.put(tree, node);
+ }
+
+ Tree enclosingParens = parenMapping.get(tree);
+ while (enclosingParens != null) {
+ treeLookupMap.put(enclosingParens, node);
+ enclosingParens = parenMapping.get(enclosingParens);
+ }
+ }
+
+ /**
+ * Add a node in the post-conversion lookup map. The node
+ * should refer to a Tree and that Tree should already be in
+ * the pre-conversion lookup map. This method is used to
+ * update the Tree-Node mapping with conversion nodes.
+ *
+ * @param node
+ * The node to add to the lookup map.
+ */
+ protected void addToConvertedLookupMap(Node node) {
+ Tree tree = node.getTree();
+ addToConvertedLookupMap(tree, node);
+ }
+
+ /**
+ * Add a node in the post-conversion lookup map. The tree
+ * argument should already be in the pre-conversion lookup
+ * map. This method is used to update the Tree-Node mapping
+ * with conversion nodes.
+ *
+ * @param tree
+ * The tree used as a key in the map.
+ * @param node
+ * The node to add to the lookup map.
+ */
+ protected void addToConvertedLookupMap(Tree tree, Node node) {
+ assert tree != null;
+ assert treeLookupMap.containsKey(tree);
+ convertedTreeLookupMap.put(tree, node);
+ }
+
+ /**
+ * Extend the list of extended nodes with a node.
+ *
+ * @param node
+ * The node to add.
+ * @return the same node (for convenience)
+ */
+ protected <T extends Node> T extendWithNode(T node) {
+ addToLookupMap(node);
+ extendWithExtendedNode(new NodeHolder(node));
+ return node;
+ }
+
+ /**
+ * Extend the list of extended nodes with a node, where
+ * {@code node} might throw the exception {@code cause}.
+ *
+ * @param node
+ * The node to add.
+ * @param cause
+ * An exception that the node might throw.
+ * @return the node holder
+ */
+ protected NodeWithExceptionsHolder extendWithNodeWithException(Node node, TypeMirror cause) {
+ addToLookupMap(node);
+ return extendWithNodeWithExceptions(node, Collections.singleton(cause));
+ }
+
+ /**
+ * Extend the list of extended nodes with a node, where
+ * {@code node} might throw any of the exception in
+ * {@code causes}.
+ *
+ * @param node
+ * The node to add.
+ * @param causes
+ * Set of exceptions that the node might throw.
+ * @return the node holder
+ */
+ protected NodeWithExceptionsHolder extendWithNodeWithExceptions(Node node,
+ Set<TypeMirror> causes) {
+ addToLookupMap(node);
+ Map<TypeMirror, Set<Label>> exceptions = new HashMap<>();
+ for (TypeMirror cause : causes) {
+ exceptions.put(cause, tryStack.possibleLabels(cause));
+ }
+ NodeWithExceptionsHolder exNode = new NodeWithExceptionsHolder(
+ node, exceptions);
+ extendWithExtendedNode(exNode);
+ return exNode;
+ }
+
+ /**
+ * Insert {@code node} after {@code pred} in
+ * the list of extended nodes, or append to the list if
+ * {@code pred} is not present.
+ *
+ * @param node
+ * The node to add.
+ * @param pred
+ * The desired predecessor of node.
+ * @return the node holder
+ */
+ protected <T extends Node> T insertNodeAfter(T node, Node pred) {
+ addToLookupMap(node);
+ insertExtendedNodeAfter(new NodeHolder(node), pred);
+ return node;
+ }
+
+ /**
+ * Insert a {@code node} that might throw the exception
+ * {@code cause} after {@code pred} in the list of
+ * extended nodes, or append to the list if {@code pred}
+ * is not present.
+ *
+ * @param node
+ * The node to add.
+ * @param causes
+ * Set of exceptions that the node might throw.
+ * @param pred
+ * The desired predecessor of node.
+ * @return the node holder
+ */
+ protected NodeWithExceptionsHolder insertNodeWithExceptionsAfter(Node node,
+ Set<TypeMirror> causes, Node pred) {
+ addToLookupMap(node);
+ Map<TypeMirror, Set<Label>> exceptions = new HashMap<>();
+ for (TypeMirror cause : causes) {
+ exceptions.put(cause, tryStack.possibleLabels(cause));
+ }
+ NodeWithExceptionsHolder exNode = new NodeWithExceptionsHolder(
+ node, exceptions);
+ insertExtendedNodeAfter(exNode, pred);
+ return exNode;
+ }
+
+ /**
+ * Extend the list of extended nodes with an extended node.
+ *
+ * @param n
+ * The extended node.
+ */
+ protected void extendWithExtendedNode(ExtendedNode n) {
+ nodeList.add(n);
+ }
+
+ /**
+ * Insert {@code n} after the node {@code pred} in the
+ * list of extended nodes, or append {@code n} if {@code pred}
+ * is not present.
+ *
+ * @param n
+ * The extended node.
+ * @param pred
+ * The desired predecessor.
+ */
+ protected void insertExtendedNodeAfter(ExtendedNode n, Node pred) {
+ int index = -1;
+ for (int i = 0; i < nodeList.size(); i++) {
+ ExtendedNode inList = nodeList.get(i);
+ if (inList instanceof NodeHolder ||
+ inList instanceof NodeWithExceptionsHolder) {
+ if (inList.getNode() == pred) {
+ index = i;
+ break;
+ }
+ }
+ }
+ if (index != -1) {
+ nodeList.add(index + 1, n);
+ // update bindings
+ for (Entry<Label, Integer> e : bindings.entrySet()) {
+ if (e.getValue() >= index+1) {
+ bindings.put(e.getKey(), e.getValue() + 1);
+ }
+ }
+ // update leaders
+ Set<Integer> newLeaders = new HashSet<>();
+ for (Integer l : leaders) {
+ if (l >= index+1) {
+ newLeaders.add(l+1);
+ } else {
+ newLeaders.add(l);
+ }
+ }
+ leaders = newLeaders;
+ } else {
+ nodeList.add(n);
+ }
+ }
+
+ /**
+ * Add the label {@code l} to the extended node that will be placed next
+ * in the sequence.
+ */
+ protected void addLabelForNextNode(Label l) {
+ leaders.add(nodeList.size());
+ bindings.put(l, nodeList.size());
+ }
+
+ /* --------------------------------------------------------- */
+ /* Utility Methods */
+ /* --------------------------------------------------------- */
+
+ protected long uid = 0;
+ protected String uniqueName(String prefix) {
+ return prefix + "#num" + uid++;
+ }
+
+ /**
+ * If the input node is an unboxed primitive type, insert a call to the
+ * appropriate valueOf method, otherwise leave it alone.
+ *
+ * @param node
+ * in input node
+ * @return a Node representing the boxed version of the input, which may
+ * simply be the input node
+ */
+ protected Node box(Node node) {
+ // For boxing conversion, see JLS 5.1.7
+ if (TypesUtils.isPrimitive(node.getType())) {
+ PrimitiveType primitive = types.getPrimitiveType(node.getType()
+ .getKind());
+ TypeMirror boxedType = types.getDeclaredType(types
+ .boxedClass(primitive));
+
+ TypeElement boxedElement = (TypeElement)((DeclaredType)boxedType).asElement();
+ IdentifierTree classTree = treeBuilder.buildClassUse(boxedElement);
+ handleArtificialTree(classTree);
+ ClassNameNode className = new ClassNameNode(classTree);
+ className.setInSource(false);
+ insertNodeAfter(className, node);
+
+ MemberSelectTree valueOfSelect = treeBuilder.buildValueOfMethodAccess(classTree);
+ handleArtificialTree(valueOfSelect);
+ MethodAccessNode valueOfAccess = new MethodAccessNode(valueOfSelect, className);
+ valueOfAccess.setInSource(false);
+ insertNodeAfter(valueOfAccess, className);
+
+ MethodInvocationTree valueOfCall =
+ treeBuilder.buildMethodInvocation(valueOfSelect, (ExpressionTree)node.getTree());
+ handleArtificialTree(valueOfCall);
+ Node boxed = new MethodInvocationNode(valueOfCall, valueOfAccess,
+ Collections.singletonList(node),
+ getCurrentPath());
+ boxed.setInSource(false);
+ // Add Throwable to account for unchecked exceptions
+ TypeElement throwableElement = elements
+ .getTypeElement("java.lang.Throwable");
+ addToConvertedLookupMap(node.getTree(), boxed);
+ insertNodeWithExceptionsAfter(boxed,
+ Collections.singleton(throwableElement.asType()), valueOfAccess);
+ return boxed;
+ } else {
+ return node;
+ }
+ }
+
+ /**
+ * If the input node is a boxed type, unbox it, otherwise leave it
+ * alone.
+ *
+ * @param node
+ * in input node
+ * @return a Node representing the unboxed version of the input, which
+ * may simply be the input node
+ */
+ protected Node unbox(Node node) {
+ if (TypesUtils.isBoxedPrimitive(node.getType())) {
+
+ MemberSelectTree primValueSelect =
+ treeBuilder.buildPrimValueMethodAccess(node.getTree());
+ handleArtificialTree(primValueSelect);
+ MethodAccessNode primValueAccess = new MethodAccessNode(primValueSelect, node);
+ primValueAccess.setInSource(false);
+ // Method access may throw NullPointerException
+ TypeElement npeElement = elements
+ .getTypeElement("java.lang.NullPointerException");
+ insertNodeWithExceptionsAfter(primValueAccess,
+ Collections.singleton(npeElement.asType()), node);
+
+ MethodInvocationTree primValueCall =
+ treeBuilder.buildMethodInvocation(primValueSelect);
+ handleArtificialTree(primValueCall);
+ Node unboxed = new MethodInvocationNode(primValueCall, primValueAccess,
+ Collections.<Node>emptyList(),
+ getCurrentPath());
+ unboxed.setInSource(false);
+
+ // Add Throwable to account for unchecked exceptions
+ TypeElement throwableElement = elements
+ .getTypeElement("java.lang.Throwable");
+ addToConvertedLookupMap(node.getTree(), unboxed);
+ insertNodeWithExceptionsAfter(unboxed,
+ Collections.singleton(throwableElement.asType()), primValueAccess);
+ return unboxed;
+ } else {
+ return node;
+ }
+ }
+
+ private TreeInfo getTreeInfo(Tree tree) {
+ final TypeMirror type = InternalUtils.typeOf(tree);
+ final boolean boxed = TypesUtils.isBoxedPrimitive(type);
+ final TypeMirror unboxedType = boxed ? types.unboxedType(type) : type;
+
+ final boolean bool = TypesUtils.isBooleanType(type);
+ final boolean numeric = TypesUtils.isNumeric(unboxedType);
+
+ return new TreeInfo() {
+ @Override
+ public boolean isNumeric() {
+ return numeric;
+ }
+
+ @Override
+ public boolean isBoxed() {
+ return boxed;
+ }
+
+ @Override
+ public boolean isBoolean() {
+ return bool;
+ }
+
+ @Override
+ public TypeMirror unboxedType() {
+ return unboxedType;
+ }
+ };
+ }
+
+ /**
+ * @return the unboxed tree if necessary, as described in JLS 5.1.8
+ */
+ private Node unboxAsNeeded(Node node, boolean boxed) {
+ return boxed ? unbox(node) : node;
+ }
+
+ /**
+ * Convert the input node to String type, if it isn't already.
+ *
+ * @param node
+ * an input node
+ * @return a Node with the value promoted to String, which may be the
+ * input node
+ */
+ protected Node stringConversion(Node node) {
+ // For string conversion, see JLS 5.1.11
+ TypeElement stringElement =
+ elements.getTypeElement("java.lang.String");
+ if (!TypesUtils.isString(node.getType())) {
+ Node converted = new StringConversionNode(node.getTree(), node,
+ stringElement.asType());
+ addToConvertedLookupMap(converted);
+ insertNodeAfter(converted, node);
+ return converted;
+ } else {
+ return node;
+ }
+ }
+
+ /**
+ * Perform unary numeric promotion on the input node.
+ *
+ * @param node
+ * a node producing a value of numeric primitive or boxed
+ * type
+ * @return a Node with the value promoted to the int, long float or
+ * double, which may be the input node
+ */
+ protected Node unaryNumericPromotion(Node node) {
+ // For unary numeric promotion, see JLS 5.6.1
+ node = unbox(node);
+
+ switch (node.getType().getKind()) {
+ case BYTE:
+ case CHAR:
+ case SHORT: {
+ TypeMirror intType = types.getPrimitiveType(TypeKind.INT);
+ Node widened = new WideningConversionNode(node.getTree(), node, intType);
+ addToConvertedLookupMap(widened);
+ insertNodeAfter(widened, node);
+ return widened;
+ }
+ default:
+ // Nothing to do.
+ break;
+ }
+
+ return node;
+ }
+
+ /**
+ * Returns true if the argument type is a numeric primitive or
+ * a boxed numeric primitive and false otherwise.
+ */
+ protected boolean isNumericOrBoxed(TypeMirror type) {
+ if (TypesUtils.isBoxedPrimitive(type)) {
+ type = types.unboxedType(type);
+ }
+ return TypesUtils.isNumeric(type);
+ }
+
+ /**
+ * Compute the type to which two numeric types must be promoted
+ * before performing a binary numeric operation on them. The
+ * input types must both be numeric and the output type is primitive.
+ *
+ * @param left the type of the left operand
+ * @param right the type of the right operand
+ * @return a TypeMirror representing the binary numeric promoted type
+ */
+ protected TypeMirror binaryPromotedType(TypeMirror left, TypeMirror right) {
+ if (TypesUtils.isBoxedPrimitive(left)) {
+ left = types.unboxedType(left);
+ }
+ if (TypesUtils.isBoxedPrimitive(right)) {
+ right = types.unboxedType(right);
+ }
+ TypeKind promotedTypeKind = TypesUtils.widenedNumericType(left, right);
+ return types.getPrimitiveType(promotedTypeKind);
+ }
+
+ /**
+ * Perform binary numeric promotion on the input node to make it match
+ * the expression type.
+ *
+ * @param node
+ * a node producing a value of numeric primitive or boxed
+ * type
+ * @param exprType
+ * the type to promote the value to
+ * @return a Node with the value promoted to the exprType, which may be
+ * the input node
+ */
+ protected Node binaryNumericPromotion(Node node, TypeMirror exprType) {
+ // For binary numeric promotion, see JLS 5.6.2
+ node = unbox(node);
+
+ if (!types.isSameType(node.getType(), exprType)) {
+ Node widened = new WideningConversionNode(node.getTree(), node,
+ exprType);
+ addToConvertedLookupMap(widened);
+ insertNodeAfter(widened, node);
+ return widened;
+ } else {
+ return node;
+ }
+ }
+
+ /**
+ * Perform widening primitive conversion on the input node to make it
+ * match the destination type.
+ *
+ * @param node
+ * a node producing a value of numeric primitive type
+ * @param destType
+ * the type to widen the value to
+ * @return a Node with the value widened to the exprType, which may be
+ * the input node
+ */
+ protected Node widen(Node node, TypeMirror destType) {
+ // For widening conversion, see JLS 5.1.2
+ assert TypesUtils.isPrimitive(node.getType())
+ && TypesUtils.isPrimitive(destType) : "widening must be applied to primitive types";
+ if (types.isSubtype(node.getType(), destType)
+ && !types.isSameType(node.getType(), destType)) {
+ Node widened = new WideningConversionNode(node.getTree(), node,
+ destType);
+ addToConvertedLookupMap(widened);
+ insertNodeAfter(widened, node);
+ return widened;
+ } else {
+ return node;
+ }
+ }
+
+ /**
+ * Perform narrowing conversion on the input node to make it match the
+ * destination type.
+ *
+ * @param node
+ * a node producing a value of numeric primitive type
+ * @param destType
+ * the type to narrow the value to
+ * @return a Node with the value narrowed to the exprType, which may be
+ * the input node
+ */
+ protected Node narrow(Node node, TypeMirror destType) {
+ // For narrowing conversion, see JLS 5.1.3
+ assert TypesUtils.isPrimitive(node.getType())
+ && TypesUtils.isPrimitive(destType) : "narrowing must be applied to primitive types";
+ if (types.isSubtype(destType, node.getType())
+ && !types.isSameType(destType, node.getType())) {
+ Node narrowed = new NarrowingConversionNode(node.getTree(), node,
+ destType);
+ addToConvertedLookupMap(narrowed);
+ insertNodeAfter(narrowed, node);
+ return narrowed;
+ } else {
+ return node;
+ }
+ }
+
+ /**
+ * Perform narrowing conversion and optionally boxing conversion on the
+ * input node to make it match the destination type.
+ *
+ * @param node
+ * a node producing a value of numeric primitive type
+ * @param destType
+ * the type to narrow the value to (possibly boxed)
+ * @return a Node with the value narrowed and boxed to the destType,
+ * which may be the input node
+ */
+ protected Node narrowAndBox(Node node, TypeMirror destType) {
+ if (TypesUtils.isBoxedPrimitive(destType)) {
+ return box(narrow(node, types.unboxedType(destType)));
+ } else {
+ return narrow(node, destType);
+ }
+ }
+
+
+ /**
+ * Return whether a conversion from the type of the node to varType
+ * requires narrowing.
+ *
+ * @param varType the type of a variable (or general LHS) to be converted to
+ * @param node a node whose value is being converted
+ * @return whether this conversion requires narrowing to succeed
+ */
+ protected boolean conversionRequiresNarrowing(TypeMirror varType, Node node) {
+ // Narrowing is restricted to cases where the left hand side
+ // is byte, char, short or Byte, Char, Short and the right
+ // hand side is a constant.
+ TypeMirror unboxedVarType = TypesUtils.isBoxedPrimitive(varType) ? types
+ .unboxedType(varType) : varType;
+ TypeKind unboxedVarKind = unboxedVarType.getKind();
+ boolean isLeftNarrowableTo = unboxedVarKind == TypeKind.BYTE
+ || unboxedVarKind == TypeKind.SHORT
+ || unboxedVarKind == TypeKind.CHAR;
+ boolean isRightConstant = node instanceof ValueLiteralNode;
+ return isLeftNarrowableTo && isRightConstant;
+ }
+
+
+ /**
+ * Assignment conversion and method invocation conversion are almost
+ * identical, except that assignment conversion allows narrowing. We
+ * factor out the common logic here.
+ *
+ * @param node
+ * a Node producing a value
+ * @param varType
+ * the type of a variable
+ * @param contextAllowsNarrowing
+ * whether to allow narrowing (for assignment conversion) or
+ * not (for method invocation conversion)
+ * @return a Node with the value converted to the type of the variable,
+ * which may be the input node itself
+ */
+ protected Node commonConvert(Node node, TypeMirror varType,
+ boolean contextAllowsNarrowing) {
+ // For assignment conversion, see JLS 5.2
+ // For method invocation conversion, see JLS 5.3
+
+ // Check for identical types or "identity conversion"
+ TypeMirror nodeType = node.getType();
+ boolean isSameType = types.isSameType(nodeType, varType);
+ if (isSameType) {
+ return node;
+ }
+
+ boolean isRightNumeric = TypesUtils.isNumeric(nodeType);
+ boolean isRightPrimitive = TypesUtils.isPrimitive(nodeType);
+ boolean isRightBoxed = TypesUtils.isBoxedPrimitive(nodeType);
+ boolean isRightReference = nodeType instanceof ReferenceType;
+ boolean isLeftNumeric = TypesUtils.isNumeric(varType);
+ boolean isLeftPrimitive = TypesUtils.isPrimitive(varType);
+ // boolean isLeftBoxed = TypesUtils.isBoxedPrimitive(varType);
+ boolean isLeftReference = varType instanceof ReferenceType;
+ boolean isSubtype = types.isSubtype(nodeType, varType);
+
+ if (isRightNumeric && isLeftNumeric && isSubtype) {
+ node = widen(node, varType);
+ nodeType = node.getType();
+ } else if (isRightReference && isLeftReference && isSubtype) {
+ // widening reference conversion is a no-op, but if it
+ // applies, then later conversions do not.
+ } else if (isRightPrimitive && isLeftReference) {
+ if (contextAllowsNarrowing && conversionRequiresNarrowing(varType, node)) {
+ node = narrowAndBox(node, varType);
+ nodeType = node.getType();
+ } else {
+ node = box(node);
+ nodeType = node.getType();
+ }
+ } else if (isRightBoxed && isLeftPrimitive) {
+ node = unbox(node);
+ nodeType = node.getType();
+
+ if (types.isSubtype(nodeType, varType)
+ && !types.isSameType(nodeType, varType)) {
+ node = widen(node, varType);
+ nodeType = node.getType();
+ }
+ } else if (isRightPrimitive && isLeftPrimitive) {
+ if (contextAllowsNarrowing && conversionRequiresNarrowing(varType, node)) {
+ node = narrow(node, varType);
+ nodeType = node.getType();
+ }
+ }
+
+ // TODO: if checkers need to know about null references of
+ // a particular type, add logic for them here.
+
+ return node;
+ }
+
+ /**
+ * Perform assignment conversion so that it can be assigned to a
+ * variable of the given type.
+ *
+ * @param node
+ * a Node producing a value
+ * @param varType
+ * the type of a variable
+ * @return a Node with the value converted to the type of the variable,
+ * which may be the input node itself
+ */
+ protected Node assignConvert(Node node, TypeMirror varType) {
+ return commonConvert(node, varType, true);
+ }
+
+ /**
+ * Perform method invocation conversion so that the node can be passed
+ * as a formal parameter of the given type.
+ *
+ * @param node
+ * a Node producing a value
+ * @param formalType
+ * the type of a formal parameter
+ * @return a Node with the value converted to the type of the formal,
+ * which may be the input node itself
+ */
+ protected Node methodInvocationConvert(Node node, TypeMirror formalType) {
+ return commonConvert(node, formalType, false);
+ }
+
+ /**
+ * Given a method element and as list of argument expressions, return a
+ * list of {@link Node}s representing the arguments converted for a call
+ * of the method. This method applies to both method invocations and
+ * constructor calls.
+ *
+ * @param method
+ * an ExecutableElement representing a method to be called
+ * @param actualExprs
+ * a List of argument expressions to a call
+ * @return a List of {@link Node}s representing arguments after
+ * conversions required by a call to this method.
+ */
+ protected List<Node> convertCallArguments(ExecutableElement method,
+ List<? extends ExpressionTree> actualExprs) {
+ // NOTE: It is important to convert one method argument before
+ // generating CFG nodes for the next argument, since label binding
+ // expects nodes to be generated in execution order. Therefore,
+ // this method first determines which conversions need to be applied
+ // and then iterates over the actual arguments.
+ List<? extends VariableElement> formals = method.getParameters();
+
+ ArrayList<Node> convertedNodes = new ArrayList<Node>();
+
+ int numFormals = formals.size();
+ int numActuals = actualExprs.size();
+ if (method.isVarArgs()) {
+ // Create a new array argument if the actuals outnumber
+ // the formals, or if the last actual is not assignable
+ // to the last formal.
+ int lastArgIndex = numFormals - 1;
+ TypeMirror lastParamType = formals.get(lastArgIndex).asType();
+ List<Node> dimensions = new ArrayList<>();
+ List<Node> initializers = new ArrayList<>();
+
+ if (numActuals == numFormals - 1) {
+ // Apply method invocation conversion to all actual
+ // arguments, then create and append an empty array
+ for (int i = 0; i < numActuals; i++) {
+ Node actualVal = scan(actualExprs.get(i), null);
+ convertedNodes.add(methodInvocationConvert(actualVal,
+ formals.get(i).asType()));
+ }
+
+ Node lastArgument = new ArrayCreationNode(null,
+ lastParamType, dimensions, initializers);
+ extendWithNode(lastArgument);
+
+ convertedNodes.add(lastArgument);
+ } else {
+ TypeMirror actualType = InternalUtils.typeOf(actualExprs
+ .get(lastArgIndex));
+ if (numActuals == numFormals
+ && types.isAssignable(actualType, lastParamType)) {
+ // Normal call with no array creation, apply method
+ // invocation conversion to all arguments.
+ for (int i = 0; i < numActuals; i++) {
+ Node actualVal = scan(actualExprs.get(i), null);
+ convertedNodes.add(methodInvocationConvert(actualVal,
+ formals.get(i).asType()));
+ }
+ } else {
+ assert lastParamType instanceof ArrayType :
+ "variable argument formal must be an array";
+ // Apply method invocation conversion to lastArgIndex
+ // arguments and use the remaining ones to initialize
+ // an array.
+ for (int i = 0; i < lastArgIndex; i++) {
+ Node actualVal = scan(actualExprs.get(i), null);
+ convertedNodes.add(methodInvocationConvert(actualVal,
+ formals.get(i).asType()));
+ }
+
+ TypeMirror elemType =
+ ((ArrayType)lastParamType).getComponentType();
+ for (int i = lastArgIndex; i < numActuals; i++) {
+ Node actualVal = scan(actualExprs.get(i), null);
+ initializers.add(assignConvert(actualVal, elemType));
+ }
+
+ Node lastArgument = new ArrayCreationNode(null,
+ lastParamType, dimensions, initializers);
+ extendWithNode(lastArgument);
+ convertedNodes.add(lastArgument);
+ }
+ }
+ } else {
+ for (int i = 0; i < numActuals; i++) {
+ Node actualVal = scan(actualExprs.get(i), null);
+ convertedNodes.add(methodInvocationConvert(actualVal,
+ formals.get(i).asType()));
+ }
+ }
+
+ return convertedNodes;
+ }
+
+ /**
+ * Convert an operand of a conditional expression to the type of the
+ * whole expression.
+ *
+ * @param node
+ * a node occurring as the second or third operand of
+ * a conditional expression
+ * @param destType
+ * the type to promote the value to
+ * @return a Node with the value promoted to the destType, which may be
+ * the input node
+ */
+ protected Node conditionalExprPromotion(Node node, TypeMirror destType) {
+ // For rules on converting operands of conditional expressions,
+ // JLS 15.25
+ TypeMirror nodeType = node.getType();
+
+ // If the operand is already the same type as the whole
+ // expression, then do nothing.
+ if (types.isSameType(nodeType, destType)) {
+ return node;
+ }
+
+ // If the operand is a primitive and the whole expression is
+ // boxed, then apply boxing.
+ if (TypesUtils.isPrimitive(nodeType) &&
+ TypesUtils.isBoxedPrimitive(destType)) {
+ return box(node);
+ }
+
+ // If the operand is byte or Byte and the whole expression is
+ // short, then convert to short.
+ boolean isBoxedPrimitive = TypesUtils.isBoxedPrimitive(nodeType);
+ TypeMirror unboxedNodeType =
+ isBoxedPrimitive ? types.unboxedType(nodeType) : nodeType;
+ TypeMirror unboxedDestType =
+ TypesUtils.isBoxedPrimitive(destType) ?
+ types.unboxedType(destType) : destType;
+ if (TypesUtils.isNumeric(unboxedNodeType) &&
+ TypesUtils.isNumeric(unboxedDestType)) {
+ if (unboxedNodeType.getKind() == TypeKind.BYTE &&
+ destType.getKind() == TypeKind.SHORT) {
+ if (isBoxedPrimitive) {
+ node = unbox(node);
+ }
+ return widen(node, destType);
+ }
+
+ // If the operand is Byte, Short or Character and the whole expression
+ // is the unboxed version of it, then apply unboxing.
+ TypeKind destKind = destType.getKind();
+ if (destKind == TypeKind.BYTE || destKind == TypeKind.CHAR ||
+ destKind == TypeKind.SHORT) {
+ if (isBoxedPrimitive) {
+ return unbox(node);
+ } else if (nodeType.getKind() == TypeKind.INT) {
+ return narrow(node, destType);
+ }
+ }
+
+ return binaryNumericPromotion(node, destType);
+ }
+
+ // For the final case in JLS 15.25, apply boxing but not lub.
+ if (TypesUtils.isPrimitive(nodeType) &&
+ (destType.getKind() == TypeKind.DECLARED ||
+ destType.getKind() == TypeKind.UNION ||
+ destType.getKind() == TypeKind.INTERSECTION)) {
+ return box(node);
+ }
+
+ return node;
+ }
+
+ /**
+ * Returns the label {@link Name} of the leaf in the argument path, or
+ * null if the leaf is not a labeled statement.
+ */
+ protected /*@Nullable*/ Name getLabel(TreePath path) {
+ if (path.getParentPath() != null) {
+ Tree parent = path.getParentPath().getLeaf();
+ if (parent.getKind() == Tree.Kind.LABELED_STATEMENT) {
+ return ((LabeledStatementTree) parent).getLabel();
+ }
+ }
+ return null;
+ }
+
+ /* --------------------------------------------------------- */
+ /* Visitor Methods */
+ /* --------------------------------------------------------- */
+
+ @Override
+ public Node visitAnnotatedType(AnnotatedTypeTree tree, Void p) {
+ return scan(tree.getUnderlyingType(), p);
+ }
+
+ @Override
+ public Node visitAnnotation(AnnotationTree tree, Void p) {
+ assert false : "AnnotationTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public MethodInvocationNode visitMethodInvocation(MethodInvocationTree tree, Void p) {
+
+ // see JLS 15.12.4
+
+ // First, compute the receiver, if any (15.12.4.1)
+ // Second, evaluate the actual arguments, left to right and
+ // possibly some arguments are stored into an array for variable
+ // arguments calls (15.12.4.2)
+ // Third, test the receiver, if any, for nullness (15.12.4.4)
+ // Fourth, convert the arguments to the type of the formal
+ // parameters (15.12.4.5)
+ // Fifth, if the method is synchronized, lock the receiving
+ // object or class (15.12.4.5)
+ ExecutableElement method = TreeUtils.elementFromUse(tree);
+ // TODO? Variable wasn't used.
+ // boolean isBooleanMethod = TypesUtils.isBooleanType(method.getReturnType());
+
+ ExpressionTree methodSelect = tree.getMethodSelect();
+ assert TreeUtils.isMethodAccess(methodSelect) : "Expected a method access, but got: " + methodSelect;
+
+ List<? extends ExpressionTree> actualExprs = tree.getArguments();
+
+ // Look up method to invoke and possibly throw NullPointerException
+ Node receiver = getReceiver(methodSelect,
+ TreeUtils.enclosingClass(getCurrentPath()));
+
+ MethodAccessNode target = new MethodAccessNode(methodSelect,
+ receiver);
+
+ ExecutableElement element = TreeUtils.elementFromUse(tree);
+ if (ElementUtils.isStatic(element) ||
+ receiver instanceof ThisLiteralNode) {
+ // No NullPointerException can be thrown, use normal node
+ extendWithNode(target);
+ } else {
+ TypeElement npeElement = elements
+ .getTypeElement("java.lang.NullPointerException");
+ extendWithNodeWithException(target, npeElement.asType());
+ }
+
+ List<Node> arguments = new ArrayList<>();
+
+ // Don't convert arguments for enum super calls. The AST contains
+ // no actual arguments, while the method element expects two arguments,
+ // leading to an exception in convertCallArguments. Since no actual
+ // arguments are present in the AST that is being checked, it shouldn't
+ // cause any harm to omit the conversions.
+ // See also BaseTypeVisitor.visitMethodInvocation and
+ // QualifierPolymorphism.annotate
+ if (!TreeUtils.isEnumSuper(tree)) {
+ arguments = convertCallArguments(method, actualExprs);
+ }
+
+ // TODO: lock the receiver for synchronized methods
+
+ MethodInvocationNode node = new MethodInvocationNode(tree, target, arguments, getCurrentPath());
+
+ Set<TypeMirror> thrownSet = new HashSet<>();
+ // Add exceptions explicitly mentioned in the throws clause.
+ List<? extends TypeMirror> thrownTypes = element.getThrownTypes();
+ thrownSet.addAll(thrownTypes);
+ // Add Throwable to account for unchecked exceptions
+ TypeElement throwableElement = elements
+ .getTypeElement("java.lang.Throwable");
+ thrownSet.add(throwableElement.asType());
+
+ ExtendedNode extendedNode = extendWithNodeWithExceptions(node, thrownSet);
+
+ /* Check for the TerminatesExecution annotation. */
+ Element methodElement = InternalUtils.symbol(tree);
+ boolean terminatesExecution = annotationProvider.getDeclAnnotation(
+ methodElement, TerminatesExecution.class) != null;
+ if (terminatesExecution) {
+ extendedNode.setTerminatesExecution(true);
+ }
+
+ return node;
+ }
+
+ @Override
+ public Node visitAssert(AssertTree tree, Void p) {
+
+ // see JLS 14.10
+
+ // If assertions are enabled, then we can just translate the
+ // assertion.
+ if (assumeAssertionsEnabled || assumeAssertionsEnabledFor(tree)) {
+ translateAssertWithAssertionsEnabled(tree);
+ return null;
+ }
+
+ // If assertions are disabled, then nothing is executed.
+ if (assumeAssertionsDisabled) {
+ return null;
+ }
+
+
+ // Otherwise, we don't know if assertions are enabled, so we use a
+ // variable "ea" and case-split on it. One branch does execute the
+ // assertion, while the other assumes assertions are disabled.
+ VariableTree ea = getAssertionsEnabledVariable();
+
+ // all necessary labels
+ Label assertionEnabled = new Label();
+ Label assertionDisabled = new Label();
+
+ extendWithNode(new LocalVariableNode(ea));
+ extendWithExtendedNode(new ConditionalJump(assertionEnabled,
+ assertionDisabled));
+
+ // 'then' branch (i.e. check the assertion)
+ addLabelForNextNode(assertionEnabled);
+
+ translateAssertWithAssertionsEnabled(tree);
+
+ // 'else' branch
+ addLabelForNextNode(assertionDisabled);
+
+ return null;
+ }
+
+ /**
+ * Should assertions be assumed to be executed for a given
+ * {@link AssertTree}? False by default.
+ */
+ protected boolean assumeAssertionsEnabledFor(AssertTree tree) {
+ return false;
+ }
+
+ /**
+ * The {@link VariableTree} that indicates whether assertions are
+ * enabled or not.
+ */
+ protected VariableTree ea = null;
+
+ /**
+ * Get a synthetic {@link VariableTree} that indicates whether assertions are
+ * enabled or not.
+ */
+ protected VariableTree getAssertionsEnabledVariable() {
+ if (ea == null) {
+ String name = uniqueName("assertionsEnabled");
+ MethodTree enclosingMethod = TreeUtils
+ .enclosingMethod(getCurrentPath());
+ Element owner;
+ if (enclosingMethod != null) {
+ owner = TreeUtils.elementFromDeclaration(enclosingMethod);
+ } else {
+ ClassTree enclosingClass = TreeUtils.enclosingClass(getCurrentPath());
+ owner = TreeUtils.elementFromDeclaration(enclosingClass);
+ }
+ ExpressionTree initializer = null;
+ ea = treeBuilder.buildVariableDecl(
+ types.getPrimitiveType(TypeKind.BOOLEAN), name, owner,
+ initializer);
+ }
+ return ea;
+ }
+
+ /**
+ * Translates an assertion statement to the correct CFG nodes. The
+ * translation assumes that assertions are enabled.
+ */
+ protected void translateAssertWithAssertionsEnabled(AssertTree tree) {
+
+ // all necessary labels
+ Label assertEnd = new Label();
+ Label elseEntry = new Label();
+
+ // basic block for the condition
+ Node condition = unbox(scan(tree.getCondition(), null));
+ ConditionalJump cjump = new ConditionalJump(assertEnd, elseEntry);
+ extendWithExtendedNode(cjump);
+
+ // else branch
+ Node detail = null;
+ addLabelForNextNode(elseEntry);
+ if (tree.getDetail() != null) {
+ detail = scan(tree.getDetail(), null);
+ }
+ TypeElement assertException = elements
+ .getTypeElement("java.lang.AssertionError");
+ AssertionErrorNode assertNode = new AssertionErrorNode(tree,
+ condition, detail, assertException.asType());
+ extendWithNode(assertNode);
+ NodeWithExceptionsHolder exNode = extendWithNodeWithException(
+ new ThrowNode(null, assertNode, env.getTypeUtils()), assertException.asType());
+ exNode.setTerminatesExecution(true);
+
+ // then branch (nothing happens)
+ addLabelForNextNode(assertEnd);
+ }
+
+ @Override
+ public Node visitAssignment(AssignmentTree tree, Void p) {
+
+ // see JLS 15.26.1
+
+ AssignmentNode assignmentNode;
+ ExpressionTree variable = tree.getVariable();
+ TypeMirror varType = InternalUtils.typeOf(variable);
+
+ // case 1: field access
+ if (TreeUtils.isFieldAccess(variable)) {
+ // visit receiver
+ Node receiver = getReceiver(variable,
+ TreeUtils.enclosingClass(getCurrentPath()));
+
+ // visit expression
+ Node expression = scan(tree.getExpression(), p);
+ expression = assignConvert(expression, varType);
+
+ // visit field access (throws null-pointer exception)
+ FieldAccessNode target = new FieldAccessNode(variable, receiver);
+ target.setLValue();
+
+ Element element = TreeUtils.elementFromUse(variable);
+ if (ElementUtils.isStatic(element) ||
+ receiver instanceof ThisLiteralNode) {
+ // No NullPointerException can be thrown, use normal node
+ extendWithNode(target);
+ } else {
+ TypeElement npeElement = elements
+ .getTypeElement("java.lang.NullPointerException");
+ extendWithNodeWithException(target, npeElement.asType());
+ }
+
+ // add assignment node
+ assignmentNode = new AssignmentNode(tree,
+ target, expression);
+ extendWithNode(assignmentNode);
+ }
+
+ // case 2: other cases
+ else {
+ Node target = scan(variable, p);
+ target.setLValue();
+
+ assignmentNode = translateAssignment(tree, target,
+ tree.getExpression());
+ }
+
+ return assignmentNode;
+ }
+
+ /**
+ * Translate an assignment.
+ */
+ protected AssignmentNode translateAssignment(Tree tree, Node target,
+ ExpressionTree rhs) {
+ Node expression = scan(rhs, null);
+ return translateAssignment(tree, target, expression);
+ }
+
+ /**
+ * Translate an assignment where the RHS has already been scanned.
+ */
+ protected AssignmentNode translateAssignment(Tree tree, Node target,
+ Node expression) {
+ assert tree instanceof AssignmentTree
+ || tree instanceof VariableTree;
+ target.setLValue();
+ expression = assignConvert(expression, target.getType());
+ AssignmentNode assignmentNode = new AssignmentNode(tree, target,
+ expression);
+ extendWithNode(assignmentNode);
+ return assignmentNode;
+ }
+
+ /**
+ * Note 1: Requires {@code tree} to be a field or method access
+ * tree.
+ * <p>
+ * Note 2: Visits the receiver and adds all necessary blocks to the CFG.
+ *
+ * @param tree
+ * the field access tree containing the receiver
+ * @param classTree
+ * the ClassTree enclosing the field access
+ * @return the receiver of the field access
+ */
+ private Node getReceiver(Tree tree, ClassTree classTree) {
+ assert TreeUtils.isFieldAccess(tree)
+ || TreeUtils.isMethodAccess(tree);
+ if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
+ MemberSelectTree mtree = (MemberSelectTree) tree;
+ return scan(mtree.getExpression(), null);
+ } else {
+ TypeMirror classType = InternalUtils.typeOf(classTree);
+ Node node = new ImplicitThisLiteralNode(classType);
+ extendWithNode(node);
+ return node;
+ }
+ }
+
+ /**
+ * Map an operation with assignment to the corresponding operation
+ * without assignment.
+ *
+ * @param kind a Tree.Kind representing an operation with assignment
+ * @return the Tree.Kind for the same operation without assignment
+ */
+ protected Tree.Kind withoutAssignment(Tree.Kind kind) {
+ switch (kind) {
+ case DIVIDE_ASSIGNMENT:
+ return Tree.Kind.DIVIDE;
+ case MULTIPLY_ASSIGNMENT:
+ return Tree.Kind.MULTIPLY;
+ case REMAINDER_ASSIGNMENT:
+ return Tree.Kind.REMAINDER;
+ case MINUS_ASSIGNMENT:
+ return Tree.Kind.MINUS;
+ case PLUS_ASSIGNMENT:
+ return Tree.Kind.PLUS;
+ case LEFT_SHIFT_ASSIGNMENT:
+ return Tree.Kind.LEFT_SHIFT;
+ case RIGHT_SHIFT_ASSIGNMENT:
+ return Tree.Kind.RIGHT_SHIFT;
+ case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
+ return Tree.Kind.UNSIGNED_RIGHT_SHIFT;
+ case AND_ASSIGNMENT:
+ return Tree.Kind.AND;
+ case OR_ASSIGNMENT:
+ return Tree.Kind.OR;
+ case XOR_ASSIGNMENT:
+ return Tree.Kind.XOR;
+ default:
+ return Tree.Kind.ERRONEOUS;
+ }
+ }
+
+
+ @Override
+ public Node visitCompoundAssignment(CompoundAssignmentTree tree, Void p) {
+ // According the JLS 15.26.2, E1 op= E2 is equivalent to
+ // E1 = (T) ((E1) op (E2)), where T is the type of E1,
+ // except that E1 is evaluated only once.
+ //
+
+ Tree.Kind kind = tree.getKind();
+ switch (kind) {
+ case DIVIDE_ASSIGNMENT:
+ case MULTIPLY_ASSIGNMENT:
+ case REMAINDER_ASSIGNMENT: {
+ // see JLS 15.17 and 15.26.2
+ Node targetLHS = scan(tree.getVariable(), p);
+ Node value = scan(tree.getExpression(), p);
+
+ TypeMirror exprType = InternalUtils.typeOf(tree);
+ TypeMirror leftType = InternalUtils.typeOf(tree.getVariable());
+ TypeMirror rightType = InternalUtils.typeOf(tree.getExpression());
+ TypeMirror promotedType = binaryPromotedType(leftType, rightType);
+ Node targetRHS = binaryNumericPromotion(targetLHS, promotedType);
+ value = binaryNumericPromotion(value, promotedType);
+
+ BinaryTree operTree = treeBuilder.buildBinary(promotedType, withoutAssignment(kind),
+ tree.getVariable(), tree.getExpression());
+ handleArtificialTree(operTree);
+ Node operNode;
+ if (kind == Tree.Kind.MULTIPLY_ASSIGNMENT) {
+ operNode = new NumericalMultiplicationNode(operTree, targetRHS, value);
+ } else if (kind == Tree.Kind.DIVIDE_ASSIGNMENT) {
+ if (TypesUtils.isIntegral(exprType)) {
+ operNode = new IntegerDivisionNode(operTree, targetRHS, value);
+ } else {
+ operNode = new FloatingDivisionNode(operTree, targetRHS, value);
+ }
+ } else {
+ assert kind == Kind.REMAINDER_ASSIGNMENT;
+ if (TypesUtils.isIntegral(exprType)) {
+ operNode = new IntegerRemainderNode(operTree, targetRHS, value);
+ } else {
+ operNode = new FloatingRemainderNode(operTree, targetRHS, value);
+ }
+ }
+ extendWithNode(operNode);
+
+ TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree);
+ handleArtificialTree(castTree);
+ TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType);
+ castNode.setInSource(false);
+ extendWithNode(castNode);
+
+ AssignmentNode assignNode = new AssignmentNode(tree, targetLHS, castNode);
+ extendWithNode(assignNode);
+ return assignNode;
+ }
+
+ case MINUS_ASSIGNMENT:
+ case PLUS_ASSIGNMENT: {
+ // see JLS 15.18 and 15.26.2
+
+ Node targetLHS = scan(tree.getVariable(), p);
+ Node value = scan(tree.getExpression(), p);
+
+ TypeMirror leftType = InternalUtils.typeOf(tree.getVariable());
+ TypeMirror rightType = InternalUtils.typeOf(tree.getExpression());
+
+ if (TypesUtils.isString(leftType) || TypesUtils.isString(rightType)) {
+ assert (kind == Tree.Kind.PLUS_ASSIGNMENT);
+ Node targetRHS = stringConversion(targetLHS);
+ value = stringConversion(value);
+ Node r = new StringConcatenateAssignmentNode(tree, targetRHS, value);
+ extendWithNode(r);
+ return r;
+ } else {
+ TypeMirror promotedType = binaryPromotedType(leftType, rightType);
+ Node targetRHS = binaryNumericPromotion(targetLHS, promotedType);
+ value = binaryNumericPromotion(value, promotedType);
+
+ BinaryTree operTree = treeBuilder.buildBinary(promotedType, withoutAssignment(kind),
+ tree.getVariable(), tree.getExpression());
+ handleArtificialTree(operTree);
+ Node operNode;
+ if (kind == Tree.Kind.PLUS_ASSIGNMENT) {
+ operNode = new NumericalAdditionNode(operTree, targetRHS, value);
+ } else {
+ assert kind == Kind.MINUS_ASSIGNMENT;
+ operNode = new NumericalSubtractionNode(operTree, targetRHS, value);
+ }
+ extendWithNode(operNode);
+
+ TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree);
+ handleArtificialTree(castTree);
+ TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType);
+ castNode.setInSource(false);
+ extendWithNode(castNode);
+
+ // Map the compound assignment tree to an assignment node, which
+ // will have the correct type.
+ AssignmentNode assignNode = new AssignmentNode(tree, targetLHS, castNode);
+ extendWithNode(assignNode);
+ return assignNode;
+ }
+ }
+
+ case LEFT_SHIFT_ASSIGNMENT:
+ case RIGHT_SHIFT_ASSIGNMENT:
+ case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: {
+ // see JLS 15.19 and 15.26.2
+ Node targetLHS = scan(tree.getVariable(), p);
+ Node value = scan(tree.getExpression(), p);
+
+ TypeMirror leftType = InternalUtils.typeOf(tree.getVariable());
+
+ Node targetRHS = unaryNumericPromotion(targetLHS);
+ value = unaryNumericPromotion(value);
+
+ BinaryTree operTree = treeBuilder.buildBinary(leftType, withoutAssignment(kind),
+ tree.getVariable(), tree.getExpression());
+ handleArtificialTree(operTree);
+ Node operNode;
+ if (kind == Tree.Kind.LEFT_SHIFT_ASSIGNMENT) {
+ operNode = new LeftShiftNode(operTree, targetRHS, value);
+ } else if (kind == Tree.Kind.RIGHT_SHIFT_ASSIGNMENT) {
+ operNode = new SignedRightShiftNode(operTree, targetRHS, value);
+ } else {
+ assert kind == Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT;
+ operNode = new UnsignedRightShiftNode(operTree, targetRHS, value);
+ }
+ extendWithNode(operNode);
+
+ TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree);
+ handleArtificialTree(castTree);
+ TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType);
+ castNode.setInSource(false);
+ extendWithNode(castNode);
+
+ AssignmentNode assignNode = new AssignmentNode(tree, targetLHS, castNode);
+ extendWithNode(assignNode);
+ return assignNode;
+ }
+
+ case AND_ASSIGNMENT:
+ case OR_ASSIGNMENT:
+ case XOR_ASSIGNMENT:
+ // see JLS 15.22
+ Node targetLHS = scan(tree.getVariable(), p);
+ Node value = scan(tree.getExpression(), p);
+
+ TypeMirror leftType = InternalUtils.typeOf(tree.getVariable());
+ TypeMirror rightType = InternalUtils.typeOf(tree.getExpression());
+
+ Node targetRHS = null;
+ if (isNumericOrBoxed(leftType) && isNumericOrBoxed(rightType)) {
+ TypeMirror promotedType = binaryPromotedType(leftType, rightType);
+ targetRHS = binaryNumericPromotion(targetLHS, promotedType);
+ value = binaryNumericPromotion(value, promotedType);
+ } else if (TypesUtils.isBooleanType(leftType) &&
+ TypesUtils.isBooleanType(rightType)) {
+ targetRHS = unbox(targetLHS);
+ value = unbox(value);
+ } else {
+ assert false :
+ "Both argument to logical operation must be numeric or boolean";
+ }
+
+ BinaryTree operTree = treeBuilder.buildBinary(leftType, withoutAssignment(kind),
+ tree.getVariable(), tree.getExpression());
+ handleArtificialTree(operTree);
+ Node operNode;
+ if (kind == Tree.Kind.AND_ASSIGNMENT) {
+ operNode = new BitwiseAndNode(operTree, targetRHS, value);
+ } else if (kind == Tree.Kind.OR_ASSIGNMENT) {
+ operNode = new BitwiseOrNode(operTree, targetRHS, value);
+ } else {
+ assert kind == Kind.XOR_ASSIGNMENT;
+ operNode = new BitwiseXorNode(operTree, targetRHS, value);
+ }
+ extendWithNode(operNode);
+
+ TypeCastTree castTree = treeBuilder.buildTypeCast(leftType, operTree);
+ handleArtificialTree(castTree);
+ TypeCastNode castNode = new TypeCastNode(castTree, operNode, leftType);
+ castNode.setInSource(false);
+ extendWithNode(castNode);
+
+ AssignmentNode assignNode = new AssignmentNode(tree, targetLHS, castNode);
+ extendWithNode(assignNode);
+ return assignNode;
+ default:
+ assert false : "unexpected compound assignment type";
+ break;
+ }
+ assert false : "unexpected compound assignment type";
+ return null;
+ }
+
+ @Override
+ public Node visitBinary(BinaryTree tree, Void p) {
+ // Note that for binary operations it is important to perform any required
+ // promotion on the left operand before generating any Nodes for the right
+ // operand, because labels must be inserted AFTER ALL preceding Nodes and
+ // BEFORE ALL following Nodes.
+ Node r = null;
+ Tree leftTree = tree.getLeftOperand();
+ Tree rightTree = tree.getRightOperand();
+
+ Tree.Kind kind = tree.getKind();
+ switch (kind) {
+ case DIVIDE:
+ case MULTIPLY:
+ case REMAINDER: {
+ // see JLS 15.17
+
+ TypeMirror exprType = InternalUtils.typeOf(tree);
+ TypeMirror leftType = InternalUtils.typeOf(leftTree);
+ TypeMirror rightType = InternalUtils.typeOf(rightTree);
+ TypeMirror promotedType = binaryPromotedType(leftType, rightType);
+
+ Node left = binaryNumericPromotion(scan(leftTree, p), promotedType);
+ Node right = binaryNumericPromotion(scan(rightTree, p), promotedType);
+
+ if (kind == Tree.Kind.MULTIPLY) {
+ r = new NumericalMultiplicationNode(tree, left, right);
+ } else if (kind == Tree.Kind.DIVIDE) {
+ if (TypesUtils.isIntegral(exprType)) {
+ r = new IntegerDivisionNode(tree, left, right);
+ } else {
+ r = new FloatingDivisionNode(tree, left, right);
+ }
+ } else {
+ assert kind == Kind.REMAINDER;
+ if (TypesUtils.isIntegral(exprType)) {
+ r = new IntegerRemainderNode(tree, left, right);
+ } else {
+ r = new FloatingRemainderNode(tree, left, right);
+ }
+ }
+ break;
+ }
+
+ case MINUS:
+ case PLUS: {
+ // see JLS 15.18
+
+ // TypeMirror exprType = InternalUtils.typeOf(tree);
+ TypeMirror leftType = InternalUtils.typeOf(leftTree);
+ TypeMirror rightType = InternalUtils.typeOf(rightTree);
+
+ if (TypesUtils.isString(leftType) || TypesUtils.isString(rightType)) {
+ assert (kind == Tree.Kind.PLUS);
+ Node left = stringConversion(scan(leftTree, p));
+ Node right = stringConversion(scan(rightTree, p));
+ r = new StringConcatenateNode(tree, left, right);
+ } else {
+ TypeMirror promotedType = binaryPromotedType(leftType, rightType);
+ Node left = binaryNumericPromotion(scan(leftTree, p), promotedType);
+ Node right = binaryNumericPromotion(scan(rightTree, p), promotedType);
+
+ // TODO: Decide whether to deal with floating-point value
+ // set conversion.
+ if (kind == Tree.Kind.PLUS) {
+ r = new NumericalAdditionNode(tree, left, right);
+ } else {
+ assert kind == Kind.MINUS;
+ r = new NumericalSubtractionNode(tree, left, right);
+ }
+ }
+ break;
+ }
+
+ case LEFT_SHIFT:
+ case RIGHT_SHIFT:
+ case UNSIGNED_RIGHT_SHIFT: {
+ // see JLS 15.19
+
+ Node left = unaryNumericPromotion(scan(leftTree, p));
+ Node right = unaryNumericPromotion(scan(rightTree, p));
+
+ if (kind == Tree.Kind.LEFT_SHIFT) {
+ r = new LeftShiftNode(tree, left, right);
+ } else if (kind == Tree.Kind.RIGHT_SHIFT) {
+ r = new SignedRightShiftNode(tree, left, right);
+ } else {
+ assert kind == Kind.UNSIGNED_RIGHT_SHIFT;
+ r = new UnsignedRightShiftNode(tree, left, right);
+ }
+ break;
+ }
+
+ case GREATER_THAN:
+ case GREATER_THAN_EQUAL:
+ case LESS_THAN:
+ case LESS_THAN_EQUAL: {
+ // see JLS 15.20.1
+ TypeMirror leftType = InternalUtils.typeOf(leftTree);
+ if (TypesUtils.isBoxedPrimitive(leftType)) {
+ leftType = types.unboxedType(leftType);
+ }
+
+ TypeMirror rightType = InternalUtils.typeOf(rightTree);
+ if (TypesUtils.isBoxedPrimitive(rightType)) {
+ rightType = types.unboxedType(rightType);
+ }
+
+ TypeMirror promotedType = binaryPromotedType(leftType, rightType);
+ Node left = binaryNumericPromotion(scan(leftTree, p), promotedType);
+ Node right = binaryNumericPromotion(scan(rightTree, p), promotedType);
+
+ Node node;
+ if (kind == Tree.Kind.GREATER_THAN) {
+ node = new GreaterThanNode(tree, left, right);
+ } else if (kind == Tree.Kind.GREATER_THAN_EQUAL) {
+ node = new GreaterThanOrEqualNode(tree, left, right);
+ } else if (kind == Tree.Kind.LESS_THAN) {
+ node = new LessThanNode(tree, left, right);
+ } else {
+ assert kind == Tree.Kind.LESS_THAN_EQUAL;
+ node = new LessThanOrEqualNode(tree, left, right);
+ }
+
+ extendWithNode(node);
+
+ return node;
+ }
+
+ case EQUAL_TO:
+ case NOT_EQUAL_TO: {
+ // see JLS 15.21
+ TreeInfo leftInfo = getTreeInfo(leftTree);
+ TreeInfo rightInfo = getTreeInfo(rightTree);
+ Node left = scan(leftTree, p);
+ Node right = scan(rightTree, p);
+
+ if (leftInfo.isNumeric() && rightInfo.isNumeric() &&
+ !(leftInfo.isBoxed() && rightInfo.isBoxed())) {
+ // JLS 15.21.1 numerical equality
+ TypeMirror promotedType = binaryPromotedType(leftInfo.unboxedType(),
+ rightInfo.unboxedType());
+ left = binaryNumericPromotion(left, promotedType);
+ right = binaryNumericPromotion(right, promotedType);
+ } else if (leftInfo.isBoolean() && rightInfo.isBoolean() &&
+ !(leftInfo.isBoxed() && rightInfo.isBoxed())) {
+ // JSL 15.21.2 boolean equality
+ left = unboxAsNeeded(left, leftInfo.isBoxed());
+ right = unboxAsNeeded(right, rightInfo.isBoxed());
+ }
+
+ Node node;
+ if (kind == Tree.Kind.EQUAL_TO) {
+ node = new EqualToNode(tree, left, right);
+ } else {
+ assert kind == Kind.NOT_EQUAL_TO;
+ node = new NotEqualNode(tree, left, right);
+ }
+ extendWithNode(node);
+
+ return node;
+ }
+
+ case AND:
+ case OR:
+ case XOR: {
+ // see JLS 15.22
+ TypeMirror leftType = InternalUtils.typeOf(leftTree);
+ TypeMirror rightType = InternalUtils.typeOf(rightTree);
+ boolean isBooleanOp = TypesUtils.isBooleanType(leftType) &&
+ TypesUtils.isBooleanType(rightType);
+
+ Node left;
+ Node right;
+
+ if (isBooleanOp) {
+ left = unbox(scan(leftTree, p));
+ right = unbox(scan(rightTree, p));
+ } else if (isNumericOrBoxed(leftType) && isNumericOrBoxed(rightType)) {
+ TypeMirror promotedType = binaryPromotedType(leftType, rightType);
+ left = binaryNumericPromotion(scan(leftTree, p), promotedType);
+ right = binaryNumericPromotion(scan(rightTree, p), promotedType);
+ } else {
+ left = unbox(scan(leftTree, p));
+ right = unbox(scan(rightTree, p));
+ }
+
+ Node node;
+ if (kind == Tree.Kind.AND) {
+ node = new BitwiseAndNode(tree, left, right);
+ } else if (kind == Tree.Kind.OR) {
+ node = new BitwiseOrNode(tree, left, right);
+ } else {
+ assert kind == Kind.XOR;
+ node = new BitwiseXorNode(tree, left, right);
+ }
+
+ extendWithNode(node);
+
+ return node;
+ }
+
+ case CONDITIONAL_AND:
+ case CONDITIONAL_OR: {
+ // see JLS 15.23 and 15.24
+
+ // all necessary labels
+ Label rightStartL = new Label();
+ Label shortCircuitL = new Label();
+
+ // left-hand side
+ Node left = scan(leftTree, p);
+
+ ConditionalJump cjump;
+ if (kind == Tree.Kind.CONDITIONAL_AND) {
+ cjump = new ConditionalJump(rightStartL, shortCircuitL);
+ cjump.setFalseFlowRule(Store.FlowRule.ELSE_TO_ELSE);
+ } else {
+ cjump = new ConditionalJump(shortCircuitL, rightStartL);
+ cjump.setTrueFlowRule(Store.FlowRule.THEN_TO_THEN);
+ }
+ extendWithExtendedNode(cjump);
+
+ // right-hand side
+ addLabelForNextNode(rightStartL);
+ Node right = scan(rightTree, p);
+
+ // conditional expression itself
+ addLabelForNextNode(shortCircuitL);
+ Node node;
+ if (kind == Tree.Kind.CONDITIONAL_AND) {
+ node = new ConditionalAndNode(tree, left, right);
+ } else {
+ node = new ConditionalOrNode(tree, left, right);
+ }
+ extendWithNode(node);
+ return node;
+ }
+ default:
+ assert false : "unexpected binary tree: " + kind;
+ break;
+ }
+ assert r != null : "unexpected binary tree";
+ return extendWithNode(r);
+ }
+
+ @Override
+ public Node visitBlock(BlockTree tree, Void p) {
+ for (StatementTree n : tree.getStatements()) {
+ scan(n, null);
+ }
+ return null;
+ }
+
+ @Override
+ public Node visitBreak(BreakTree tree, Void p) {
+ Name label = tree.getLabel();
+ if (label == null) {
+ assert breakTargetL != null : "no target for break statement";
+
+ extendWithExtendedNode(new UnconditionalJump(breakTargetL));
+ } else {
+ assert breakLabels.containsKey(label);
+
+ extendWithExtendedNode(new UnconditionalJump(
+ breakLabels.get(label)));
+ }
+
+ return null;
+ }
+
+ @Override
+ public Node visitSwitch(SwitchTree tree, Void p) {
+ SwitchBuilder builder = new SwitchBuilder(tree, p);
+ builder.build();
+ return null;
+ }
+
+ private class SwitchBuilder {
+ final private SwitchTree switchTree;
+ final private Label[] caseBodyLabels;
+ final private Void p;
+ private Node switchExpr;
+
+ private SwitchBuilder(SwitchTree tree, Void p) {
+ this.switchTree = tree;
+ this.caseBodyLabels = new Label[switchTree.getCases().size() + 1];
+ this.p = p;
+ }
+
+ public void build() {
+ Label oldBreakTargetL = breakTargetL;
+ breakTargetL = new Label();
+ int cases = caseBodyLabels.length-1;
+ for (int i = 0; i < cases; ++i) {
+ caseBodyLabels[i] = new Label();
+ }
+ caseBodyLabels[cases] = breakTargetL;
+
+ switchExpr = unbox(scan(switchTree.getExpression(), p));
+ extendWithNode(new MarkerNode(switchTree, "start of switch statement", env.getTypeUtils()));
+
+ Integer defaultIndex = null;
+ for (int i = 0; i < cases; ++i) {
+ CaseTree caseTree = switchTree.getCases().get(i);
+ if (caseTree.getExpression() == null) {
+ defaultIndex = i;
+ } else {
+ buildCase(caseTree, i);
+ }
+ }
+ if (defaultIndex != null) {
+ // the checks of all cases must happen before the default case,
+ // therefore we build the default case last.
+ // fallthrough is still handled correctly with the caseBodyLabels.
+ buildCase(switchTree.getCases().get(defaultIndex), defaultIndex);
+ }
+
+ addLabelForNextNode(breakTargetL);
+ breakTargetL = oldBreakTargetL;
+ }
+
+ private void buildCase(CaseTree tree, int index) {
+ final Label thisBodyL = caseBodyLabels[index];
+ final Label nextBodyL = caseBodyLabels[index+1];
+ final Label nextCaseL = new Label();
+
+ ExpressionTree exprTree = tree.getExpression();
+ if (exprTree != null) {
+ Node expr = scan(exprTree, p);
+ CaseNode test = new CaseNode(tree, switchExpr, expr, env.getTypeUtils());
+ extendWithNode(test);
+ extendWithExtendedNode(new ConditionalJump(thisBodyL, nextCaseL));
+ }
+ addLabelForNextNode(thisBodyL);
+ for (StatementTree stmt : tree.getStatements()) {
+ scan(stmt, p);
+ }
+ extendWithExtendedNode(new UnconditionalJump(nextBodyL));
+ addLabelForNextNode(nextCaseL);
+ }
+ }
+
+ @Override
+ public Node visitCase(CaseTree tree, Void p) {
+ throw new AssertionError("case visitor is implemented in SwitchBuilder");
+ }
+
+ @Override
+ public Node visitCatch(CatchTree tree, Void p) {
+ scan(tree.getParameter(), p);
+ scan(tree.getBlock(), p);
+ return null;
+ }
+
+ @Override
+ public Node visitClass(ClassTree tree, Void p) {
+ declaredClasses.add(tree);
+ return null;
+ }
+
+ @Override
+ public Node visitConditionalExpression(ConditionalExpressionTree tree,
+ Void p) {
+ // see JLS 15.25
+ TypeMirror exprType = InternalUtils.typeOf(tree);
+
+ Label trueStart = new Label();
+ Label falseStart = new Label();
+ Label merge = new Label();
+
+ Node condition = unbox(scan(tree.getCondition(), p));
+ ConditionalJump cjump = new ConditionalJump(trueStart, falseStart);
+ extendWithExtendedNode(cjump);
+
+ addLabelForNextNode(trueStart);
+ Node trueExpr = scan(tree.getTrueExpression(), p);
+ trueExpr = conditionalExprPromotion(trueExpr, exprType);
+ extendWithExtendedNode(new UnconditionalJump(merge));
+
+ addLabelForNextNode(falseStart);
+ Node falseExpr = scan(tree.getFalseExpression(), p);
+ falseExpr = conditionalExprPromotion(falseExpr, exprType);
+
+ addLabelForNextNode(merge);
+ Node node = new TernaryExpressionNode(tree, condition, trueExpr, falseExpr);
+ extendWithNode(node);
+
+ return node;
+ }
+
+ @Override
+ public Node visitContinue(ContinueTree tree, Void p) {
+ Name label = tree.getLabel();
+ if (label == null) {
+ assert continueTargetL != null : "no target for continue statement";
+
+ extendWithExtendedNode(new UnconditionalJump(continueTargetL));
+ } else {
+ assert continueLabels.containsKey(label);
+
+ extendWithExtendedNode(new UnconditionalJump(
+ continueLabels.get(label)));
+ }
+
+ return null;
+ }
+
+ @Override
+ public Node visitDoWhileLoop(DoWhileLoopTree tree, Void p) {
+ Name parentLabel = getLabel(getCurrentPath());
+
+ Label loopEntry = new Label();
+ Label loopExit = new Label();
+
+ // If the loop is a labeled statement, then its continue
+ // target is identical for continues with no label and
+ // continues with the loop's label.
+ Label conditionStart;
+ if (parentLabel != null) {
+ conditionStart = continueLabels.get(parentLabel);
+ } else {
+ conditionStart = new Label();
+ }
+
+ Label oldBreakTargetL = breakTargetL;
+ breakTargetL = loopExit;
+
+ Label oldContinueTargetL = continueTargetL;
+ continueTargetL = conditionStart;
+
+ // Loop body
+ addLabelForNextNode(loopEntry);
+ if (tree.getStatement() != null) {
+ scan(tree.getStatement(), p);
+ }
+
+ // Condition
+ addLabelForNextNode(conditionStart);
+ if (tree.getCondition() != null) {
+ unbox(scan(tree.getCondition(), p));
+ ConditionalJump cjump = new ConditionalJump(loopEntry, loopExit);
+ extendWithExtendedNode(cjump);
+ }
+
+ // Loop exit
+ addLabelForNextNode(loopExit);
+
+ breakTargetL = oldBreakTargetL;
+ continueTargetL = oldContinueTargetL;
+
+ return null;
+ }
+
+ @Override
+ public Node visitErroneous(ErroneousTree tree, Void p) {
+ assert false : "ErroneousTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public Node visitExpressionStatement(ExpressionStatementTree tree,
+ Void p) {
+ return scan(tree.getExpression(), p);
+ }
+
+ @Override
+ public Node visitEnhancedForLoop(EnhancedForLoopTree tree, Void p) {
+ // see JLS 14.14.2
+ Name parentLabel = getLabel(getCurrentPath());
+
+ Label conditionStart = new Label();
+ Label loopEntry = new Label();
+ Label loopExit = new Label();
+
+ // If the loop is a labeled statement, then its continue
+ // target is identical for continues with no label and
+ // continues with the loop's label.
+ Label updateStart;
+ if (parentLabel != null) {
+ updateStart = continueLabels.get(parentLabel);
+ } else {
+ updateStart = new Label();
+ }
+
+ Label oldBreakTargetL = breakTargetL;
+ breakTargetL = loopExit;
+
+ Label oldContinueTargetL = continueTargetL;
+ continueTargetL = updateStart;
+
+ // Distinguish loops over Iterables from loops over arrays.
+
+ TypeElement iterableElement = elements.getTypeElement("java.lang.Iterable");
+ TypeMirror iterableType = types.erasure(iterableElement.asType());
+
+ VariableTree variable = tree.getVariable();
+ VariableElement variableElement =
+ TreeUtils.elementFromDeclaration(variable);
+ ExpressionTree expression = tree.getExpression();
+ StatementTree statement = tree.getStatement();
+
+ TypeMirror exprType = InternalUtils.typeOf(expression);
+
+ if (types.isSubtype(exprType, iterableType)) {
+ // Take the upper bound of a type variable or wildcard
+ exprType = TypesUtils.upperBound(exprType);
+
+ assert (exprType instanceof DeclaredType) : "an Iterable must be a DeclaredType";
+ DeclaredType declaredExprType = (DeclaredType) exprType;
+ declaredExprType.getTypeArguments();
+
+ MemberSelectTree iteratorSelect =
+ treeBuilder.buildIteratorMethodAccess(expression);
+ handleArtificialTree(iteratorSelect);
+
+ MethodInvocationTree iteratorCall =
+ treeBuilder.buildMethodInvocation(iteratorSelect);
+ handleArtificialTree(iteratorCall);
+
+ VariableTree iteratorVariable = createEnhancedForLoopIteratorVariable(iteratorCall, variableElement);
+ handleArtificialTree(iteratorVariable);
+
+ VariableDeclarationNode iteratorVariableDecl =
+ new VariableDeclarationNode(iteratorVariable);
+ iteratorVariableDecl.setInSource(false);
+
+ extendWithNode(iteratorVariableDecl);
+
+ Node expressionNode = scan(expression, p);
+
+ MethodAccessNode iteratorAccessNode =
+ new MethodAccessNode(iteratorSelect, expressionNode);
+ iteratorAccessNode.setInSource(false);
+ extendWithNode(iteratorAccessNode);
+ MethodInvocationNode iteratorCallNode =
+ new MethodInvocationNode(iteratorCall, iteratorAccessNode,
+ Collections.<Node>emptyList(), getCurrentPath());
+ iteratorCallNode.setInSource(false);
+ extendWithNode(iteratorCallNode);
+
+ translateAssignment(iteratorVariable,
+ new LocalVariableNode(iteratorVariable),
+ iteratorCallNode);
+
+ // Test the loop ending condition
+ addLabelForNextNode(conditionStart);
+ IdentifierTree iteratorUse1 =
+ treeBuilder.buildVariableUse(iteratorVariable);
+ handleArtificialTree(iteratorUse1);
+
+ LocalVariableNode iteratorReceiverNode =
+ new LocalVariableNode(iteratorUse1);
+ iteratorReceiverNode.setInSource(false);
+ extendWithNode(iteratorReceiverNode);
+
+ MemberSelectTree hasNextSelect =
+ treeBuilder.buildHasNextMethodAccess(iteratorUse1);
+ handleArtificialTree(hasNextSelect);
+
+ MethodAccessNode hasNextAccessNode =
+ new MethodAccessNode(hasNextSelect, iteratorReceiverNode);
+ hasNextAccessNode.setInSource(false);
+ extendWithNode(hasNextAccessNode);
+
+ MethodInvocationTree hasNextCall =
+ treeBuilder.buildMethodInvocation(hasNextSelect);
+ handleArtificialTree(hasNextCall);
+
+ MethodInvocationNode hasNextCallNode =
+ new MethodInvocationNode(hasNextCall, hasNextAccessNode,
+ Collections.<Node>emptyList(), getCurrentPath());
+ hasNextCallNode.setInSource(false);
+ extendWithNode(hasNextCallNode);
+ extendWithExtendedNode(new ConditionalJump(loopEntry, loopExit));
+
+ // Loop body, starting with declaration of the loop iteration variable
+ addLabelForNextNode(loopEntry);
+ extendWithNode(new VariableDeclarationNode(variable));
+
+ IdentifierTree iteratorUse2 =
+ treeBuilder.buildVariableUse(iteratorVariable);
+ handleArtificialTree(iteratorUse2);
+
+ LocalVariableNode iteratorReceiverNode2 =
+ new LocalVariableNode(iteratorUse2);
+ iteratorReceiverNode2.setInSource(false);
+ extendWithNode(iteratorReceiverNode2);
+
+ MemberSelectTree nextSelect =
+ treeBuilder.buildNextMethodAccess(iteratorUse2);
+ handleArtificialTree(nextSelect);
+
+ MethodAccessNode nextAccessNode =
+ new MethodAccessNode(nextSelect, iteratorReceiverNode2);
+ nextAccessNode.setInSource(false);
+ extendWithNode(nextAccessNode);
+
+ MethodInvocationTree nextCall =
+ treeBuilder.buildMethodInvocation(nextSelect);
+ handleArtificialTree(nextCall);
+
+ MethodInvocationNode nextCallNode =
+ new MethodInvocationNode(nextCall, nextAccessNode,
+ Collections.<Node>emptyList(), getCurrentPath());
+ nextCallNode.setInSource(false);
+ extendWithNode(nextCallNode);
+
+ translateAssignment(variable,
+ new LocalVariableNode(variable),
+ nextCall);
+
+ if (statement != null) {
+ scan(statement, p);
+ }
+
+ // Loop back edge
+ addLabelForNextNode(updateStart);
+ extendWithExtendedNode(new UnconditionalJump(conditionStart));
+
+ } else {
+ // TODO: Shift any labels after the initialization of the
+ // temporary array variable.
+
+ VariableTree arrayVariable = createEnhancedForLoopArrayVariable(expression, variableElement);
+ handleArtificialTree(arrayVariable);
+
+ VariableDeclarationNode arrayVariableNode =
+ new VariableDeclarationNode(arrayVariable);
+ arrayVariableNode.setInSource(false);
+ extendWithNode(arrayVariableNode);
+ Node expressionNode = scan(expression, p);
+
+ translateAssignment(arrayVariable,
+ new LocalVariableNode(arrayVariable),
+ expressionNode);
+
+ // Declare and initialize the loop index variable
+ TypeMirror intType = types.getPrimitiveType(TypeKind.INT);
+
+ LiteralTree zero =
+ treeBuilder.buildLiteral(new Integer(0));
+ handleArtificialTree(zero);
+
+ VariableTree indexVariable =
+ treeBuilder.buildVariableDecl(intType,
+ uniqueName("index"),
+ variableElement.getEnclosingElement(),
+ zero);
+ handleArtificialTree(indexVariable);
+ VariableDeclarationNode indexVariableNode =
+ new VariableDeclarationNode(indexVariable);
+ indexVariableNode.setInSource(false);
+ extendWithNode(indexVariableNode);
+ IntegerLiteralNode zeroNode =
+ extendWithNode(new IntegerLiteralNode(zero));
+
+ translateAssignment(indexVariable,
+ new LocalVariableNode(indexVariable),
+ zeroNode);
+
+ // Compare index to array length
+ addLabelForNextNode(conditionStart);
+ IdentifierTree indexUse1 =
+ treeBuilder.buildVariableUse(indexVariable);
+ handleArtificialTree(indexUse1);
+ LocalVariableNode indexNode1 =
+ new LocalVariableNode(indexUse1);
+ indexNode1.setInSource(false);
+ extendWithNode(indexNode1);
+
+ IdentifierTree arrayUse1 =
+ treeBuilder.buildVariableUse(arrayVariable);
+ handleArtificialTree(arrayUse1);
+ LocalVariableNode arrayNode1 =
+ extendWithNode(new LocalVariableNode(arrayUse1));
+
+ MemberSelectTree lengthSelect =
+ treeBuilder.buildArrayLengthAccess(arrayUse1);
+ handleArtificialTree(lengthSelect);
+ FieldAccessNode lengthAccessNode =
+ new FieldAccessNode(lengthSelect, arrayNode1);
+ lengthAccessNode.setInSource(false);
+ extendWithNode(lengthAccessNode);
+
+ BinaryTree lessThan =
+ treeBuilder.buildLessThan(indexUse1, lengthSelect);
+ handleArtificialTree(lessThan);
+
+ LessThanNode lessThanNode =
+ new LessThanNode(lessThan, indexNode1, lengthAccessNode);
+ lessThanNode.setInSource(false);
+ extendWithNode(lessThanNode);
+ extendWithExtendedNode(new ConditionalJump(loopEntry, loopExit));
+
+ // Loop body, starting with declaration of the loop iteration variable
+ addLabelForNextNode(loopEntry);
+ extendWithNode(new VariableDeclarationNode(variable));
+
+ IdentifierTree arrayUse2 =
+ treeBuilder.buildVariableUse(arrayVariable);
+ handleArtificialTree(arrayUse2);
+ LocalVariableNode arrayNode2 =
+ new LocalVariableNode(arrayUse2);
+ arrayNode2.setInSource(false);
+ extendWithNode(arrayNode2);
+
+ IdentifierTree indexUse2 =
+ treeBuilder.buildVariableUse(indexVariable);
+ handleArtificialTree(indexUse2);
+ LocalVariableNode indexNode2 =
+ new LocalVariableNode(indexUse2);
+ indexNode2.setInSource(false);
+ extendWithNode(indexNode2);
+
+ ArrayAccessTree arrayAccess =
+ treeBuilder.buildArrayAccess(arrayUse2, indexUse2);
+ handleArtificialTree(arrayAccess);
+ ArrayAccessNode arrayAccessNode =
+ new ArrayAccessNode(arrayAccess, arrayNode2, indexNode2);
+ arrayAccessNode.setInSource(false);
+ extendWithNode(arrayAccessNode);
+ translateAssignment(variable,
+ new LocalVariableNode(variable),
+ arrayAccessNode);
+
+ if (statement != null) {
+ scan(statement, p);
+ }
+
+ // Loop back edge
+ addLabelForNextNode(updateStart);
+
+ IdentifierTree indexUse3 =
+ treeBuilder.buildVariableUse(indexVariable);
+ handleArtificialTree(indexUse3);
+ LocalVariableNode indexNode3 =
+ new LocalVariableNode(indexUse3);
+ indexNode3.setInSource(false);
+ extendWithNode(indexNode3);
+
+ LiteralTree oneTree = treeBuilder.buildLiteral(Integer.valueOf(1));
+ handleArtificialTree(oneTree);
+ Node one = new IntegerLiteralNode(oneTree);
+ one.setInSource(false);
+ extendWithNode(one);
+
+ BinaryTree addOneTree = treeBuilder.buildBinary(intType, Tree.Kind.PLUS,
+ indexUse3, oneTree);
+ handleArtificialTree(addOneTree);
+ Node addOneNode = new NumericalAdditionNode(addOneTree, indexNode3, one);
+ addOneNode.setInSource(false);
+ extendWithNode(addOneNode);
+
+ AssignmentTree assignTree = treeBuilder.buildAssignment(indexUse3, addOneTree);
+ handleArtificialTree(assignTree);
+ Node assignNode = new AssignmentNode(assignTree, indexNode3, addOneNode);
+ assignNode.setInSource(false);
+ extendWithNode(assignNode);
+
+ extendWithExtendedNode(new UnconditionalJump(conditionStart));
+ }
+
+ // Loop exit
+ addLabelForNextNode(loopExit);
+
+ breakTargetL = oldBreakTargetL;
+ continueTargetL = oldContinueTargetL;
+
+ return null;
+ }
+
+ protected VariableTree createEnhancedForLoopIteratorVariable(MethodInvocationTree iteratorCall, VariableElement variableElement) {
+ TypeMirror iteratorType = InternalUtils.typeOf(iteratorCall);
+
+ // Declare and initialize a new, unique iterator variable
+ VariableTree iteratorVariable =
+ treeBuilder.buildVariableDecl(iteratorType, // annotatedIteratorTypeTree,
+ uniqueName("iter"),
+ variableElement.getEnclosingElement(),
+ iteratorCall);
+ return iteratorVariable;
+ }
+
+ protected VariableTree createEnhancedForLoopArrayVariable(ExpressionTree expression, VariableElement variableElement) {
+ TypeMirror arrayType = InternalUtils.typeOf(expression);
+
+ // Declare and initialize a temporary array variable
+ VariableTree arrayVariable =
+ treeBuilder.buildVariableDecl(arrayType,
+ uniqueName("array"),
+ variableElement.getEnclosingElement(),
+ expression);
+ return arrayVariable;
+ }
+
+ @Override
+ public Node visitForLoop(ForLoopTree tree, Void p) {
+ Name parentLabel = getLabel(getCurrentPath());
+
+ Label conditionStart = new Label();
+ Label loopEntry = new Label();
+ Label loopExit = new Label();
+
+ // If the loop is a labeled statement, then its continue
+ // target is identical for continues with no label and
+ // continues with the loop's label.
+ Label updateStart;
+ if (parentLabel != null) {
+ updateStart = continueLabels.get(parentLabel);
+ } else {
+ updateStart = new Label();
+ }
+
+ Label oldBreakTargetL = breakTargetL;
+ breakTargetL = loopExit;
+
+ Label oldContinueTargetL = continueTargetL;
+ continueTargetL = updateStart;
+
+ // Initializer
+ for (StatementTree init : tree.getInitializer()) {
+ scan(init, p);
+ }
+
+ // Condition
+ addLabelForNextNode(conditionStart);
+ if (tree.getCondition() != null) {
+ unbox(scan(tree.getCondition(), p));
+ ConditionalJump cjump = new ConditionalJump(loopEntry, loopExit);
+ extendWithExtendedNode(cjump);
+ }
+
+ // Loop body
+ addLabelForNextNode(loopEntry);
+ if (tree.getStatement() != null) {
+ scan(tree.getStatement(), p);
+ }
+
+ // Update
+ addLabelForNextNode(updateStart);
+ for (ExpressionStatementTree update : tree.getUpdate()) {
+ scan(update, p);
+ }
+
+ extendWithExtendedNode(new UnconditionalJump(conditionStart));
+
+ // Loop exit
+ addLabelForNextNode(loopExit);
+
+ breakTargetL = oldBreakTargetL;
+ continueTargetL = oldContinueTargetL;
+
+ return null;
+ }
+
+ @Override
+ public Node visitIdentifier(IdentifierTree tree, Void p) {
+ Node node;
+ if (TreeUtils.isFieldAccess(tree)) {
+ Node receiver = getReceiver(tree,
+ TreeUtils.enclosingClass(getCurrentPath()));
+ node = new FieldAccessNode(tree, receiver);
+ } else {
+ Element element = TreeUtils.elementFromUse(tree);
+ switch (element.getKind()) {
+ case ANNOTATION_TYPE:
+ case CLASS:
+ case ENUM:
+ case INTERFACE:
+ case TYPE_PARAMETER:
+ node = new ClassNameNode(tree);
+ break;
+ case FIELD:
+ // Note that "this"/"super" is a field, but not a field access.
+ if (element.getSimpleName().contentEquals("this")) {
+ node = new ExplicitThisLiteralNode(tree);
+ } else {
+ node = new SuperNode(tree);
+ }
+ break;
+ case EXCEPTION_PARAMETER:
+ case LOCAL_VARIABLE:
+ case RESOURCE_VARIABLE:
+ case PARAMETER:
+ node = new LocalVariableNode(tree);
+ break;
+ case PACKAGE:
+ node = new PackageNameNode(tree);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "unexpected element kind : " + element.getKind());
+ }
+ }
+ extendWithNode(node);
+ return node;
+ }
+
+ @Override
+ public Node visitIf(IfTree tree, Void p) {
+ // all necessary labels
+ Label thenEntry = new Label();
+ Label elseEntry = new Label();
+ Label endIf = new Label();
+
+ // basic block for the condition
+ unbox(scan(tree.getCondition(), p));
+
+ ConditionalJump cjump = new ConditionalJump(thenEntry, elseEntry);
+ extendWithExtendedNode(cjump);
+
+ // then branch
+ addLabelForNextNode(thenEntry);
+ StatementTree thenStatement = tree.getThenStatement();
+ scan(thenStatement, p);
+ extendWithExtendedNode(new UnconditionalJump(endIf));
+
+ // else branch
+ addLabelForNextNode(elseEntry);
+ StatementTree elseStatement = tree.getElseStatement();
+ if (elseStatement != null) {
+ scan(elseStatement, p);
+ }
+
+ // label the end of the if statement
+ addLabelForNextNode(endIf);
+
+ return null;
+ }
+
+ @Override
+ public Node visitImport(ImportTree tree, Void p) {
+ assert false : "ImportTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public Node visitArrayAccess(ArrayAccessTree tree, Void p) {
+ Node array = scan(tree.getExpression(), p);
+ Node index = unaryNumericPromotion(scan(tree.getIndex(), p));
+ return extendWithNode(new ArrayAccessNode(tree, array, index));
+ }
+
+ @Override
+ public Node visitLabeledStatement(LabeledStatementTree tree, Void p) {
+ // This method can set the break target after generating all Nodes
+ // in the contained statement, but it can't set the continue target,
+ // which may be in the middle of a sequence of nodes. Labeled loops
+ // must look up and use the continue Labels.
+ Name labelName = tree.getLabel();
+
+ Label breakL = new Label(labelName + "_break");
+ Label continueL = new Label(labelName + "_continue");
+
+ breakLabels.put(labelName, breakL);
+ continueLabels.put(labelName, continueL);
+
+ scan(tree.getStatement(), p);
+
+ addLabelForNextNode(breakL);
+
+ breakLabels.remove(labelName);
+ continueLabels.remove(labelName);
+
+ return null;
+ }
+
+ @Override
+ public Node visitLiteral(LiteralTree tree, Void p) {
+ Node r = null;
+ switch (tree.getKind()) {
+ case BOOLEAN_LITERAL:
+ r = new BooleanLiteralNode(tree);
+ break;
+ case CHAR_LITERAL:
+ r = new CharacterLiteralNode(tree);
+ break;
+ case DOUBLE_LITERAL:
+ r = new DoubleLiteralNode(tree);
+ break;
+ case FLOAT_LITERAL:
+ r = new FloatLiteralNode(tree);
+ break;
+ case INT_LITERAL:
+ r = new IntegerLiteralNode(tree);
+ break;
+ case LONG_LITERAL:
+ r = new LongLiteralNode(tree);
+ break;
+ case NULL_LITERAL:
+ r = new NullLiteralNode(tree);
+ break;
+ case STRING_LITERAL:
+ r = new StringLiteralNode(tree);
+ break;
+ default:
+ assert false : "unexpected literal tree";
+ break;
+ }
+ assert r != null : "unexpected literal tree";
+ Node result = extendWithNode(r);
+ return result;
+ }
+
+ @Override
+ public Node visitMethod(MethodTree tree, Void p) {
+ assert false : "MethodTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public Node visitModifiers(ModifiersTree tree, Void p) {
+ assert false : "ModifiersTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public Node visitNewArray(NewArrayTree tree, Void p) {
+ // see JLS 15.10
+
+ ArrayType type = (ArrayType)InternalUtils.typeOf(tree);
+ TypeMirror elemType = type.getComponentType();
+
+ List<? extends ExpressionTree> dimensions = tree.getDimensions();
+ List<? extends ExpressionTree> initializers = tree
+ .getInitializers();
+
+ List<Node> dimensionNodes = new ArrayList<Node>();
+ if (dimensions != null) {
+ for (ExpressionTree dim : dimensions) {
+ dimensionNodes.add(unaryNumericPromotion(scan(dim, p)));
+ }
+ }
+
+ List<Node> initializerNodes = new ArrayList<Node>();
+ if (initializers != null) {
+ for (ExpressionTree init : initializers) {
+ initializerNodes.add(assignConvert(scan(init, p), elemType));
+ }
+ }
+
+ Node node = new ArrayCreationNode(tree, type, dimensionNodes,
+ initializerNodes);
+ return extendWithNode(node);
+ }
+
+ @Override
+ public Node visitNewClass(NewClassTree tree, Void p) {
+ // see JLS 15.9
+
+ Tree enclosingExpr = tree.getEnclosingExpression();
+ if (enclosingExpr != null) {
+ scan(enclosingExpr, p);
+ }
+
+ // We ignore any class body because its methods should
+ // be visited separately.
+
+ // Convert constructor arguments
+ ExecutableElement constructor = TreeUtils.elementFromUse(tree);
+
+ List<? extends ExpressionTree> actualExprs = tree.getArguments();
+
+ List<Node> arguments = convertCallArguments(constructor,
+ actualExprs);
+
+ Node constructorNode = scan(tree.getIdentifier(), p);
+
+ Node node = new ObjectCreationNode(tree, constructorNode, arguments);
+
+ Set<TypeMirror> thrownSet = new HashSet<>();
+ // Add exceptions explicitly mentioned in the throws clause.
+ List<? extends TypeMirror> thrownTypes = constructor.getThrownTypes();
+ thrownSet.addAll(thrownTypes);
+ // Add Throwable to account for unchecked exceptions
+ TypeElement throwableElement = elements
+ .getTypeElement("java.lang.Throwable");
+ thrownSet.add(throwableElement.asType());
+
+ extendWithNodeWithExceptions(node, thrownSet);
+
+ return node;
+ }
+
+ /**
+ * Maps a {@code Tree} its directly enclosing {@code ParenthesizedTree} if one exists.
+ *
+ * This map is used by {@link CFGTranslationPhaseOne#addToLookupMap(Node)} to
+ * associate a {@code ParenthesizedTree} with the dataflow {@code Node} that was used
+ * during inference. This map is necessary because dataflow does
+ * not create a {@code Node} for a {@code ParenthesizedTree.}
+ */
+ private final Map<Tree, ParenthesizedTree> parenMapping = new HashMap<>();
+
+ @Override
+ public Node visitParenthesized(ParenthesizedTree tree, Void p) {
+ parenMapping.put(tree.getExpression(), tree);
+ return scan(tree.getExpression(), p);
+ }
+
+ @Override
+ public Node visitReturn(ReturnTree tree, Void p) {
+ ExpressionTree ret = tree.getExpression();
+ // TODO: also have a return-node if nothing is returned
+ ReturnNode result = null;
+ if (ret != null) {
+ Node node = scan(ret, p);
+ Tree enclosing = TreeUtils.enclosingOfKind(getCurrentPath(), new HashSet<Kind>(Arrays.asList(Kind.METHOD, Kind.LAMBDA_EXPRESSION)));
+ if (enclosing.getKind() == Kind.LAMBDA_EXPRESSION) {
+ LambdaExpressionTree lambdaTree = (LambdaExpressionTree) enclosing;
+ TreePath lambdaTreePath = TreePath.getPath(getCurrentPath().getCompilationUnit(), lambdaTree);
+ Context ctx = ((JavacProcessingEnvironment)env).getContext();
+ Element overriddenElement = com.sun.tools.javac.code.Types.instance(ctx).findDescriptorSymbol(
+ ((Type)trees.getTypeMirror(lambdaTreePath)).tsym);
+
+ result = new ReturnNode(tree, node, env.getTypeUtils(), lambdaTree, (MethodSymbol)overriddenElement);
+ } else {
+ result = new ReturnNode(tree, node, env.getTypeUtils(), (MethodTree)enclosing);
+ }
+ returnNodes.add(result);
+ extendWithNode(result);
+ }
+ extendWithExtendedNode(new UnconditionalJump(regularExitLabel));
+ // TODO: return statements should also flow to an enclosing finally block
+ return result;
+ }
+
+ @Override
+ public Node visitMemberSelect(MemberSelectTree tree, Void p) {
+ Node expr = scan(tree.getExpression(), p);
+ if (!TreeUtils.isFieldAccess(tree)) {
+ // Could be a selector of a class or package
+ Node result = null;
+ Element element = TreeUtils.elementFromUse(tree);
+ switch (element.getKind()) {
+ case ANNOTATION_TYPE:
+ case CLASS:
+ case ENUM:
+ case INTERFACE:
+ result = extendWithNode(new ClassNameNode(tree, expr));
+ break;
+ case PACKAGE:
+ result = extendWithNode(new PackageNameNode(tree, (PackageNameNode) expr));
+ break;
+ default:
+ assert false : "Unexpected element kind: " + element.getKind();
+ return null;
+ }
+ return result;
+ }
+
+ Node node = new FieldAccessNode(tree, expr);
+
+ Element element = TreeUtils.elementFromUse(tree);
+ if (ElementUtils.isStatic(element) ||
+ expr instanceof ImplicitThisLiteralNode ||
+ expr instanceof ExplicitThisLiteralNode) {
+ // No NullPointerException can be thrown, use normal node
+ extendWithNode(node);
+ } else {
+ TypeElement npeElement = elements
+ .getTypeElement("java.lang.NullPointerException");
+ extendWithNodeWithException(node, npeElement.asType());
+ }
+
+ return node;
+ }
+
+ @Override
+ public Node visitEmptyStatement(EmptyStatementTree tree, Void p) {
+ return null;
+ }
+
+ @Override
+ public Node visitSynchronized(SynchronizedTree tree, Void p) {
+ // see JLS 14.19
+
+ Node synchronizedExpr = scan(tree.getExpression(), p);
+ SynchronizedNode synchronizedStartNode = new SynchronizedNode(tree, synchronizedExpr, true, env.getTypeUtils());
+ extendWithNode(synchronizedStartNode);
+ scan(tree.getBlock(), p);
+ SynchronizedNode synchronizedEndNode = new SynchronizedNode(tree, synchronizedExpr, false, env.getTypeUtils());
+ extendWithNode(synchronizedEndNode);
+
+ return null;
+ }
+
+ @Override
+ public Node visitThrow(ThrowTree tree, Void p) {
+ Node expression = scan(tree.getExpression(), p);
+ TypeMirror exception = expression.getType();
+ ThrowNode throwsNode = new ThrowNode(tree, expression, env.getTypeUtils());
+ NodeWithExceptionsHolder exNode = extendWithNodeWithException(
+ throwsNode, exception);
+ exNode.setTerminatesExecution(true);
+ return throwsNode;
+ }
+
+ @Override
+ public Node visitCompilationUnit(CompilationUnitTree tree, Void p) {
+ assert false : "CompilationUnitTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public Node visitTry(TryTree tree, Void p) {
+ List<? extends CatchTree> catches = tree.getCatches();
+ BlockTree finallyBlock = tree.getFinallyBlock();
+
+ extendWithNode(new MarkerNode(tree, "start of try statement", env.getTypeUtils()));
+
+ // TODO: Should we handle try-with-resources blocks by also generating code
+ // for automatically closing the resources?
+ List<? extends Tree> resources = tree.getResources();
+ for (Tree resource : resources) {
+ scan(resource, p);
+ }
+
+ List<Pair<TypeMirror, Label>> catchLabels = new ArrayList<>();
+ for (CatchTree c : catches) {
+ TypeMirror type = InternalUtils.typeOf(c.getParameter().getType());
+ assert type != null : "exception parameters must have a type";
+ catchLabels.add(Pair.of(type, new Label()));
+ }
+
+ Label finallyLabel = null;
+ if (finallyBlock != null) {
+ finallyLabel = new Label();
+ tryStack.pushFrame(new TryFinallyFrame(finallyLabel));
+ }
+
+ Label doneLabel = new Label();
+
+ tryStack.pushFrame(new TryCatchFrame(types, catchLabels));
+
+ scan(tree.getBlock(), p);
+ extendWithExtendedNode(new UnconditionalJump(firstNonNull(finallyLabel, doneLabel)));
+
+ tryStack.popFrame();
+
+ int catchIndex = 0;
+ for (CatchTree c : catches) {
+ addLabelForNextNode(catchLabels.get(catchIndex).second);
+ scan(c, p);
+ catchIndex++;
+ extendWithExtendedNode(new UnconditionalJump(firstNonNull(finallyLabel, doneLabel)));
+ }
+
+ if (finallyLabel != null) {
+ tryStack.popFrame();
+ addLabelForNextNode(finallyLabel);
+ scan(finallyBlock, p);
+
+ TypeMirror throwableType =
+ elements.getTypeElement("java.lang.Throwable").asType();
+ extendWithNodeWithException(new MarkerNode(tree, "end of finally block", env.getTypeUtils()),
+ throwableType);
+ }
+
+ addLabelForNextNode(doneLabel);
+
+ return null;
+ }
+
+ @Override
+ public Node visitParameterizedType(ParameterizedTypeTree tree, Void p) {
+ return extendWithNode(new ParameterizedTypeNode(tree));
+ }
+
+ @Override
+ public Node visitUnionType(UnionTypeTree tree, Void p) {
+ assert false : "UnionTypeTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public Node visitArrayType(ArrayTypeTree tree, Void p) {
+ return extendWithNode(new ArrayTypeNode(tree));
+ }
+
+ @Override
+ public Node visitTypeCast(TypeCastTree tree, Void p) {
+ final Node operand = scan(tree.getExpression(), p);
+ final TypeMirror type = InternalUtils.typeOf(tree.getType());
+ final Node node = new TypeCastNode(tree, operand, type);
+ final TypeElement cceElement = elements.getTypeElement("java.lang.ClassCastException");
+
+ extendWithNodeWithException(node, cceElement.asType());
+ return node;
+ }
+
+ @Override
+ public Node visitPrimitiveType(PrimitiveTypeTree tree, Void p) {
+ return extendWithNode(new PrimitiveTypeNode(tree));
+ }
+
+ @Override
+ public Node visitTypeParameter(TypeParameterTree tree, Void p) {
+ assert false : "TypeParameterTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public Node visitInstanceOf(InstanceOfTree tree, Void p) {
+ Node operand = scan(tree.getExpression(), p);
+ TypeMirror refType = InternalUtils.typeOf(tree.getType());
+ InstanceOfNode node = new InstanceOfNode(tree, operand, refType,
+ types);
+ extendWithNode(node);
+ return node;
+ }
+
+ @Override
+ public Node visitUnary(UnaryTree tree, Void p) {
+ Node result = null;
+ Tree.Kind kind = tree.getKind();
+ switch (kind) {
+ case BITWISE_COMPLEMENT:
+ case UNARY_MINUS:
+ case UNARY_PLUS: {
+ // see JLS 15.14 and 15.15
+ Node expr = scan(tree.getExpression(), p);
+ expr = unaryNumericPromotion(expr);
+
+ // TypeMirror exprType = InternalUtils.typeOf(tree);
+
+ switch (kind) {
+ case BITWISE_COMPLEMENT:
+ result = extendWithNode(new BitwiseComplementNode(tree,
+ expr));
+ break;
+ case UNARY_MINUS:
+ result = extendWithNode(new NumericalMinusNode(tree, expr));
+ break;
+ case UNARY_PLUS:
+ result = extendWithNode(new NumericalPlusNode(tree, expr));
+ break;
+ default:
+ assert false;
+ break;
+ }
+ break;
+ }
+
+ case LOGICAL_COMPLEMENT: {
+ // see JLS 15.15.6
+ Node expr = scan(tree.getExpression(), p);
+ result = extendWithNode(new ConditionalNotNode(tree,
+ unbox(expr)));
+ break;
+ }
+
+ case POSTFIX_DECREMENT:
+ case POSTFIX_INCREMENT: {
+ ExpressionTree exprTree = tree.getExpression();
+ TypeMirror exprType = InternalUtils.typeOf(exprTree);
+ TypeMirror oneType = types.getPrimitiveType(TypeKind.INT);
+ Node expr = scan(exprTree, p);
+
+ TypeMirror promotedType = binaryPromotedType(exprType, oneType);
+
+ LiteralTree oneTree = treeBuilder.buildLiteral(Integer.valueOf(1));
+ handleArtificialTree(oneTree);
+
+ Node exprRHS = binaryNumericPromotion(expr, promotedType);
+ Node one = new IntegerLiteralNode(oneTree);
+ one.setInSource(false);
+ extendWithNode(one);
+ one = binaryNumericPromotion(one, promotedType);
+
+ BinaryTree operTree = treeBuilder.buildBinary(promotedType,
+ (kind == Tree.Kind.POSTFIX_INCREMENT ? Tree.Kind.PLUS : Tree.Kind.MINUS),
+ exprTree, oneTree);
+ handleArtificialTree(operTree);
+ Node operNode;
+ if (kind == Tree.Kind.POSTFIX_INCREMENT) {
+ operNode = new NumericalAdditionNode(operTree, exprRHS, one);
+ } else {
+ assert kind == Tree.Kind.POSTFIX_DECREMENT;
+ operNode = new NumericalSubtractionNode(operTree, exprRHS, one);
+ }
+ extendWithNode(operNode);
+
+ Node narrowed = narrowAndBox(operNode, exprType);
+ // TODO: By using the assignment as the result of the expression, we
+ // act like a pre-increment/decrement. Fix this by saving the initial
+ // value of the expression in a temporary.
+ AssignmentNode assignNode = new AssignmentNode(tree, expr, narrowed);
+ extendWithNode(assignNode);
+ result = assignNode;
+ break;
+ }
+ case PREFIX_DECREMENT:
+ case PREFIX_INCREMENT: {
+ ExpressionTree exprTree = tree.getExpression();
+ TypeMirror exprType = InternalUtils.typeOf(exprTree);
+ TypeMirror oneType = types.getPrimitiveType(TypeKind.INT);
+ Node expr = scan(exprTree, p);
+
+ TypeMirror promotedType = binaryPromotedType(exprType, oneType);
+
+ LiteralTree oneTree = treeBuilder.buildLiteral(Integer.valueOf(1));
+ handleArtificialTree(oneTree);
+
+ Node exprRHS = binaryNumericPromotion(expr, promotedType);
+ Node one = new IntegerLiteralNode(oneTree);
+ one.setInSource(false);
+ extendWithNode(one);
+ one = binaryNumericPromotion(one, promotedType);
+
+ BinaryTree operTree = treeBuilder.buildBinary(promotedType,
+ (kind == Tree.Kind.PREFIX_INCREMENT ? Tree.Kind.PLUS : Tree.Kind.MINUS),
+ exprTree, oneTree);
+ handleArtificialTree(operTree);
+ Node operNode;
+ if (kind == Tree.Kind.PREFIX_INCREMENT) {
+ operNode = new NumericalAdditionNode(operTree, exprRHS, one);
+ } else {
+ assert kind == Tree.Kind.PREFIX_DECREMENT;
+ operNode = new NumericalSubtractionNode(operTree, exprRHS, one);
+ }
+ extendWithNode(operNode);
+
+ Node narrowed = narrowAndBox(operNode, exprType);
+ AssignmentNode assignNode = new AssignmentNode(tree, expr, narrowed);
+ extendWithNode(assignNode);
+ result = assignNode;
+ break;
+ }
+
+ case OTHER:
+ default:
+ // special node NLLCHK
+ if (tree.toString().startsWith("<*nullchk*>")) {
+ Node expr = scan(tree.getExpression(), p);
+ result = extendWithNode(new NullChkNode(tree, expr));
+ break;
+ }
+
+ assert false : "Unknown kind (" + kind
+ + ") of unary expression: " + tree;
+ }
+
+ return result;
+ }
+
+ @Override
+ public Node visitVariable(VariableTree tree, Void p) {
+
+ // see JLS 14.4
+
+ boolean isField = getCurrentPath().getParentPath() != null
+ && getCurrentPath().getParentPath().getLeaf().getKind() == Kind.CLASS;
+ Node node = null;
+
+ ClassTree enclosingClass = TreeUtils
+ .enclosingClass(getCurrentPath());
+ TypeElement classElem = TreeUtils
+ .elementFromDeclaration(enclosingClass);
+ Node receiver = new ImplicitThisLiteralNode(classElem.asType());
+
+ if (isField) {
+ ExpressionTree initializer = tree.getInitializer();
+ assert initializer != null;
+ node = translateAssignment(
+ tree,
+ new FieldAccessNode(tree, TreeUtils.elementFromDeclaration(tree), receiver),
+ initializer);
+ } else {
+ // local variable definition
+ VariableDeclarationNode decl = new VariableDeclarationNode(tree);
+ extendWithNode(decl);
+
+ // initializer
+
+ ExpressionTree initializer = tree.getInitializer();
+ if (initializer != null) {
+ node = translateAssignment(tree, new LocalVariableNode(tree, receiver),
+ initializer);
+ }
+ }
+
+ return node;
+ }
+
+ @Override
+ public Node visitWhileLoop(WhileLoopTree tree, Void p) {
+ Name parentLabel = getLabel(getCurrentPath());
+
+ Label loopEntry = new Label();
+ Label loopExit = new Label();
+
+ // If the loop is a labeled statement, then its continue
+ // target is identical for continues with no label and
+ // continues with the loop's label.
+ Label conditionStart;
+ if (parentLabel != null) {
+ conditionStart = continueLabels.get(parentLabel);
+ } else {
+ conditionStart = new Label();
+ }
+
+ Label oldBreakTargetL = breakTargetL;
+ breakTargetL = loopExit;
+
+ Label oldContinueTargetL = continueTargetL;
+ continueTargetL = conditionStart;
+
+ // Condition
+ addLabelForNextNode(conditionStart);
+ if (tree.getCondition() != null) {
+ unbox(scan(tree.getCondition(), p));
+ ConditionalJump cjump = new ConditionalJump(loopEntry, loopExit);
+ extendWithExtendedNode(cjump);
+ }
+
+ // Loop body
+ addLabelForNextNode(loopEntry);
+ if (tree.getStatement() != null) {
+ scan(tree.getStatement(), p);
+ }
+ extendWithExtendedNode(new UnconditionalJump(conditionStart));
+
+ // Loop exit
+ addLabelForNextNode(loopExit);
+
+ breakTargetL = oldBreakTargetL;
+ continueTargetL = oldContinueTargetL;
+
+ return null;
+ }
+
+ @Override
+ public Node visitLambdaExpression(LambdaExpressionTree tree, Void p) {
+ declaredLambdas.add(tree);
+ Node node = new FunctionalInterfaceNode(tree);
+ extendWithNode(node);
+ return node;
+ }
+
+ @Override
+ public Node visitMemberReference(MemberReferenceTree tree, Void p) {
+ Tree enclosingExpr = tree.getQualifierExpression();
+ if (enclosingExpr != null) {
+ scan(enclosingExpr, p);
+ }
+
+ Node node = new FunctionalInterfaceNode(tree);
+ extendWithNode(node);
+
+ return node;
+ }
+
+ @Override
+ public Node visitWildcard(WildcardTree tree, Void p) {
+ assert false : "WildcardTree is unexpected in AST to CFG translation";
+ return null;
+ }
+
+ @Override
+ public Node visitOther(Tree tree, Void p) {
+ assert false : "Unknown AST element encountered in AST to CFG translation.";
+ return null;
+ }
+ }
+
+ /**
+ * A tuple with 4 named elements.
+ */
+ private interface TreeInfo {
+ boolean isBoxed();
+ boolean isNumeric();
+ boolean isBoolean();
+ TypeMirror unboxedType();
+ }
+
+ private static <A> A firstNonNull(A first, A second) {
+ if (first != null) {
+ return first;
+ } else if (second != null) {
+ return second;
+ } else {
+ throw new NullPointerException();
+ }
+ }
+
+ /* --------------------------------------------------------- */
+ /* Utility routines for debugging CFG building */
+ /* --------------------------------------------------------- */
+
+ /**
+ * Print a set of {@link Block}s and the edges between them. This is useful
+ * for examining the results of phase two.
+ */
+ protected static void printBlocks(Set<Block> blocks) {
+ for (Block b : blocks) {
+ System.out.print(b.hashCode() + ": " + b);
+ switch (b.getType()) {
+ case REGULAR_BLOCK:
+ case SPECIAL_BLOCK: {
+ Block succ = ((SingleSuccessorBlockImpl) b).getSuccessor();
+ System.out.println(" -> "
+ + (succ != null ? succ.hashCode() : "||"));
+ break;
+ }
+ case EXCEPTION_BLOCK: {
+ Block succ = ((SingleSuccessorBlockImpl) b).getSuccessor();
+ System.out.print(" -> "
+ + (succ != null ? succ.hashCode() : "||") + " {");
+ for (Map.Entry<TypeMirror, Set<Block>> entry : ((ExceptionBlockImpl) b).getExceptionalSuccessors().entrySet()) {
+ System.out.print(entry.getKey() + " : " + entry.getValue() + ", ");
+ }
+ System.out.println("}");
+ break;
+ }
+ case CONDITIONAL_BLOCK: {
+ Block tSucc = ((ConditionalBlockImpl) b).getThenSuccessor();
+ Block eSucc = ((ConditionalBlockImpl) b).getElseSuccessor();
+ System.out.println(" -> T "
+ + (tSucc != null ? tSucc.hashCode() : "||") + " F "
+ + (eSucc != null ? eSucc.hashCode() : "||"));
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGVisualizer.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGVisualizer.java
new file mode 100644
index 0000000000..8156f92942
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/CFGVisualizer.java
@@ -0,0 +1,181 @@
+package org.checkerframework.dataflow.cfg;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.analysis.AbstractValue;
+import org.checkerframework.dataflow.analysis.Analysis;
+import org.checkerframework.dataflow.analysis.FlowExpressions;
+import org.checkerframework.dataflow.analysis.Store;
+import org.checkerframework.dataflow.analysis.TransferFunction;
+import org.checkerframework.dataflow.cfg.block.Block;
+import org.checkerframework.dataflow.cfg.block.SpecialBlock;
+import org.checkerframework.dataflow.cfg.node.Node;
+
+import java.util.Map;
+
+/**
+ * Perform some visualization on a control flow graph.
+ * The particular operations depend on the implementation.
+ */
+public interface CFGVisualizer<A extends AbstractValue<A>,
+ S extends Store<S>, T extends TransferFunction<A, S>> {
+ /**
+ * Initialization method guaranteed to be called once before the
+ * first invocation of {@link visualize}.
+ *
+ * @param args implementation-dependent options
+ */
+ void init(Map<String, Object> args);
+
+ /**
+ * Output a visualization representing the control flow graph starting
+ * at {@code entry}.
+ * The concrete actions are implementation dependent.
+ *
+ * An invocation {@code visualize(cfg, entry, null);} does not
+ * output stores at the beginning of basic blocks.
+ *
+ * @param cfg
+ * The CFG to visualize.
+ * @param entry
+ * The entry node of the control flow graph to be represented.
+ * @param analysis
+ * An analysis containing information about the program
+ * represented by the CFG. The information includes {@link Store}s
+ * that are valid at the beginning of basic blocks reachable
+ * from {@code entry} and per-node information for value
+ * producing {@link Node}s. Can also be {@code null} to
+ * indicate that this information should not be output.
+ * @return possible analysis results, e.g. generated file names.
+ */
+ /*@Nullable*/ Map<String, Object> visualize(ControlFlowGraph cfg, Block entry,
+ /*@Nullable*/ Analysis<A, S, T> analysis);
+
+ /**
+ * Delegate the visualization responsibility
+ * to the passed {@link Store} instance, which will call back to this
+ * visualizer instance for sub-components.
+ *
+ * @param store the store to visualize
+ */
+ void visualizeStore(S store);
+
+ /**
+ * Called by a {@code CFAbstractStore} to visualize
+ * the class name before calling the
+ * {@code CFAbstractStore#internalVisualize()} method.
+ *
+ * @param classCanonicalName the canonical name of the class
+ */
+ void visualizeStoreHeader(String classCanonicalName);
+
+ /**
+ * Called by {@code CFAbstractStore#internalVisualize()} to visualize
+ * a local variable.
+ *
+ * @param localVar the local variable
+ * @param value the value of the local variable
+ */
+ void visualizeStoreLocalVar(FlowExpressions.LocalVariable localVar, A value);
+
+ /**
+ * Called by {@code CFAbstractStore#internalVisualize()} to visualize
+ * the value of the current object {@code this} in this Store.
+ *
+ * @param value the value of the current object this
+ */
+ void visualizeStoreThisVal(A value);
+
+ /**
+ * Called by {@code CFAbstractStore#internalVisualize()} to visualize
+ * the value of fields collected by this Store.
+ *
+ * @param fieldAccess the field
+ * @param value the value of the field
+ */
+ void visualizeStoreFieldVals(FlowExpressions.FieldAccess fieldAccess, A value);
+
+ /**
+ * Called by {@code CFAbstractStore#internalVisualize()} to visualize
+ * the value of arrays collected by this Store.
+ *
+ * @param arrayValue the array
+ * @param value the value of the array
+ */
+ void visualizeStoreArrayVal(FlowExpressions.ArrayAccess arrayValue, A value);
+
+ /**
+ * Called by {@code CFAbstractStore#internalVisualize()} to visualize
+ * the value of pure method calls collected by this Store.
+ *
+ * @param methodCall the pure method call
+ * @param value the value of the pure method call
+ */
+ void visualizeStoreMethodVals(FlowExpressions.MethodCall methodCall, A value);
+
+ /**
+ * Called by {@code CFAbstractStore#internalVisualize()} to visualize
+ * the value of class names collected by this Store.
+ *
+ * @param className the class name
+ * @param value the value of the class name
+ */
+ void visualizeStoreClassVals(FlowExpressions.ClassName className, A value);
+
+ /**
+ * Called by {@code CFAbstractStore#internalVisualize()} to visualize
+ * the specific information collected according to the specific kind of Store.
+ * Currently, these Stores call this method: {@code LockStore},
+ * {@code NullnessStore}, and {@code InitializationStore} to visualize additional
+ * information.
+ *
+ * @param keyName the name of the specific information to be visualized
+ * @param value the value of the specific information to be visualized
+ */
+ void visualizeStoreKeyVal(String keyName, Object value);
+
+ /**
+ * Called by {@code CFAbstractStore} to visualize
+ * any information after the invocation of {@code CFAbstractStore#internalVisualize()}.
+ */
+ void visualizeStoreFooter();
+
+ /**
+ * Visualize a block based on the analysis.
+ *
+ * @param bb the block
+ * @param analysis the current analysis
+ */
+ void visualizeBlock(Block bb, /*@Nullable*/ Analysis<A, S, T> analysis);
+
+ /**
+ * Visualize a SpecialBlock.
+ *
+ * @param sbb the special block
+ */
+ void visualizeSpecialBlock(SpecialBlock sbb);
+
+ /**
+ * Visualize the transferInput of a Block based on the analysis.
+ *
+ * @param bb the block
+ * @param analysis the current analysis
+ */
+ void visualizeBlockTransferInput(Block bb, Analysis<A, S, T> analysis);
+
+ /**
+ * Visualize a Node based on the analysis.
+ *
+ * @param t the node
+ * @param analysis the current analysis
+ */
+ void visualizeBlockNode(Node t, /*@Nullable*/ Analysis<A, S, T> analysis);
+
+ /**
+ * Shutdown method called once from the shutdown hook of the
+ * {@code BaseTypeChecker}.
+ */
+ void shutdown();
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/ControlFlowGraph.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/ControlFlowGraph.java
new file mode 100644
index 0000000000..58f354e6ae
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/ControlFlowGraph.java
@@ -0,0 +1,254 @@
+package org.checkerframework.dataflow.cfg;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.cfg.block.Block;
+import org.checkerframework.dataflow.cfg.block.Block.BlockType;
+import org.checkerframework.dataflow.cfg.block.ConditionalBlock;
+import org.checkerframework.dataflow.cfg.block.ExceptionBlock;
+import org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock;
+import org.checkerframework.dataflow.cfg.block.SpecialBlock;
+import org.checkerframework.dataflow.cfg.block.SpecialBlockImpl;
+import org.checkerframework.dataflow.cfg.node.Node;
+import org.checkerframework.dataflow.cfg.node.ReturnNode;
+
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A control flow graph (CFG for short) of a single method.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class ControlFlowGraph {
+
+ /** The entry block of the control flow graph. */
+ protected final SpecialBlock entryBlock;
+
+ /** The regular exit block of the control flow graph. */
+ protected final SpecialBlock regularExitBlock;
+
+ /** The exceptional exit block of the control flow graph. */
+ protected final SpecialBlock exceptionalExitBlock;
+
+ /** The AST this CFG corresponds to. */
+ protected UnderlyingAST underlyingAST;
+
+ /**
+ * Maps from AST {@link Tree}s to {@link Node}s. Every Tree that produces
+ * a value will have at least one corresponding Node. Trees
+ * that undergo conversions, such as boxing or unboxing, can map to two
+ * distinct Nodes. The Node for the pre-conversion value is stored
+ * in treeLookup, while the Node for the post-conversion value
+ * is stored in convertedTreeLookup.
+ */
+ protected IdentityHashMap<Tree, Node> treeLookup;
+
+ /** Map from AST {@link Tree}s to post-conversion {@link Node}s. */
+ protected IdentityHashMap<Tree, Node> convertedTreeLookup;
+
+ /**
+ * All return nodes (if any) encountered. Only includes return
+ * statements that actually return something
+ */
+ protected final List<ReturnNode> returnNodes;
+
+ public ControlFlowGraph(SpecialBlock entryBlock, SpecialBlockImpl regularExitBlock, SpecialBlockImpl exceptionalExitBlock, UnderlyingAST underlyingAST,
+ IdentityHashMap<Tree, Node> treeLookup,
+ IdentityHashMap<Tree, Node> convertedTreeLookup,
+ List<ReturnNode> returnNodes) {
+ super();
+ this.entryBlock = entryBlock;
+ this.underlyingAST = underlyingAST;
+ this.treeLookup = treeLookup;
+ this.convertedTreeLookup = convertedTreeLookup;
+ this.regularExitBlock = regularExitBlock;
+ this.exceptionalExitBlock = exceptionalExitBlock;
+ this.returnNodes = returnNodes;
+ }
+
+ /**
+ * @return the {@link Node} to which the {@link Tree} {@code t}
+ * corresponds.
+ */
+ public Node getNodeCorrespondingToTree(Tree t) {
+ if (convertedTreeLookup.containsKey(t)) {
+ return convertedTreeLookup.get(t);
+ } else {
+ return treeLookup.get(t);
+ }
+ }
+
+ /** @return the entry block of the control flow graph. */
+ public SpecialBlock getEntryBlock() {
+ return entryBlock;
+ }
+
+ public List<ReturnNode> getReturnNodes() {
+ return returnNodes;
+ }
+
+ public SpecialBlock getRegularExitBlock() {
+ return regularExitBlock;
+ }
+
+ public SpecialBlock getExceptionalExitBlock() {
+ return exceptionalExitBlock;
+ }
+
+ /** @return the AST this CFG corresponds to. */
+ public UnderlyingAST getUnderlyingAST() {
+ return underlyingAST;
+ }
+
+ /**
+ * @return the set of all basic block in this control flow graph
+ */
+ public Set<Block> getAllBlocks() {
+ Set<Block> visited = new HashSet<>();
+ Queue<Block> worklist = new LinkedList<>();
+ Block cur = entryBlock;
+ visited.add(entryBlock);
+
+ // traverse the whole control flow graph
+ while (true) {
+ if (cur == null) {
+ break;
+ }
+
+ Queue<Block> succs = new LinkedList<>();
+ if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
+ ConditionalBlock ccur = ((ConditionalBlock) cur);
+ succs.add(ccur.getThenSuccessor());
+ succs.add(ccur.getElseSuccessor());
+ } else {
+ assert cur instanceof SingleSuccessorBlock;
+ Block b = ((SingleSuccessorBlock) cur).getSuccessor();
+ if (b != null) {
+ succs.add(b);
+ }
+ }
+
+ if (cur.getType() == BlockType.EXCEPTION_BLOCK) {
+ ExceptionBlock ecur = (ExceptionBlock) cur;
+ for (Set<Block> exceptionSuccSet : ecur.getExceptionalSuccessors().values()) {
+ succs.addAll(exceptionSuccSet);
+ }
+ }
+
+ for (Block b : succs) {
+ if (!visited.contains(b)) {
+ visited.add(b);
+ worklist.add(b);
+ }
+ }
+
+ cur = worklist.poll();
+ }
+
+ return visited;
+ }
+
+ /**
+ * @return the list of all basic block in this control flow graph
+ * in reversed depth-first postorder sequence.
+ *
+ * Blocks may appear more than once in the sequence.
+ */
+ public List<Block> getDepthFirstOrderedBlocks() {
+ List<Block> dfsOrderResult = new LinkedList<>();
+ Set<Block> visited = new HashSet<>();
+ Deque<Block> worklist = new LinkedList<>();
+ worklist.add(entryBlock);
+ while (!worklist.isEmpty()) {
+ Block cur = worklist.getLast();
+ if (visited.contains(cur)) {
+ dfsOrderResult.add(cur);
+ worklist.removeLast();
+ } else {
+ visited.add(cur);
+ Deque<Block> successors = getSuccessors(cur);
+ successors.removeAll(visited);
+ worklist.addAll(successors);
+ }
+ }
+
+ Collections.reverse(dfsOrderResult);
+ return dfsOrderResult;
+ }
+
+ /**
+ * Get a list of all successor Blocks for cur
+ * @return a Deque of successor Blocks
+ */
+ private Deque<Block> getSuccessors(Block cur) {
+ Deque<Block> succs = new LinkedList<>();
+ if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
+ ConditionalBlock ccur = ((ConditionalBlock) cur);
+ succs.add(ccur.getThenSuccessor());
+ succs.add(ccur.getElseSuccessor());
+ } else {
+ assert cur instanceof SingleSuccessorBlock;
+ Block b = ((SingleSuccessorBlock) cur).getSuccessor();
+ if (b != null) {
+ succs.add(b);
+ }
+ }
+
+ if (cur.getType() == BlockType.EXCEPTION_BLOCK) {
+ ExceptionBlock ecur = (ExceptionBlock) cur;
+ for (Set<Block> exceptionSuccSet : ecur.getExceptionalSuccessors().values()) {
+ succs.addAll(exceptionSuccSet);
+ }
+ }
+ return succs;
+ }
+
+ /**
+ * @return the tree-lookup map
+ */
+ public IdentityHashMap<Tree, Node> getTreeLookup() {
+ return new IdentityHashMap<>(treeLookup);
+ }
+
+ /**
+ * Get the {@link MethodTree} of the CFG if the argument {@link Tree} maps
+ * to a {@link Node} in the CFG or null otherwise.
+ */
+ public /*@Nullable*/ MethodTree getContainingMethod(Tree t) {
+ if (treeLookup.containsKey(t)) {
+ if (underlyingAST.getKind() == UnderlyingAST.Kind.METHOD) {
+ UnderlyingAST.CFGMethod cfgMethod = (UnderlyingAST.CFGMethod) underlyingAST;
+ return cfgMethod.getMethod();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the {@link ClassTree} of the CFG if the argument {@link Tree} maps
+ * to a {@link Node} in the CFG or null otherwise.
+ */
+ public /*@Nullable*/ ClassTree getContainingClass(Tree t) {
+ if (treeLookup.containsKey(t)) {
+ if (underlyingAST.getKind() == UnderlyingAST.Kind.METHOD) {
+ UnderlyingAST.CFGMethod cfgMethod = (UnderlyingAST.CFGMethod) underlyingAST;
+ return cfgMethod.getClassTree();
+ }
+ }
+ return null;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/DOTCFGVisualizer.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/DOTCFGVisualizer.java
new file mode 100644
index 0000000000..3810b30765
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/DOTCFGVisualizer.java
@@ -0,0 +1,508 @@
+package org.checkerframework.dataflow.cfg;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.analysis.AbstractValue;
+import org.checkerframework.dataflow.analysis.Analysis;
+import org.checkerframework.dataflow.analysis.FlowExpressions;
+import org.checkerframework.dataflow.analysis.Store;
+import org.checkerframework.dataflow.analysis.TransferFunction;
+import org.checkerframework.dataflow.analysis.TransferInput;
+import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGMethod;
+import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGStatement;
+import org.checkerframework.dataflow.cfg.block.Block;
+import org.checkerframework.dataflow.cfg.block.Block.BlockType;
+import org.checkerframework.dataflow.cfg.block.ConditionalBlock;
+import org.checkerframework.dataflow.cfg.block.ExceptionBlock;
+import org.checkerframework.dataflow.cfg.block.RegularBlock;
+import org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock;
+import org.checkerframework.dataflow.cfg.block.SpecialBlock;
+import org.checkerframework.dataflow.cfg.node.Node;
+import org.checkerframework.javacutil.ErrorReporter;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+
+import javax.lang.model.type.TypeMirror;
+
+import com.sun.tools.javac.tree.JCTree;
+
+/**
+ * Generate a graph description in the DOT language of a control graph.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class DOTCFGVisualizer<A extends AbstractValue<A>,
+ S extends Store<S>, T extends TransferFunction<A, S>>
+ implements CFGVisualizer<A, S, T> {
+
+ protected String outdir;
+ protected boolean verbose;
+ protected String checkerName;
+
+ protected StringBuilder sbDigraph;
+ protected StringBuilder sbStore;
+ protected StringBuilder sbBlock;
+
+ /** Mapping from class/method representation to generated dot file. */
+ protected Map<String, String> generated;
+
+ public void init(Map<String, Object> args) {
+ this.outdir = (String) args.get("outdir");
+ {
+ Object verb = args.get("verbose");
+ this.verbose = verb == null ? false :
+ verb instanceof String ? Boolean.getBoolean((String) verb) :
+ (boolean) verb;
+ }
+ this.checkerName = (String) args.get("checkerName");
+
+ this.generated = new HashMap<>();
+
+ this.sbDigraph = new StringBuilder();
+
+ this.sbStore = new StringBuilder();
+
+ this.sbBlock = new StringBuilder();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public /*@Nullable*/ Map<String, Object> visualize(ControlFlowGraph cfg, Block entry,
+ /*@Nullable*/ Analysis<A, S, T> analysis) {
+
+ String dotgraph = generateDotGraph(cfg, entry, analysis);
+
+ String dotfilename = dotOutputFileName(cfg.underlyingAST);
+ // System.err.println("Output to DOT file: " + dotfilename);
+
+ try {
+ FileWriter fstream = new FileWriter(dotfilename);
+ BufferedWriter out = new BufferedWriter(fstream);
+ out.write(dotgraph);
+ out.close();
+ } catch (IOException e) {
+ ErrorReporter.errorAbort("Error creating dot file: " + dotfilename +
+ "; ensure the path is valid", e);
+ }
+
+ Map<String, Object> res = new HashMap<>();
+ res.put("dotFileName", dotfilename);
+
+ return res;
+ }
+
+ /**
+ * Generate the dot representation as String.
+ */
+ protected String generateDotGraph(ControlFlowGraph cfg, Block entry,
+ /*@Nullable*/ Analysis<A, S, T> analysis) {
+ this.sbDigraph.setLength(0);
+ Set<Block> visited = new HashSet<>();
+
+ // header
+ this.sbDigraph.append("digraph {\n");
+
+ Block cur = entry;
+ Queue<Block> worklist = new LinkedList<>();
+ visited.add(entry);
+ // traverse control flow graph and define all arrows
+ while (true) {
+ if (cur == null) {
+ break;
+ }
+
+ if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
+ ConditionalBlock ccur = ((ConditionalBlock) cur);
+ Block thenSuccessor = ccur.getThenSuccessor();
+ addDotEdge(ccur.getId(), thenSuccessor.getId(), "then\\n" + ccur.getThenFlowRule());
+ if (!visited.contains(thenSuccessor)) {
+ visited.add(thenSuccessor);
+ worklist.add(thenSuccessor);
+ }
+ Block elseSuccessor = ccur.getElseSuccessor();
+ addDotEdge(ccur.getId(), elseSuccessor.getId(), "else\\n" + ccur.getElseFlowRule());
+ if (!visited.contains(elseSuccessor)) {
+ visited.add(elseSuccessor);
+ worklist.add(elseSuccessor);
+ }
+ } else {
+ assert cur instanceof SingleSuccessorBlock;
+ Block b = ((SingleSuccessorBlock) cur).getSuccessor();
+ if (b != null) {
+ addDotEdge(cur.getId(), b.getId(), ((SingleSuccessorBlock) cur).getFlowRule().name());
+ if (!visited.contains(b)) {
+ visited.add(b);
+ worklist.add(b);
+ }
+ }
+ }
+
+ // exceptional edges
+ if (cur.getType() == BlockType.EXCEPTION_BLOCK) {
+ ExceptionBlock ecur = (ExceptionBlock) cur;
+ for (Entry<TypeMirror, Set<Block>> e : ecur
+ .getExceptionalSuccessors().entrySet()) {
+ Set<Block> blocks = e.getValue();
+ TypeMirror cause = e.getKey();
+ String exception = cause.toString();
+ if (exception.startsWith("java.lang.")) {
+ exception = exception.replace("java.lang.", "");
+ }
+
+ for (Block b : blocks) {
+ addDotEdge(cur.getId(), b.getId(), exception);
+ if (!visited.contains(b)) {
+ visited.add(b);
+ worklist.add(b);
+ }
+ }
+ }
+ }
+
+ cur = worklist.poll();
+ }
+
+ generateDotNodes(visited, cfg, analysis);
+
+ // footer
+ this.sbDigraph.append("}\n");
+
+ return this.sbDigraph.toString();
+ }
+
+ protected void generateDotNodes(Set<Block> visited, ControlFlowGraph cfg, Analysis<A, S, T> analysis) {
+ IdentityHashMap<Block, List<Integer>> processOrder = getProcessOrder(cfg);
+ this.sbDigraph.append(" node [shape=rectangle];\n\n");
+ // definition of all nodes including their labels
+ for (Block v : visited) {
+ this.sbDigraph.append(" " + v.getId() + " [");
+ if (v.getType() == BlockType.CONDITIONAL_BLOCK) {
+ this.sbDigraph.append("shape=polygon sides=8 ");
+ } else if (v.getType() == BlockType.SPECIAL_BLOCK) {
+ this.sbDigraph.append("shape=oval ");
+ }
+ this.sbDigraph.append("label=\"");
+ if (verbose) {
+ this.sbDigraph.append("Process order: " + processOrder.get(v).toString().replaceAll("[\\[\\]]", "") + "\\n");
+ }
+ visualizeBlock(v, analysis);
+ }
+
+ this.sbDigraph.append("\n");
+ }
+
+ /** @return the file name used for DOT output. */
+ protected String dotOutputFileName(UnderlyingAST ast) {
+ StringBuilder srcloc = new StringBuilder();
+
+ StringBuilder outfile = new StringBuilder(outdir);
+ outfile.append('/');
+ if (ast.getKind() == UnderlyingAST.Kind.ARBITRARY_CODE) {
+ CFGStatement cfgs = (CFGStatement) ast;
+ String clsname = cfgs.getClassTree().getSimpleName().toString();
+ outfile.append(clsname);
+ outfile.append("-initializer-");
+ outfile.append(ast.hashCode());
+
+ srcloc.append('<');
+ srcloc.append(clsname);
+ srcloc.append("::initializer::");
+ srcloc.append(((JCTree)cfgs.getCode()).pos);
+ srcloc.append('>');
+ } else if (ast.getKind() == UnderlyingAST.Kind.METHOD) {
+ CFGMethod cfgm = (CFGMethod) ast;
+ String clsname = cfgm.getClassTree().getSimpleName().toString();
+ String methname = cfgm.getMethod().getName().toString();
+ outfile.append(clsname);
+ outfile.append('-');
+ outfile.append(methname);
+
+ srcloc.append('<');
+ srcloc.append(clsname);
+ srcloc.append("::");
+ srcloc.append(methname);
+ srcloc.append('(');
+ srcloc.append(cfgm.getMethod().getParameters());
+ srcloc.append(")::");
+ srcloc.append(((JCTree)cfgm.getMethod()).pos);
+ srcloc.append('>');
+ } else {
+ ErrorReporter.errorAbort("Unexpected AST kind: " + ast.getKind() +
+ " value: " + ast.toString());
+ return null;
+ }
+ outfile.append('-');
+ outfile.append(checkerName);
+ outfile.append(".dot");
+
+ // make path safe for Windows
+ String out = outfile.toString().replace("<", "_").replace(">", "");
+
+ generated.put(srcloc.toString(), out);
+
+ return out;
+ }
+
+ protected IdentityHashMap<Block, List<Integer>> getProcessOrder(ControlFlowGraph cfg) {
+ IdentityHashMap<Block, List<Integer>> depthFirstOrder = new IdentityHashMap<>();
+ int count = 1;
+ for (Block b : cfg.getDepthFirstOrderedBlocks()) {
+ if (depthFirstOrder.get(b) == null) {
+ depthFirstOrder.put(b, new ArrayList<Integer>());
+ }
+ depthFirstOrder.get(b).add(count++);
+ }
+ return depthFirstOrder;
+ }
+
+ /**
+ * Produce a representation of the contests of a basic block.
+ *
+ * @param bb basic block to visualize
+ */
+ @Override
+ public void visualizeBlock(Block bb,
+ /*@Nullable*/ Analysis<A, S, T> analysis) {
+
+ this.sbBlock.setLength(0);
+
+ // loop over contents
+ List<Node> contents = new LinkedList<>();
+ switch (bb.getType()) {
+ case REGULAR_BLOCK:
+ contents.addAll(((RegularBlock) bb).getContents());
+ break;
+ case EXCEPTION_BLOCK:
+ contents.add(((ExceptionBlock) bb).getNode());
+ break;
+ case CONDITIONAL_BLOCK:
+ break;
+ case SPECIAL_BLOCK:
+ break;
+ default:
+ assert false : "All types of basic blocks covered";
+ }
+ boolean notFirst = false;
+ for (Node t : contents) {
+ if (notFirst) {
+ this.sbBlock.append("\\n");
+ }
+ notFirst = true;
+ visualizeBlockNode(t, analysis);
+ }
+
+ // handle case where no contents are present
+ boolean centered = false;
+ if (this.sbBlock.length() == 0) {
+ centered = true;
+ if (bb.getType() == BlockType.SPECIAL_BLOCK) {
+ visualizeSpecialBlock((SpecialBlock) bb);
+ } else if (bb.getType() == BlockType.CONDITIONAL_BLOCK) {
+ this.sbDigraph.append(" \",];\n");
+ return;
+ } else {
+ this.sbDigraph.append("?? empty ?? \",];\n");
+ return;
+ }
+ }
+
+ // visualize transfer input if necessary
+ if (analysis != null) {
+ visualizeBlockTransferInput(bb, analysis);
+ }
+
+ this.sbDigraph.append((this.sbBlock.toString() + (centered ? "" : "\\n")).replace("\\n", "\\l") + " \",];\n");
+ }
+
+ @Override
+ public void visualizeSpecialBlock(SpecialBlock sbb) {
+ switch (sbb.getSpecialType()) {
+ case ENTRY:
+ this.sbBlock.append("<entry>");
+ break;
+ case EXIT:
+ this.sbBlock.append("<exit>");
+ break;
+ case EXCEPTIONAL_EXIT:
+ this.sbBlock.append("<exceptional-exit>");
+ break;
+ }
+ }
+
+ @Override
+ public void visualizeBlockTransferInput(Block bb, Analysis<A, S, T> analysis) {
+ TransferInput<A, S> input = analysis.getInput(bb);
+ this.sbStore.setLength(0);
+
+ // split input representation to two lines
+ this.sbStore.append("Before:");
+ S thenStore = input.getThenStore();
+ if (thenStore == null) {
+ S regularStore = input.getRegularStore();
+ this.sbStore.append('[');
+ visualizeStore(regularStore);
+ this.sbStore.append(']');
+ } else {
+ S elseStore = input.getElseStore();
+ this.sbStore.append("[then=");
+ visualizeStore(thenStore);
+ this.sbStore.append(", else=");
+ visualizeStore(elseStore);
+ this.sbStore.append("]");
+ }
+ // separator
+ this.sbStore.append("\\n~~~~~~~~~\\n");
+
+ // the transfer input before this block is added before the block content
+ this.sbBlock.insert(0, this.sbStore);
+
+ if (verbose) {
+ Node lastNode;
+ switch (bb.getType()) {
+ case REGULAR_BLOCK:
+ List<Node> blockContents = ((RegularBlock) bb).getContents();
+ lastNode = blockContents.get(blockContents.size() - 1);
+ break;
+ case EXCEPTION_BLOCK:
+ lastNode = ((ExceptionBlock) bb).getNode();
+ break;
+ default:
+ lastNode = null;
+ }
+ if (lastNode != null) {
+ this.sbStore.setLength(0);
+ this.sbStore.append("\\n~~~~~~~~~\\n");
+ this.sbStore.append("After:");
+ visualizeStore(analysis.getResult().getStoreAfter(lastNode.getTree()));
+ this.sbBlock.append(this.sbStore);
+ }
+ }
+ }
+
+ @Override
+ public void visualizeBlockNode(Node t, /*@Nullable*/ Analysis<A, S, T> analysis) {
+ A value = analysis.getValue(t);
+ String valueInfo = "";
+ if (value != null) {
+ valueInfo = " > " + prepareString(value.toString());
+ }
+ this.sbBlock.append(prepareString(t.toString()) + " [ " + prepareNodeType(t) + " ]" + valueInfo);
+ }
+
+ protected String prepareNodeType(Node t) {
+ String name = t.getClass().getSimpleName();
+ return name.replace("Node", "");
+ }
+
+ protected String prepareString(String s) {
+ return s.replace("\"", "\\\"");
+ }
+
+ protected void addDotEdge(long sId, long eId, String labelContent) {
+ this.sbDigraph.append(" " + sId + " -> "+ eId + " [label=\""+ labelContent + "\"];\n");
+ }
+
+ @Override
+ public void visualizeStore(S store) {
+ store.visualize(this);
+ }
+
+ @Override
+ public void visualizeStoreThisVal(A value) {
+ this.sbStore.append(" this > " + value
+ + "\\n");
+ }
+
+ @Override
+ public void visualizeStoreLocalVar(FlowExpressions.LocalVariable localVar, A value) {
+ this.sbStore.append(" " + localVar + " > " +
+ toStringEscapeDoubleQuotes(value)
+ + "\\n");
+ }
+
+ @Override
+ public void visualizeStoreFieldVals(FlowExpressions.FieldAccess fieldAccess, A value) {
+ this.sbStore.append(" " + fieldAccess + " > " +
+ toStringEscapeDoubleQuotes(value)
+ + "\\n");
+ }
+
+ @Override
+ public void visualizeStoreArrayVal(FlowExpressions.ArrayAccess arrayValue, A value) {
+ this.sbStore.append(" " + arrayValue + " > " +
+ toStringEscapeDoubleQuotes(value) + "\\n");
+ }
+
+ @Override
+ public void visualizeStoreMethodVals(FlowExpressions.MethodCall methodCall, A value) {
+ this.sbStore.append(" " + methodCall.toString().replace("\"", "\\\"") + " > " +
+ value + "\\n");
+ }
+
+ @Override
+ public void visualizeStoreClassVals(FlowExpressions.ClassName className, A value) {
+ this.sbStore.append(" " + className + " > " + toStringEscapeDoubleQuotes(value) + "\\n");
+ }
+
+ @Override
+ public void visualizeStoreKeyVal(String keyName, Object value) {
+ this.sbStore.append(" "+keyName+" = "+value+"\\n");
+ }
+
+ protected String escapeDoubleQuotes(final String str) {
+ return str.replace("\"", "\\\"");
+ }
+
+ protected String toStringEscapeDoubleQuotes(final Object obj) {
+ return escapeDoubleQuotes(String.valueOf(obj));
+ }
+
+ @Override
+ public void visualizeStoreHeader(String classCanonicalName) {
+ this.sbStore.append(classCanonicalName + " (\\n");
+ }
+
+ @Override
+ public void visualizeStoreFooter() {
+ this.sbStore.append(")");
+ }
+
+ /**
+ * Write a file {@code methods.txt} that contains a mapping from
+ * source code location to generated dot file.
+ */
+ @Override
+ public void shutdown() {
+ try {
+ // Open for append, in case of multiple sub-checkers.
+ FileWriter fstream = new FileWriter(outdir + "/methods.txt", true);
+ BufferedWriter out = new BufferedWriter(fstream);
+ for (Map.Entry<String, String> kv : generated.entrySet()) {
+ out.write(kv.getKey());
+ out.append('\t');
+ out.write(kv.getValue());
+ out.append('\n');
+ }
+ out.close();
+ } catch (IOException e) {
+ ErrorReporter.errorAbort("Error creating methods.txt file in: " + outdir +
+ "; ensure the path is valid", e);
+ }
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/JavaSource2CFGDOT.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/JavaSource2CFGDOT.java
new file mode 100644
index 0000000000..241c087cbc
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/JavaSource2CFGDOT.java
@@ -0,0 +1,275 @@
+package org.checkerframework.dataflow.cfg;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.analysis.AbstractValue;
+import org.checkerframework.dataflow.analysis.Analysis;
+import org.checkerframework.dataflow.analysis.Store;
+import org.checkerframework.dataflow.analysis.TransferFunction;
+import org.checkerframework.javacutil.BasicTypeProcessor;
+import org.checkerframework.javacutil.TreeUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.lang.model.element.ExecutableElement;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.xml.ws.Holder;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.util.TreePathScanner;
+import com.sun.tools.javac.file.JavacFileManager;
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+
+/**
+ * Class to generate the DOT representation of the control flow graph of a given
+ * method.
+ *
+ * @author Stefan Heule
+ */
+public class JavaSource2CFGDOT {
+
+ /** Main method. */
+ public static void main(String[] args) {
+ if (args.length < 2) {
+ printUsage();
+ System.exit(1);
+ }
+ String input = args[0];
+ String output = args[1];
+ File file = new File(input);
+ if (!file.canRead()) {
+ printError("Cannot read input file: " + file.getAbsolutePath());
+ printUsage();
+ System.exit(1);
+ }
+
+ String method = "test";
+ String clas = "Test";
+ boolean pdf = false;
+ boolean error = false;
+
+ for (int i = 2; i < args.length; i++) {
+ if (args[i].equals("-pdf")) {
+ pdf = true;
+ } else if (args[i].equals("-method")) {
+ if (i >= args.length - 1) {
+ printError("Did not find <name> after -method.");
+ continue;
+ }
+ i++;
+ method = args[i];
+ } else if (args[i].equals("-class")) {
+ if (i >= args.length - 1) {
+ printError("Did not find <name> after -class.");
+ continue;
+ }
+ i++;
+ clas = args[i];
+ } else {
+ printError("Unknown command line argument: " + args[i]);
+ error = true;
+ }
+ }
+
+ if (error) {
+ System.exit(1);
+ }
+
+ generateDOTofCFG(input, output, method, clas, pdf);
+ }
+
+ /** Print an error message. */
+ protected static void printError(String string) {
+ System.err.println("ERROR: " + string);
+ }
+
+ /** Print usage information. */
+ protected static void printUsage() {
+ System.out
+ .println("Generate the control flow graph of a Java method, represented as a DOT graph.");
+ System.out
+ .println("Parameters: <inputfile> <outputdir> [-method <name>] [-class <name>] [-pdf]");
+ System.out
+ .println(" -pdf: Also generate the PDF by invoking 'dot'.");
+ System.out
+ .println(" -method: The method to generate the CFG for (defaults to 'test').");
+ System.out
+ .println(" -class: The class in which to find the method (defaults to 'Test').");
+ }
+
+ /** Just like method above but without analysis. */
+ public static void generateDOTofCFG(String inputFile, String outputDir,
+ String method, String clas, boolean pdf) {
+ generateDOTofCFG(inputFile, outputDir, method, clas, pdf, null);
+ }
+
+ /**
+ * Generate the DOT representation of the CFG for a method.
+ *
+ * @param inputFile
+ * Java source input file.
+ * @param outputDir
+ * Source output directory.
+ * @param method
+ * Method name to generate the CFG for.
+ * @param pdf
+ * Also generate a PDF?
+ * @param analysis
+ * Analysis to perform befor the visualization (or
+ * {@code null} if no analysis is to be performed).
+ */
+ public static
+ <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>>
+ void generateDOTofCFG(
+ String inputFile, String outputDir, String method, String clas,
+ boolean pdf, /*@Nullable*/ Analysis<A, S, T> analysis) {
+ Entry<MethodTree, CompilationUnitTree> m = getMethodTreeAndCompilationUnit(inputFile, method, clas);
+ generateDOTofCFG(inputFile, outputDir, method, clas, pdf, analysis, m.getKey(), m.getValue());
+ }
+
+ public static
+ <A extends AbstractValue<A>, S extends Store<S>, T extends TransferFunction<A, S>>
+ void generateDOTofCFG(
+ String inputFile, String outputDir, String method, String clas,
+ boolean pdf, /*@Nullable*/ Analysis<A, S, T> analysis, MethodTree m,
+ CompilationUnitTree r) {
+ String fileName = (new File(inputFile)).getName();
+ System.out.println("Working on " + fileName + "...");
+
+ if (m == null) {
+ printError("Method not found.");
+ System.exit(1);
+ }
+
+ ControlFlowGraph cfg = CFGBuilder.build(r, null, m, null);
+ if (analysis != null) {
+ analysis.performAnalysis(cfg);
+ }
+
+ Map<String, Object> args = new HashMap<>();
+ args.put("outdir", outputDir);
+ args.put("checkerName", "");
+
+ CFGVisualizer<A, S, T> viz = new DOTCFGVisualizer<A, S, T>();
+ viz.init(args);
+ Map<String, Object> res = viz.visualize(cfg, cfg.getEntryBlock(), analysis);
+ viz.shutdown();
+
+ if (pdf) {
+ producePDF((String) res.get("dotFileName"));
+ }
+ }
+
+ /**
+ * Invoke DOT to generate a PDF.
+ */
+ protected static void producePDF(String file) {
+ try {
+ String command = "dot -Tpdf \"" + file + ".txt\" -o \"" + file
+ + ".pdf\"";
+ Process child = Runtime.getRuntime().exec(command);
+ child.waitFor();
+ } catch (InterruptedException | IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ /**
+ * @return the AST of a specific method in a specific class in a specific
+ * file (or null if no such method exists)
+ */
+ public static /*@Nullable*/ MethodTree getMethodTree(String file,
+ final String method, String clas) {
+ return getMethodTreeAndCompilationUnit(file, method, clas).getKey();
+ }
+
+ /**
+ * @return the AST of a specific method in a specific class as well as the
+ * {@link CompilationUnitTree} in a specific file (or null they do
+ * not exist).
+ */
+ public static Entry</*@Nullable*/ MethodTree, /*@Nullable*/ CompilationUnitTree> getMethodTreeAndCompilationUnit(
+ String file, final String method, String clas) {
+ final Holder<MethodTree> m = new Holder<>();
+ final Holder<CompilationUnitTree> c = new Holder<>();
+ BasicTypeProcessor typeProcessor = new BasicTypeProcessor() {
+ @Override
+ protected TreePathScanner<?, ?> createTreePathScanner(
+ CompilationUnitTree root) {
+ c.value = root;
+ return new TreePathScanner<Void, Void>() {
+ @Override
+ public Void visitMethod(MethodTree node, Void p) {
+ ExecutableElement el = TreeUtils
+ .elementFromDeclaration(node);
+ if (el.getSimpleName().contentEquals(method)) {
+ m.value = node;
+ // stop execution by throwing an exception. this
+ // makes sure that compilation does not proceed, and
+ // thus the AST is not modified by further phases of
+ // the compilation (and we save the work to do the
+ // compilation).
+ throw new RuntimeException();
+ }
+ return null;
+ }
+ };
+ }
+ };
+
+ Context context = new Context();
+ JavaCompiler javac = new JavaCompiler(context);
+ javac.attrParseOnly = true;
+ JavacFileManager fileManager = (JavacFileManager) context
+ .get(JavaFileManager.class);
+
+ JavaFileObject l = fileManager
+ .getJavaFileObjectsFromStrings(List.of(file)).iterator().next();
+
+ PrintStream err = System.err;
+ try {
+ // redirect syserr to nothing (and prevent the compiler from issuing
+ // warnings about our exception.
+ System.setErr(new PrintStream(new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ }
+ }));
+ javac.compile(List.of(l), List.of(clas), List.of(typeProcessor));
+ } catch (Throwable e) {
+ // ok
+ } finally {
+ System.setErr(err);
+ }
+ return new Entry<MethodTree, CompilationUnitTree>() {
+ @Override
+ public CompilationUnitTree setValue(CompilationUnitTree value) {
+ return null;
+ }
+
+ @Override
+ public CompilationUnitTree getValue() {
+ return c.value;
+ }
+
+ @Override
+ public MethodTree getKey() {
+ return m.value;
+ }
+ };
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/UnderlyingAST.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/UnderlyingAST.java
new file mode 100644
index 0000000000..ce4c267832
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/UnderlyingAST.java
@@ -0,0 +1,136 @@
+package org.checkerframework.dataflow.cfg;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * Represents an abstract syntax tree of type {@link Tree} that underlies a
+ * given control flow graph.
+ *
+ * @author Stefan Heule
+ *
+ */
+public abstract class UnderlyingAST {
+ public enum Kind {
+ /** The underlying code is a whole method */
+ METHOD,
+ /** The underlying code is a lambda expression */
+ LAMBDA,
+
+ /**
+ * The underlying code is an arbitrary Java statement or expression
+ */
+ ARBITRARY_CODE,
+ }
+
+ protected final Kind kind;
+
+ public UnderlyingAST(Kind kind) {
+ this.kind = kind;
+ }
+
+ /**
+ * @return the code that corresponds to the CFG
+ */
+ abstract public Tree getCode();
+
+ public Kind getKind() {
+ return kind;
+ }
+
+ /**
+ * If the underlying AST is a method.
+ */
+ public static class CFGMethod extends UnderlyingAST {
+
+ /** The method declaration */
+ protected final MethodTree method;
+
+ /** The class tree this method belongs to. */
+ protected final ClassTree classTree;
+
+ public CFGMethod(MethodTree method, ClassTree classTree) {
+ super(Kind.METHOD);
+ this.method = method;
+ this.classTree = classTree;
+ }
+
+ @Override
+ public Tree getCode() {
+ return method.getBody();
+ }
+
+ public MethodTree getMethod() {
+ return method;
+ }
+
+ public ClassTree getClassTree() {
+ return classTree;
+ }
+
+ @Override
+ public String toString() {
+ return "CFGMethod(\n" + method + "\n)";
+ }
+ }
+
+ /**
+ * If the underlying AST is a lambda.
+ */
+ public static class CFGLambda extends UnderlyingAST {
+
+ private final LambdaExpressionTree lambda;
+
+ public CFGLambda(LambdaExpressionTree lambda) {
+ super(Kind.LAMBDA);
+ this.lambda = lambda;
+ }
+
+ @Override
+ public Tree getCode() {
+ return lambda.getBody();
+ }
+
+ public LambdaExpressionTree getLambdaTree() {
+ return lambda;
+ }
+
+ @Override
+ public String toString() {
+ return "CFGLambda(\n" + lambda + "\n)";
+ }
+ }
+
+ /**
+ * If the underlying AST is a statement or expression.
+ */
+ public static class CFGStatement extends UnderlyingAST {
+
+ protected final Tree code;
+
+ /** The class tree this method belongs to. */
+ protected final ClassTree classTree;
+
+ public CFGStatement(Tree code, ClassTree classTree) {
+ super(Kind.ARBITRARY_CODE);
+ this.code = code;
+ this.classTree = classTree;
+ }
+
+ @Override
+ public Tree getCode() {
+ return code;
+ }
+
+ public ClassTree getClassTree() {
+ return classTree;
+ }
+
+ @Override
+ public String toString() {
+ return "CFGStatement(\n" + code + "\n)";
+ }
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/Block.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/Block.java
new file mode 100644
index 0000000000..09aa2d4180
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/Block.java
@@ -0,0 +1,37 @@
+package org.checkerframework.dataflow.cfg.block;
+
+/**
+ * Represents a basic block in a control flow graph.
+ *
+ * @author Stefan Heule
+ *
+ */
+public interface Block {
+
+ /** The types of basic blocks */
+ public static enum BlockType {
+
+ /** A regular basic block. */
+ REGULAR_BLOCK,
+
+ /** A conditional basic block. */
+ CONDITIONAL_BLOCK,
+
+ /** A special basic block. */
+ SPECIAL_BLOCK,
+
+ /** A basic block that can throw an exception. */
+ EXCEPTION_BLOCK,
+ }
+
+ /**
+ * @return the type of this basic block
+ */
+ BlockType getType();
+
+ /**
+ * @return the unique identifier of this block
+ */
+ long getId();
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/BlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/BlockImpl.java
new file mode 100644
index 0000000000..568b62b071
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/BlockImpl.java
@@ -0,0 +1,63 @@
+package org.checkerframework.dataflow.cfg.block;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Base class of the {@link Block} implementation hierarchy.
+ *
+ * @author Stefan Heule
+ *
+ */
+public abstract class BlockImpl implements Block {
+
+ /** A unique ID for this node. */
+ protected long id = BlockImpl.uniqueID();
+
+ /** The last ID that has already been used. */
+ protected static long lastId = 0;
+
+ /** The type of this basic block. */
+ protected BlockType type;
+
+ /** The set of predecessors. */
+ protected Set<BlockImpl> predecessors;
+
+ /**
+ * @return a fresh identifier
+ */
+ private static long uniqueID() {
+ return lastId++;
+ }
+
+ public BlockImpl() {
+ predecessors = new HashSet<>();
+ }
+
+ @Override
+ public long getId() {
+ return id;
+ }
+
+ @Override
+ public BlockType getType() {
+ return type;
+ }
+
+ /**
+ * @return the list of predecessors of this basic block
+ */
+ public Set<BlockImpl> getPredecessors() {
+ return Collections.unmodifiableSet(predecessors);
+ }
+
+ public void addPredecessor(BlockImpl pred) {
+ predecessors.add(pred);
+ }
+
+ public void removePredecessor(BlockImpl pred) {
+ predecessors.remove(pred);
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlock.java
new file mode 100644
index 0000000000..2f1621ef2f
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlock.java
@@ -0,0 +1,48 @@
+package org.checkerframework.dataflow.cfg.block;
+
+import org.checkerframework.dataflow.analysis.Store;
+import org.checkerframework.dataflow.cfg.node.Node;
+
+/**
+ * Represents a conditional basic block that contains exactly one boolean
+ * {@link Node}.
+ *
+ * @author Stefan Heule
+ *
+ */
+public interface ConditionalBlock extends Block {
+
+ /**
+ * @return the entry block of the then branch
+ */
+ Block getThenSuccessor();
+
+ /**
+ * @return the entry block of the else branch
+ */
+ Block getElseSuccessor();
+
+ /**
+ * @return the flow rule for information flowing from
+ * this block to its then successor
+ */
+ Store.FlowRule getThenFlowRule();
+
+ /**
+ * @return the flow rule for information flowing from
+ * this block to its else successor
+ */
+ Store.FlowRule getElseFlowRule();
+
+ /**
+ * Set the flow rule for information flowing from this block to
+ * its then successor.
+ */
+ void setThenFlowRule(Store.FlowRule rule);
+
+ /**
+ * Set the flow rule for information flowing from this block to
+ * its else successor.
+ */
+ void setElseFlowRule(Store.FlowRule rule);
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlockImpl.java
new file mode 100644
index 0000000000..15c3842bad
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ConditionalBlockImpl.java
@@ -0,0 +1,87 @@
+package org.checkerframework.dataflow.cfg.block;
+
+import org.checkerframework.dataflow.analysis.Store;
+
+/**
+ * Implementation of a conditional basic block.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class ConditionalBlockImpl extends BlockImpl implements ConditionalBlock {
+
+ /** Successor of the then branch. */
+ protected BlockImpl thenSuccessor;
+
+ /** Successor of the else branch. */
+ protected BlockImpl elseSuccessor;
+
+ /**
+ * The rules below say that the THEN store before a conditional
+ * block flows to BOTH of the stores of the then successor, while
+ * the ELSE store before a conditional block flows to BOTH of the
+ * stores of the else successor.
+ */
+ protected Store.FlowRule thenFlowRule = Store.FlowRule.THEN_TO_BOTH;
+
+ protected Store.FlowRule elseFlowRule = Store.FlowRule.ELSE_TO_BOTH;
+
+ /**
+ * Initialize an empty conditional basic block to be filled with contents
+ * and linked to other basic blocks later.
+ */
+ public ConditionalBlockImpl() {
+ type = BlockType.CONDITIONAL_BLOCK;
+ }
+
+ /**
+ * Set the then branch successor.
+ */
+ public void setThenSuccessor(BlockImpl b) {
+ thenSuccessor = b;
+ b.addPredecessor(this);
+ }
+
+ /**
+ * Set the else branch successor.
+ */
+ public void setElseSuccessor(BlockImpl b) {
+ elseSuccessor = b;
+ b.addPredecessor(this);
+ }
+
+ @Override
+ public Block getThenSuccessor() {
+ return thenSuccessor;
+ }
+
+ @Override
+ public Block getElseSuccessor() {
+ return elseSuccessor;
+ }
+
+ @Override
+ public Store.FlowRule getThenFlowRule() {
+ return thenFlowRule;
+ }
+
+ @Override
+ public Store.FlowRule getElseFlowRule() {
+ return elseFlowRule;
+ }
+
+ @Override
+ public void setThenFlowRule(Store.FlowRule rule) {
+ thenFlowRule = rule;
+ }
+
+ @Override
+ public void setElseFlowRule(Store.FlowRule rule) {
+ elseFlowRule = rule;
+ }
+
+ @Override
+ public String toString() {
+ return "ConditionalBlock()";
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlock.java
new file mode 100644
index 0000000000..da024d12ef
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlock.java
@@ -0,0 +1,38 @@
+package org.checkerframework.dataflow.cfg.block;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.cfg.node.Node;
+
+/**
+ * Represents a basic block that contains exactly one {@link Node} which can
+ * throw an exception. This block has exactly one non-exceptional successor, and
+ * one or more exceptional successors.
+ *
+ * <p>
+ *
+ * The following invariant holds.
+ *
+ * <pre>
+ * getNode().getBlock() == this
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public interface ExceptionBlock extends SingleSuccessorBlock {
+
+ /**
+ * @return the node of this block
+ */
+ Node getNode();
+
+ /**
+ * @return the list of exceptional successor blocks as an unmodifiable map
+ */
+ Map<TypeMirror, Set<Block>> getExceptionalSuccessors();
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlockImpl.java
new file mode 100644
index 0000000000..2148e060cd
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/ExceptionBlockImpl.java
@@ -0,0 +1,76 @@
+package org.checkerframework.dataflow.cfg.block;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.cfg.node.Node;
+
+/**
+ * Base class of the {@link Block} implementation hierarchy.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class ExceptionBlockImpl extends SingleSuccessorBlockImpl implements
+ ExceptionBlock {
+
+ /** Set of exceptional successors. */
+ protected Map<TypeMirror, Set<Block>> exceptionalSuccessors;
+
+ public ExceptionBlockImpl() {
+ type = BlockType.EXCEPTION_BLOCK;
+ exceptionalSuccessors = new HashMap<>();
+ }
+
+ /** The node of this block. */
+ protected Node node;
+
+ /**
+ * Set the node.
+ */
+ public void setNode(Node c) {
+ node = c;
+ c.setBlock(this);
+ }
+
+ @Override
+ public Node getNode() {
+ return node;
+ }
+
+ /**
+ * Add an exceptional successor.
+ */
+ public void addExceptionalSuccessor(BlockImpl b,
+ TypeMirror cause) {
+ if (exceptionalSuccessors == null) {
+ exceptionalSuccessors = new HashMap<>();
+ }
+ Set<Block> blocks = exceptionalSuccessors.get(cause);
+ if (blocks == null) {
+ blocks = new HashSet<Block>();
+ exceptionalSuccessors.put(cause, blocks);
+ }
+ blocks.add(b);
+ b.addPredecessor(this);
+ }
+
+ @Override
+ public Map<TypeMirror, Set<Block>> getExceptionalSuccessors() {
+ if (exceptionalSuccessors == null) {
+ return Collections.emptyMap();
+ }
+ return Collections.unmodifiableMap(exceptionalSuccessors);
+ }
+
+ @Override
+ public String toString() {
+ return "ExceptionBlock(" + node + ")";
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlock.java
new file mode 100644
index 0000000000..d29d7f1ec0
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlock.java
@@ -0,0 +1,38 @@
+package org.checkerframework.dataflow.cfg.block;
+
+import java.util.List;
+
+import org.checkerframework.dataflow.cfg.node.Node;
+
+/**
+ * A regular basic block that contains a sequence of {@link Node}s.
+ *
+ * <p>
+ *
+ * The following invariant holds.
+ *
+ * <pre>
+ * forall n in getContents() :: n.getBlock() == this
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public interface RegularBlock extends SingleSuccessorBlock {
+
+ /**
+ * @return the unmodifiable sequence of {@link Node}s.
+ */
+ List<Node> getContents();
+
+ /**
+ * @return the regular successor block
+ */
+ Block getRegularSuccessor();
+
+ /**
+ * Is this block empty (i.e., does it not contain any contents).
+ */
+ boolean isEmpty();
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlockImpl.java
new file mode 100644
index 0000000000..853f2c94f0
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/RegularBlockImpl.java
@@ -0,0 +1,67 @@
+package org.checkerframework.dataflow.cfg.block;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.checkerframework.dataflow.cfg.node.Node;
+
+/**
+ * Implementation of a regular basic block.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class RegularBlockImpl extends SingleSuccessorBlockImpl implements
+ RegularBlock {
+
+ /** Internal representation of the contents. */
+ protected List<Node> contents;
+
+ /**
+ * Initialize an empty basic block to be filled with contents and linked to
+ * other basic blocks later.
+ */
+ public RegularBlockImpl() {
+ contents = new LinkedList<>();
+ type = BlockType.REGULAR_BLOCK;
+ }
+
+ /**
+ * Add a node to the contents of this basic block.
+ */
+ public void addNode(Node t) {
+ contents.add(t);
+ t.setBlock(this);
+ }
+
+ /**
+ * Add multiple nodes to the contents of this basic block.
+ */
+ public void addNodes(List<? extends Node> ts) {
+ for (Node t : ts) {
+ addNode(t);
+ }
+ }
+
+ @Override
+ public List<Node> getContents() {
+ return Collections.unmodifiableList(contents);
+ }
+
+ @Override
+ public BlockImpl getRegularSuccessor() {
+ return successor;
+ }
+
+ @Override
+ public String toString() {
+ return "RegularBlock(" + contents + ")";
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return contents.isEmpty();
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlock.java
new file mode 100644
index 0000000000..9afd8fd9cd
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlock.java
@@ -0,0 +1,32 @@
+package org.checkerframework.dataflow.cfg.block;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.analysis.Store;
+
+/**
+ * A basic block that has at exactly one non-exceptional successor.
+ *
+ * @author Stefan Heule
+ *
+ */
+public interface SingleSuccessorBlock extends Block {
+
+ /**
+ * @return the non-exceptional successor block, or {@code null} if there is
+ * no successor.
+ */
+ /*@Nullable*/ Block getSuccessor();
+
+ /**
+ * @return the flow rule for information flowing from this block to its successor
+ */
+ Store.FlowRule getFlowRule();
+
+ /**
+ * Set the flow rule for information flowing from this block to its successor.
+ */
+ void setFlowRule(Store.FlowRule rule);
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlockImpl.java
new file mode 100644
index 0000000000..7e5988e2e7
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SingleSuccessorBlockImpl.java
@@ -0,0 +1,49 @@
+package org.checkerframework.dataflow.cfg.block;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.analysis.Store;
+
+/**
+ * Implementation of a non-special basic block.
+ *
+ * @author Stefan Heule
+ *
+ */
+public abstract class SingleSuccessorBlockImpl extends BlockImpl implements
+ SingleSuccessorBlock {
+
+ /** Internal representation of the successor. */
+ protected /*@Nullable*/ BlockImpl successor;
+
+ /**
+ * The rule below say that EACH store at the end of a single
+ * successor block flow to the corresponding store of the successor.
+ */
+ protected Store.FlowRule flowRule = Store.FlowRule.EACH_TO_EACH;
+
+ @Override
+ public /*@Nullable*/ Block getSuccessor() {
+ return successor;
+ }
+
+ /**
+ * Set a basic block as the successor of this block.
+ */
+ public void setSuccessor(BlockImpl successor) {
+ this.successor = successor;
+ successor.addPredecessor(this);
+ }
+
+ @Override
+ public Store.FlowRule getFlowRule() {
+ return flowRule;
+ }
+
+ @Override
+ public void setFlowRule(Store.FlowRule rule) {
+ flowRule = rule;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlock.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlock.java
new file mode 100644
index 0000000000..805a3b5135
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlock.java
@@ -0,0 +1,34 @@
+package org.checkerframework.dataflow.cfg.block;
+
+/**
+ * Represents a special basic block; i.e., one of the following:
+ * <ul>
+ * <li>Entry block of a method.</li>
+ * <li>Regular exit block of a method.</li>
+ * <li>Exceptional exit block of a method.</li>
+ * </ul>
+ *
+ * @author Stefan Heule
+ *
+ */
+public interface SpecialBlock extends SingleSuccessorBlock {
+
+ /** The types of special basic blocks */
+ public static enum SpecialBlockType {
+
+ /** The entry block of a method */
+ ENTRY,
+
+ /** The exit block of a method */
+ EXIT,
+
+ /** A special exit block of a method for exceptional termination */
+ EXCEPTIONAL_EXIT,
+ }
+
+ /**
+ * @return the type of this special basic block
+ */
+ SpecialBlockType getSpecialType();
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlockImpl.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlockImpl.java
new file mode 100644
index 0000000000..9b3f8fb8e3
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/block/SpecialBlockImpl.java
@@ -0,0 +1,24 @@
+package org.checkerframework.dataflow.cfg.block;
+
+public class SpecialBlockImpl extends SingleSuccessorBlockImpl implements
+ SpecialBlock {
+
+ /** The type of this special basic block. */
+ protected SpecialBlockType specialType;
+
+ public SpecialBlockImpl(SpecialBlockType type) {
+ this.specialType = type;
+ this.type = BlockType.SPECIAL_BLOCK;
+ }
+
+ @Override
+ public SpecialBlockType getSpecialType() {
+ return specialType;
+ }
+
+ @Override
+ public String toString() {
+ return "SpecialBlock(" + specialType + ")";
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java
new file mode 100644
index 0000000000..0f8a998efe
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AbstractNodeVisitor.java
@@ -0,0 +1,382 @@
+package org.checkerframework.dataflow.cfg.node;
+
+
+/**
+ * A default implementation of the node visitor interface. The class introduces
+ * several 'summary' methods, that can be overridden to change the behavior of
+ * several related visit methods at once. An example is the
+ * {@code visitValueLiteral} method, that is called for every
+ * {@link ValueLiteralNode}.
+ *
+ * <p>
+ *
+ * This is useful to implement a visitor that performs the same operation (e.g.,
+ * nothing) for most {@link Node}s and only has special behavior for a few.
+ *
+ * @author Stefan Heule
+ *
+ * @param <R>
+ * Return type of the visitor.
+ * @param <P>
+ * Parameter type of the visitor.
+ */
+public abstract class AbstractNodeVisitor<R, P> implements NodeVisitor<R, P> {
+
+ abstract public R visitNode(Node n, P p);
+
+ public R visitValueLiteral(ValueLiteralNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Literals
+ @Override
+ public R visitShortLiteral(ShortLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ @Override
+ public R visitIntegerLiteral(IntegerLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ @Override
+ public R visitLongLiteral(LongLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ @Override
+ public R visitFloatLiteral(FloatLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ @Override
+ public R visitDoubleLiteral(DoubleLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ @Override
+ public R visitBooleanLiteral(BooleanLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ @Override
+ public R visitCharacterLiteral(CharacterLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ @Override
+ public R visitStringLiteral(StringLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ @Override
+ public R visitNullLiteral(NullLiteralNode n, P p) {
+ return visitValueLiteral(n, p);
+ }
+
+ // Unary operations
+ @Override
+ public R visitNumericalMinus(NumericalMinusNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitNumericalPlus(NumericalPlusNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitBitwiseComplement(BitwiseComplementNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitNullChk(NullChkNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Binary operations
+ @Override
+ public R visitStringConcatenate(StringConcatenateNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitNumericalAddition(NumericalAdditionNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitNumericalSubtraction(NumericalSubtractionNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitNumericalMultiplication(NumericalMultiplicationNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitIntegerDivision(IntegerDivisionNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitFloatingDivision(FloatingDivisionNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitIntegerRemainder(IntegerRemainderNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitFloatingRemainder(FloatingRemainderNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitLeftShift(LeftShiftNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitSignedRightShift(SignedRightShiftNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitUnsignedRightShift(UnsignedRightShiftNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitBitwiseAnd(BitwiseAndNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitBitwiseOr(BitwiseOrNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitBitwiseXor(BitwiseXorNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Compound assignments
+ @Override
+ public R visitStringConcatenateAssignment(
+ StringConcatenateAssignmentNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Comparison operations
+ @Override
+ public R visitLessThan(LessThanNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitLessThanOrEqual(LessThanOrEqualNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitGreaterThan(GreaterThanNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitGreaterThanOrEqual(GreaterThanOrEqualNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitEqualTo(EqualToNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitNotEqual(NotEqualNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Conditional operations
+ @Override
+ public R visitConditionalAnd(ConditionalAndNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitConditionalOr(ConditionalOrNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitConditionalNot(ConditionalNotNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitTernaryExpression(TernaryExpressionNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitAssignment(AssignmentNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitLocalVariable(LocalVariableNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitVariableDeclaration(VariableDeclarationNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitFieldAccess(FieldAccessNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitMethodAccess(MethodAccessNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitArrayAccess(ArrayAccessNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ public R visitThisLiteral(ThisLiteralNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitImplicitThisLiteral(ImplicitThisLiteralNode n, P p) {
+ return visitThisLiteral(n, p);
+ }
+
+ @Override
+ public R visitExplicitThisLiteral(ExplicitThisLiteralNode n, P p) {
+ return visitThisLiteral(n, p);
+ }
+
+ @Override
+ public R visitSuper(SuperNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitReturn(ReturnNode n, P p) {
+ return visitNode(n, p);
+ };
+
+ @Override
+ public R visitStringConversion(StringConversionNode n, P p) {
+ return visitNode(n, p);
+ };
+
+ @Override
+ public R visitNarrowingConversion(NarrowingConversionNode n, P p) {
+ return visitNode(n, p);
+ };
+
+ @Override
+ public R visitWideningConversion(WideningConversionNode n, P p) {
+ return visitNode(n, p);
+ };
+
+ @Override
+ public R visitInstanceOf(InstanceOfNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitTypeCast(TypeCastNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Statements
+ @Override
+ public R visitAssertionError(AssertionErrorNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitSynchronized(SynchronizedNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitThrow(ThrowNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Cases
+ @Override
+ public R visitCase(CaseNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Method and constructor invocations
+ @Override
+ public R visitMethodInvocation(MethodInvocationNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitObjectCreation(ObjectCreationNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitMemberReference(FunctionalInterfaceNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitArrayCreation(ArrayCreationNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Type, package and class names
+ @Override
+ public R visitArrayType(ArrayTypeNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitPrimitiveType(PrimitiveTypeNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitClassName(ClassNameNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ @Override
+ public R visitPackageName(PackageNameNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Parameterized types
+ @Override
+ public R visitParameterizedType(ParameterizedTypeNode n, P p) {
+ return visitNode(n, p);
+ }
+
+ // Marker nodes
+ @Override
+ public R visitMarker(MarkerNode n, P p) {
+ return visitNode(n, p);
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayAccessNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayAccessNode.java
new file mode 100644
index 0000000000..c51d7c1fbf
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayAccessNode.java
@@ -0,0 +1,87 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for an array access:
+ *
+ * <pre>
+ * <em>array ref</em> [ <em>index</em> ]
+ * </pre>
+ *
+ * We allow array accesses without corresponding AST {@link Tree}s.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+
+public class ArrayAccessNode extends Node {
+
+ protected Tree tree;
+ protected Node array;
+ protected Node index;
+
+ public ArrayAccessNode(Tree t, Node array, Node index) {
+ super(InternalUtils.typeOf(t));
+ assert t instanceof ArrayAccessTree;
+ this.tree = t;
+ this.array = array;
+ this.index = index;
+ }
+
+ public Node getArray() {
+ return array;
+ }
+
+ public Node getIndex() {
+ return index;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitArrayAccess(this, p);
+ }
+
+ @Override
+ public String toString() {
+ String base = getArray().toString() + "[" + getIndex().toString() + "]";
+ return base;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ArrayAccessNode)) {
+ return false;
+ }
+ ArrayAccessNode other = (ArrayAccessNode) obj;
+ return getArray().equals(other.getArray())
+ && getIndex().equals(other.getIndex());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getArray(), getIndex());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getArray());
+ list.add(getIndex());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayCreationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayCreationNode.java
new file mode 100644
index 0000000000..4af69077d3
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayCreationNode.java
@@ -0,0 +1,135 @@
+package org.checkerframework.dataflow.cfg.node;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.lang.model.type.TypeMirror;
+
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for new array creation
+ *
+ * <pre>
+ * <em>new type [1][2]</em>
+ * <em>new type [] = { expr1, expr2, ... }</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ArrayCreationNode extends Node {
+
+ // The tree is null when an array is created for
+ // variable arity method calls.
+ protected /*@Nullable*/ NewArrayTree tree;
+ protected List<Node> dimensions;
+ protected List<Node> initializers;
+
+ public ArrayCreationNode(/*@Nullable*/ NewArrayTree tree,
+ TypeMirror type,
+ List<Node> dimensions,
+ List<Node> initializers) {
+ super(type);
+ this.tree = tree;
+ this.dimensions = dimensions;
+ this.initializers = initializers;
+ }
+
+ public List<Node> getDimensions() {
+ return dimensions;
+ }
+
+ public Node getDimension(int i) {
+ return dimensions.get(i);
+ }
+
+ public List<Node> getInitializers() {
+ return initializers;
+ }
+
+ public Node getInitializer(int i) {
+ return initializers.get(i);
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitArrayCreation(this, p);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("new " + type);
+ if (!dimensions.isEmpty()) {
+ boolean needComma = false;
+ sb.append(" (");
+ for (Node dim : dimensions) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(dim);
+ needComma = true;
+ }
+ sb.append(")");
+ }
+ if (!initializers.isEmpty()) {
+ boolean needComma = false;
+ sb.append(" = {");
+ for (Node init : initializers) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(init);
+ needComma = true;
+ }
+ sb.append("}");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ArrayCreationNode)) {
+ return false;
+ }
+ ArrayCreationNode other = (ArrayCreationNode) obj;
+
+ return getDimensions().equals(other.getDimensions())
+ && getInitializers().equals(other.getInitializers());
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ for (Node dim : dimensions) {
+ hash = HashCodeUtils.hash(hash, dim.hashCode());
+ }
+ for (Node init : initializers) {
+ hash = HashCodeUtils.hash(hash, init.hashCode());
+ }
+ return hash;
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.addAll(dimensions);
+ list.addAll(initializers);
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayTypeNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayTypeNode.java
new file mode 100644
index 0000000000..d7e0cd7f82
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ArrayTypeNode.java
@@ -0,0 +1,65 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node representing a array type used in an expression
+ * such as a field access
+ *
+ * <em>type</em> .class
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ArrayTypeNode extends Node {
+
+ protected final ArrayTypeTree tree;
+
+ public ArrayTypeNode(ArrayTypeTree tree) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitArrayType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return tree.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ArrayTypeNode)) {
+ return false;
+ }
+ ArrayTypeNode other = (ArrayTypeNode) obj;
+ return getType().equals(other.getType());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getType());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssertionErrorNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssertionErrorNode.java
new file mode 100644
index 0000000000..efc066a5bf
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssertionErrorNode.java
@@ -0,0 +1,85 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the {@link AssertionError} when an assertion fails.
+ *
+ * <pre>
+ * assert <em>condition</em> : <em>detail</em> ;
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class AssertionErrorNode extends Node {
+
+ protected Tree tree;
+ protected Node condition;
+ protected Node detail;
+
+ public AssertionErrorNode(Tree tree, Node condition, Node detail, TypeMirror type) {
+ // TODO: Find out the correct "type" for statements.
+ // Is it TypeKind.NONE?
+ super(type);
+ assert tree.getKind() == Kind.ASSERT;
+ this.tree = tree;
+ this.condition = condition;
+ this.detail = detail;
+ }
+
+ public Node getCondition() {
+ return condition;
+ }
+
+ public Node getDetail() {
+ return detail;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitAssertionError(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "AssertionError(" + getDetail() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof AssertionErrorNode)) {
+ return false;
+ }
+ AssertionErrorNode other = (AssertionErrorNode) obj;
+ return getCondition().equals(other.getCondition()) &&
+ getDetail().equals(other.getDetail());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getCondition(), getDetail());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getCondition());
+ list.add(getDetail());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentContext.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentContext.java
new file mode 100644
index 0000000000..85d17e7c06
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentContext.java
@@ -0,0 +1,140 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+
+import org.checkerframework.javacutil.TreeUtils;
+
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+
+/**
+ * An assignment context for a node, which represents the place to which the
+ * node with this context is 'assigned' to. An 'assignment' (as we use the term
+ * here) can occur for Java assignments, method calls (for all the actual
+ * parameters which get assigned to their formal parameters) or method return
+ * statements.
+ *
+ * <p>
+ * The main use of {@link AssignmentContext} is to be able to get the declared
+ * type of the left-hand side of the assignment for proper type-refinement.
+ *
+ * @author Stefan Heule
+ */
+public abstract class AssignmentContext {
+
+ /**
+ * An assignment context for an assignment 'lhs = rhs'.
+ */
+ public static class AssignmentLhsContext extends AssignmentContext {
+
+ protected final Node node;
+
+ public AssignmentLhsContext(Node node) {
+ this.node = node;
+ }
+
+ @Override
+ public Element getElementForType() {
+ Tree tree = node.getTree();
+ if (tree == null) {
+ return null;
+ } else if (tree instanceof ExpressionTree) {
+ return TreeUtils.elementFromUse((ExpressionTree) tree);
+ } else if (tree instanceof VariableTree) {
+ return TreeUtils.elementFromDeclaration((VariableTree) tree);
+ } else {
+ assert false : "unexpected tree";
+ return null;
+ }
+ }
+
+ @Override
+ public Tree getContextTree() {
+ return node.getTree();
+ }
+ }
+
+ /**
+ * An assignment context for a method parameter.
+ */
+ public static class MethodParameterContext extends AssignmentContext {
+
+ protected final ExecutableElement method;
+ protected final int paramNum;
+
+ public MethodParameterContext(ExecutableElement method, int paramNum) {
+ this.method = method;
+ this.paramNum = paramNum;
+ }
+
+ @Override
+ public Element getElementForType() {
+ return method.getParameters().get(paramNum);
+ }
+
+ @Override
+ public Tree getContextTree() {
+ // TODO: what is the right assignment context? We might not have
+ // a tree for the invoked method.
+ return null;
+ }
+ }
+
+ /**
+ * An assignment context for method return statements.
+ */
+ public static class MethodReturnContext extends AssignmentContext {
+
+ protected final ExecutableElement method;
+ protected final Tree ret;
+
+ public MethodReturnContext(MethodTree method) {
+ this.method = TreeUtils.elementFromDeclaration(method);
+ this.ret = method.getReturnType();
+ }
+
+ @Override
+ public Element getElementForType() {
+ return method;
+ }
+
+ @Override
+ public Tree getContextTree() {
+ return ret;
+ }
+ }
+
+ /**
+ * An assignment context for lambda return statements.
+ */
+ public static class LambdaReturnContext extends AssignmentContext {
+
+ protected final ExecutableElement method;
+
+ public LambdaReturnContext(ExecutableElement method) {
+ this.method = method;
+ }
+
+ @Override
+ public Element getElementForType() {
+ return method;
+ }
+
+ @Override
+ public Tree getContextTree() {
+ // TODO: what is the right assignment context? We might not have
+ // a tree for the invoked method.
+ return null;
+ }
+ }
+
+ /**
+ * Returns an {@link Element} that has the type of this assignment context.
+ */
+ public abstract Element getElementForType();
+
+ public abstract Tree getContextTree();
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentNode.java
new file mode 100644
index 0000000000..d48666a1a6
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/AssignmentNode.java
@@ -0,0 +1,95 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.cfg.node.AssignmentContext.AssignmentLhsContext;
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.CompoundAssignmentTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.UnaryTree;
+import com.sun.source.tree.VariableTree;
+
+/**
+ * A node for an assignment:
+ *
+ * <pre>
+ * <em>variable</em> = <em>expression</em>
+ * <em>expression</em> . <em>field</em> = <em>expression</em>
+ * <em>expression</em> [ <em>index</em> ] = <em>expression</em>
+ * </pre>
+ *
+ * We allow assignments without corresponding AST {@link Tree}s.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class AssignmentNode extends Node {
+
+ protected Tree tree;
+ protected Node lhs;
+ protected Node rhs;
+
+ public AssignmentNode(Tree tree, Node target, Node expression) {
+ super(InternalUtils.typeOf(tree));
+ assert tree instanceof AssignmentTree || tree instanceof VariableTree
+ || tree instanceof CompoundAssignmentTree || tree instanceof UnaryTree;
+ assert target instanceof FieldAccessNode
+ || target instanceof LocalVariableNode
+ || target instanceof ArrayAccessNode;
+ this.tree = tree;
+ this.lhs = target;
+ this.rhs = expression;
+ rhs.setAssignmentContext(new AssignmentLhsContext(lhs));
+ }
+
+ public Node getTarget() {
+ return lhs;
+ }
+
+ public Node getExpression() {
+ return rhs;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitAssignment(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getTarget() + " = " + getExpression();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof AssignmentNode)) {
+ return false;
+ }
+ AssignmentNode other = (AssignmentNode) obj;
+ return getTarget().equals(other.getTarget())
+ && getExpression().equals(other.getExpression());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getTarget(), getExpression());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getTarget());
+ list.add(getExpression());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseAndNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseAndNode.java
new file mode 100644
index 0000000000..82c5f5d5f8
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseAndNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the bitwise or logical (single bit) and operation:
+ *
+ * <pre>
+ * <em>expression</em> &amp; <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class BitwiseAndNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public BitwiseAndNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.AND;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitBitwiseAnd(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " & " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof BitwiseAndNode)) {
+ return false;
+ }
+ BitwiseAndNode other = (BitwiseAndNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseComplementNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseComplementNode.java
new file mode 100644
index 0000000000..1b8ad73175
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseComplementNode.java
@@ -0,0 +1,73 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the bitwise complement operation:
+ *
+ * <pre>
+ * ~ <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class BitwiseComplementNode extends Node {
+
+ protected Tree tree;
+ protected Node operand;
+
+ public BitwiseComplementNode(Tree tree, Node operand) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.BITWISE_COMPLEMENT;
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitBitwiseComplement(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(~ " + getOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof BitwiseComplementNode)) {
+ return false;
+ }
+ BitwiseComplementNode other = (BitwiseComplementNode) obj;
+ return getOperand().equals(other.getOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseOrNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseOrNode.java
new file mode 100644
index 0000000000..f9763c9b14
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseOrNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the bitwise or logical (single bit) or operation:
+ *
+ * <pre>
+ * <em>expression</em> | <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class BitwiseOrNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public BitwiseOrNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.OR;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitBitwiseOr(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " | " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof BitwiseOrNode)) {
+ return false;
+ }
+ BitwiseOrNode other = (BitwiseOrNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseXorNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseXorNode.java
new file mode 100644
index 0000000000..848b2f17ec
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BitwiseXorNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the bitwise or logical (single bit) xor operation:
+ *
+ * <pre>
+ * <em>expression</em> ^ <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class BitwiseXorNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public BitwiseXorNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.XOR;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitBitwiseXor(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " ^ " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof BitwiseXorNode)) {
+ return false;
+ }
+ BitwiseXorNode other = (BitwiseXorNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BooleanLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BooleanLiteralNode.java
new file mode 100644
index 0000000000..8368c87793
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/BooleanLiteralNode.java
@@ -0,0 +1,51 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a boolean literal:
+ *
+ * <pre>
+ * <em>true</em>
+ * <em>false</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class BooleanLiteralNode extends ValueLiteralNode {
+
+ public BooleanLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.BOOLEAN_LITERAL);
+ }
+
+ @Override
+ public Boolean getValue() {
+ return (Boolean) tree.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitBooleanLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a BooleanLiteralNode
+ if (!(obj instanceof BooleanLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CaseNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CaseNode.java
new file mode 100644
index 0000000000..f5a3d460a5
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CaseNode.java
@@ -0,0 +1,86 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for a case in a switch statement. Although
+ * a case has no abstract value, it can imply facts about
+ * the abstract values of its operands.
+ *
+ * <pre>
+ * case <em>constant</em>:
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class CaseNode extends Node {
+
+ protected CaseTree tree;
+ protected Node switchExpr;
+ protected Node caseExpr;
+
+ public CaseNode(CaseTree tree, Node switchExpr, Node caseExpr, Types types) {
+ super(types.getNoType(TypeKind.NONE));
+ assert tree.getKind().equals(Kind.CASE);
+ this.tree = tree;
+ this.switchExpr = switchExpr;
+ this.caseExpr = caseExpr;
+ }
+
+ public Node getSwitchOperand() {
+ return switchExpr;
+ }
+
+ public Node getCaseOperand() {
+ return caseExpr;
+ }
+
+ @Override
+ public CaseTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitCase(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "case " + getCaseOperand() + ":";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof CaseNode)) {
+ return false;
+ }
+ CaseNode other = (CaseNode) obj;
+ return getSwitchOperand().equals(other.getSwitchOperand())
+ && getCaseOperand().equals(other.getCaseOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getSwitchOperand(), getCaseOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getSwitchOperand());
+ list.add(getCaseOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CharacterLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CharacterLiteralNode.java
new file mode 100644
index 0000000000..979a4177d8
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/CharacterLiteralNode.java
@@ -0,0 +1,53 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a character literal. For example:
+ *
+ * <pre>
+ * <em>'a'</em>
+ * <em>'\t'</em>
+ * <em>'\u03a9'</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class CharacterLiteralNode extends ValueLiteralNode {
+
+ public CharacterLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.CHAR_LITERAL);
+ }
+
+ @Override
+ public Character getValue() {
+ return (Character) tree.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitCharacterLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a CharacterLiteralNode
+ if (obj == null || !(obj instanceof CharacterLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java
new file mode 100644
index 0000000000..af1a974b1f
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ClassNameNode.java
@@ -0,0 +1,117 @@
+package org.checkerframework.dataflow.cfg.node;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.TreeUtils;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.element.Element;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node representing a class name used in an expression
+ * such as a static method invocation.
+ *
+ * parent.<em>class</em> .forName(...)
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ClassNameNode extends Node {
+
+ protected final Tree tree;
+ /** The class named by this node */
+ protected final Element element;
+
+ /** The parent name, if any. */
+ protected final /*@Nullable*/ Node parent;
+
+ public ClassNameNode(IdentifierTree tree) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Tree.Kind.IDENTIFIER;
+ this.tree = tree;
+ this.element = TreeUtils.elementFromUse(tree);
+ this.parent = null;
+ }
+
+ public ClassNameNode(ClassTree tree) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Tree.Kind.CLASS || tree.getKind() == Tree.Kind.ENUM || tree.getKind() == Tree.Kind.INTERFACE || tree.getKind() == Tree.Kind.ANNOTATION_TYPE;
+ this.tree = tree;
+ this.element = TreeUtils.elementFromDeclaration(tree);
+ this.parent = null;
+ }
+
+ public ClassNameNode(MemberSelectTree tree, Node parent) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ this.element = TreeUtils.elementFromUse(tree);
+ this.parent = parent;
+ }
+
+ public Element getElement() {
+ return element;
+ }
+
+ public Node getParent() {
+ return parent;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitClassName(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getElement().getSimpleName().toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ClassNameNode)) {
+ return false;
+ }
+ ClassNameNode other = (ClassNameNode) obj;
+ if (getParent() == null) {
+ return other.getParent() == null
+ && getElement().equals(other.getElement());
+ } else {
+ return getParent().equals(other.getParent())
+ && getElement().equals(other.getElement());
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (parent == null) {
+ return HashCodeUtils.hash(getElement());
+ }
+ return HashCodeUtils.hash(getElement(), getParent());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ if (parent == null) {
+ return Collections.emptyList();
+ }
+ return Collections.singleton(parent);
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalAndNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalAndNode.java
new file mode 100644
index 0000000000..dfa9f99c27
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalAndNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for a conditional and expression:
+ *
+ * <pre>
+ * <em>expression</em> &amp;&amp; <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ConditionalAndNode extends Node {
+
+ protected BinaryTree tree;
+ protected Node lhs;
+ protected Node rhs;
+
+ public ConditionalAndNode(BinaryTree tree, Node lhs, Node rhs) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind().equals(Kind.CONDITIONAL_AND);
+ this.tree = tree;
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ public Node getLeftOperand() {
+ return lhs;
+ }
+
+ public Node getRightOperand() {
+ return rhs;
+ }
+
+ @Override
+ public BinaryTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitConditionalAnd(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " && " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ConditionalAndNode)) {
+ return false;
+ }
+ ConditionalAndNode other = (ConditionalAndNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalNotNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalNotNode.java
new file mode 100644
index 0000000000..5143c69c50
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalNotNode.java
@@ -0,0 +1,73 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.UnaryTree;
+
+/**
+ * A node for a conditional not expression:
+ *
+ * <pre>
+ * ! <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ConditionalNotNode extends Node {
+
+ protected UnaryTree tree;
+ protected Node operand;
+
+ public ConditionalNotNode(UnaryTree tree, Node operand) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind().equals(Kind.LOGICAL_COMPLEMENT);
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ @Override
+ public UnaryTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitConditionalNot(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(!" + getOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ConditionalNotNode)) {
+ return false;
+ }
+ ConditionalNotNode other = (ConditionalNotNode) obj;
+ return getOperand().equals(other.getOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalOrNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalOrNode.java
new file mode 100644
index 0000000000..2da33fadf6
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ConditionalOrNode.java
@@ -0,0 +1,82 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for a conditional or expression:
+ *
+ * <pre>
+ * <em>expression</em> || <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class ConditionalOrNode extends Node {
+
+ protected BinaryTree tree;
+ protected Node lhs;
+ protected Node rhs;
+
+ public ConditionalOrNode(BinaryTree tree, Node lhs, Node rhs) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind().equals(Kind.CONDITIONAL_OR);
+ this.tree = tree;
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ public Node getLeftOperand() {
+ return lhs;
+ }
+
+ public Node getRightOperand() {
+ return rhs;
+ }
+
+ @Override
+ public BinaryTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitConditionalOr(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " || " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ConditionalOrNode)) {
+ return false;
+ }
+ ConditionalOrNode other = (ConditionalOrNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/DoubleLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/DoubleLiteralNode.java
new file mode 100644
index 0000000000..855a95a7b5
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/DoubleLiteralNode.java
@@ -0,0 +1,52 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a double literal. For example:
+ *
+ * <pre>
+ * <em>-9.</em>
+ * <em>3.14159D</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class DoubleLiteralNode extends ValueLiteralNode {
+
+ public DoubleLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.DOUBLE_LITERAL);
+ }
+
+ @Override
+ public Double getValue() {
+ return (Double) tree.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitDoubleLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a DoubleLiteralNode
+ if (obj == null || !(obj instanceof DoubleLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/EqualToNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/EqualToNode.java
new file mode 100644
index 0000000000..ed56d4ab68
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/EqualToNode.java
@@ -0,0 +1,82 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for an equality check:
+ *
+ * <pre>
+ * <em>expression</em> == <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class EqualToNode extends Node {
+
+ protected BinaryTree tree;
+ protected Node lhs;
+ protected Node rhs;
+
+ public EqualToNode(BinaryTree tree, Node lhs, Node rhs) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind().equals(Kind.EQUAL_TO);
+ this.tree = tree;
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ public Node getLeftOperand() {
+ return lhs;
+ }
+
+ public Node getRightOperand() {
+ return rhs;
+ }
+
+ @Override
+ public BinaryTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitEqualTo(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " == " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof EqualToNode)) {
+ return false;
+ }
+ EqualToNode other = (EqualToNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ExplicitThisLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ExplicitThisLiteralNode.java
new file mode 100644
index 0000000000..0c1069843c
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ExplicitThisLiteralNode.java
@@ -0,0 +1,44 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a reference to 'this'.
+ *
+ * <pre>
+ * <em>this</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ExplicitThisLiteralNode extends ThisLiteralNode {
+
+ protected Tree tree;
+
+ public ExplicitThisLiteralNode(Tree t) {
+ super(InternalUtils.typeOf(t));
+ assert t instanceof IdentifierTree
+ && ((IdentifierTree) t).getName().contentEquals("this");
+ tree = t;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitExplicitThisLiteral(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FieldAccessNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FieldAccessNode.java
new file mode 100644
index 0000000000..db5bf51c5b
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FieldAccessNode.java
@@ -0,0 +1,113 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.element.VariableElement;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.ElementUtils;
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.TreeUtils;
+
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a field access, including a method accesses:
+ *
+ * <pre>
+ * <em>expression</em> . <em>field</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class FieldAccessNode extends Node {
+
+ protected Tree tree;
+ protected VariableElement element;
+ protected String field;
+ protected Node receiver;
+
+ // TODO: add method to get modifiers (static, access level, ..)
+
+ public FieldAccessNode(Tree tree, Node receiver) {
+ super(InternalUtils.typeOf(tree));
+ assert TreeUtils.isFieldAccess(tree);
+ this.tree = tree;
+ this.receiver = receiver;
+ this.field = TreeUtils.getFieldName(tree);
+
+ if (tree instanceof MemberSelectTree) {
+ this.element = (VariableElement) TreeUtils.elementFromUse((MemberSelectTree) tree);
+ } else {
+ assert tree instanceof IdentifierTree;
+ this.element = (VariableElement) TreeUtils.elementFromUse((IdentifierTree) tree);
+ }
+ }
+
+ public FieldAccessNode(Tree tree, VariableElement element, Node receiver) {
+ super(element.asType());
+ this.tree = tree;
+ this.element = element;
+ this.receiver = receiver;
+ this.field = element.getSimpleName().toString();
+ }
+
+ public VariableElement getElement() {
+ return element;
+ }
+
+ public Node getReceiver() {
+ return receiver;
+ }
+
+ public String getFieldName() {
+ return field;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitFieldAccess(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getReceiver() + "." + field;
+ }
+
+ /**
+ * Is this a static field?
+ */
+ public boolean isStatic() {
+ return ElementUtils.isStatic(getElement());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof FieldAccessNode)) {
+ return false;
+ }
+ FieldAccessNode other = (FieldAccessNode) obj;
+ return getReceiver().equals(other.getReceiver())
+ && getFieldName().equals(other.getFieldName());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getReceiver(), getFieldName());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(receiver);
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatLiteralNode.java
new file mode 100644
index 0000000000..518c974868
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatLiteralNode.java
@@ -0,0 +1,52 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a float literal. For example:
+ *
+ * <pre>
+ * <em>8.0f</em>
+ * <em>6.022137e+23F</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class FloatLiteralNode extends ValueLiteralNode {
+
+ public FloatLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.FLOAT_LITERAL);
+ }
+
+ @Override
+ public Float getValue() {
+ return (Float) tree.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitFloatLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a FloatLiteralNode
+ if (obj == null || !(obj instanceof FloatLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingDivisionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingDivisionNode.java
new file mode 100644
index 0000000000..cd495b7b01
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingDivisionNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the floating-point division:
+ *
+ * <pre>
+ * <em>expression</em> / <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class FloatingDivisionNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public FloatingDivisionNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.DIVIDE;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitFloatingDivision(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " / " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof FloatingDivisionNode)) {
+ return false;
+ }
+ FloatingDivisionNode other = (FloatingDivisionNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingRemainderNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingRemainderNode.java
new file mode 100644
index 0000000000..d3b3caa0ab
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FloatingRemainderNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the floating-point remainder:
+ *
+ * <pre>
+ * <em>expression</em> % <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class FloatingRemainderNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public FloatingRemainderNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.REMAINDER;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitFloatingRemainder(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " % " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof FloatingRemainderNode)) {
+ return false;
+ }
+ FloatingRemainderNode other = (FloatingRemainderNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FunctionalInterfaceNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FunctionalInterfaceNode.java
new file mode 100644
index 0000000000..5446a0584c
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/FunctionalInterfaceNode.java
@@ -0,0 +1,93 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import org.checkerframework.javacutil.ErrorReporter;
+import org.checkerframework.javacutil.InternalUtils;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.MemberReferenceTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for member references and lambdas.
+ *
+ * The {@link Node#type} of a FunctionalInterfaceNode is determined by the
+ * assignment context the member reference or lambda is used in.
+ *
+ * <pre>
+ * <em>FunctionalInterface func = param1, param2, ... &rarr; statement</em>
+ * </pre>
+ *
+ * <pre>
+ * <em>FunctionalInterface func = param1, param2, ... &rarr; { ... }</em>
+ * </pre>
+ *
+ * <pre>
+ * <em>FunctionalInterface func = member reference</em>
+ * </pre>
+ *
+ * @author David
+ *
+ */
+public class FunctionalInterfaceNode extends Node {
+
+ protected Tree tree;
+
+ public FunctionalInterfaceNode(MemberReferenceTree tree) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ }
+
+ public FunctionalInterfaceNode(LambdaExpressionTree tree) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitMemberReference(this, p);
+ }
+
+ @Override
+ public String toString() {
+ if (tree instanceof LambdaExpressionTree) {
+ return "FunctionalInterfaceNode:" + ((LambdaExpressionTree) tree).getBodyKind();
+ } else if (tree instanceof MemberReferenceTree) {
+ return "FunctionalInterfaceNode:" + ((MemberReferenceTree) tree).getName();
+ } else {
+ // This should never happen.
+ ErrorReporter.errorAbort("Invalid tree in FunctionalInterfaceNode");
+ return null; // Dead code
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ FunctionalInterfaceNode that = (FunctionalInterfaceNode) o;
+
+ if (tree != null ? !tree.equals(that.tree) : that.tree != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return tree != null ? tree.hashCode() : 0;
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanNode.java
new file mode 100644
index 0000000000..7e51ccd115
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the greater than comparison:
+ *
+ * <pre>
+ * <em>expression</em> &gt; <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class GreaterThanNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public GreaterThanNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.GREATER_THAN;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitGreaterThan(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " > " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof GreaterThanNode)) {
+ return false;
+ }
+ GreaterThanNode other = (GreaterThanNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanOrEqualNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanOrEqualNode.java
new file mode 100644
index 0000000000..e23c824512
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/GreaterThanOrEqualNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the greater than or equal comparison:
+ *
+ * <pre>
+ * <em>expression</em> &gt;= <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class GreaterThanOrEqualNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public GreaterThanOrEqualNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.GREATER_THAN_EQUAL;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitGreaterThanOrEqual(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " >= " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof GreaterThanOrEqualNode)) {
+ return false;
+ }
+ GreaterThanOrEqualNode other = (GreaterThanOrEqualNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ImplicitThisLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ImplicitThisLiteralNode.java
new file mode 100644
index 0000000000..c2ba16f18f
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ImplicitThisLiteralNode.java
@@ -0,0 +1,33 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import javax.lang.model.type.TypeMirror;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * A node to model the implicit {@code this}, e.g., in a field access.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class ImplicitThisLiteralNode extends ThisLiteralNode {
+
+ public ImplicitThisLiteralNode(TypeMirror type) {
+ super(type);
+ }
+
+ @Override
+ public Tree getTree() {
+ return null;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitImplicitThisLiteral(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getName() + ")";
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java
new file mode 100644
index 0000000000..2f49436da2
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/InstanceOfNode.java
@@ -0,0 +1,92 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for the instanceof operator:
+ *
+ * <em>x</em> instanceof <em>Point</em>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class InstanceOfNode extends Node {
+
+ /** The value being tested. */
+ protected Node operand;
+
+ /** The reference type being tested against. */
+ protected TypeMirror refType;
+
+ /** The tree associated with this node. */
+ protected final InstanceOfTree tree;
+
+ public InstanceOfNode(Tree tree, Node operand, TypeMirror refType, Types types) {
+ super(types.getPrimitiveType(TypeKind.BOOLEAN));
+ assert tree.getKind() == Tree.Kind.INSTANCE_OF;
+ this.tree = (InstanceOfTree) tree;
+ this.operand = operand;
+ this.refType = refType;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ @Override
+ public TypeMirror getType() {
+ return type;
+ }
+
+ public TypeMirror getRefType() {
+ return refType;
+ }
+
+ @Override
+ public InstanceOfTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitInstanceOf(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getOperand() + " instanceof " + getRefType() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof InstanceOfNode)) {
+ return false;
+ }
+ InstanceOfNode other = (InstanceOfNode) obj;
+ // TODO: TypeMirror.equals may be too restrictive.
+ // Check whether Types.isSameType is the better comparison.
+ return getOperand().equals(other.getOperand())
+ && getRefType().equals(other.getRefType());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerDivisionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerDivisionNode.java
new file mode 100644
index 0000000000..0b5701413b
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerDivisionNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the integer division:
+ *
+ * <pre>
+ * <em>expression</em> / <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class IntegerDivisionNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public IntegerDivisionNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.DIVIDE;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitIntegerDivision(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " / " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof IntegerDivisionNode)) {
+ return false;
+ }
+ IntegerDivisionNode other = (IntegerDivisionNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerLiteralNode.java
new file mode 100644
index 0000000000..b23a9cb512
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerLiteralNode.java
@@ -0,0 +1,53 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for an integer literal. For example:
+ *
+ * <pre>
+ * <em>42</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class IntegerLiteralNode extends ValueLiteralNode {
+
+ int value;
+
+ public IntegerLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.INT_LITERAL);
+ value = (Integer) tree.getValue();
+ }
+
+ @Override
+ public Integer getValue() {
+ return value;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitIntegerLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a IntegerLiteralNode
+ if (!(obj instanceof IntegerLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerRemainderNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerRemainderNode.java
new file mode 100644
index 0000000000..39021cd2ab
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/IntegerRemainderNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the integer remainder:
+ *
+ * <pre>
+ * <em>expression</em> % <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class IntegerRemainderNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public IntegerRemainderNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.REMAINDER;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitIntegerRemainder(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " % " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof IntegerRemainderNode)) {
+ return false;
+ }
+ IntegerRemainderNode other = (IntegerRemainderNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LeftShiftNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LeftShiftNode.java
new file mode 100644
index 0000000000..60652e6100
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LeftShiftNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for bitwise left shift operations:
+ *
+ * <pre>
+ * <em>expression</em> &lt;&lt; <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class LeftShiftNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public LeftShiftNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.LEFT_SHIFT;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitLeftShift(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " << " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof LeftShiftNode)) {
+ return false;
+ }
+ LeftShiftNode other = (LeftShiftNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanNode.java
new file mode 100644
index 0000000000..a8dd5a18b4
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanNode.java
@@ -0,0 +1,85 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the less than comparison:
+ *
+ * <pre>
+ * <em>expression</em> &lt; <em>expression</em>
+ * </pre>
+ *
+ * We allow less than nodes without corresponding AST {@link Tree}s.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class LessThanNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public LessThanNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.LESS_THAN;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitLessThan(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " < " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof LessThanNode)) {
+ return false;
+ }
+ LessThanNode other = (LessThanNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanOrEqualNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanOrEqualNode.java
new file mode 100644
index 0000000000..4944ab66a0
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LessThanOrEqualNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the less than or equal comparison:
+ *
+ * <pre>
+ * <em>expression</em> &lt;= <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class LessThanOrEqualNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public LessThanOrEqualNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.LESS_THAN_EQUAL;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitLessThanOrEqual(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " <= " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof LessThanOrEqualNode)) {
+ return false;
+ }
+ LessThanOrEqualNode other = (LessThanOrEqualNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LocalVariableNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LocalVariableNode.java
new file mode 100644
index 0000000000..bc31cf72ee
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LocalVariableNode.java
@@ -0,0 +1,106 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.element.Element;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.TreeUtils;
+
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+
+/**
+ * A node for a local variable or a parameter:
+ *
+ * <pre>
+ * <em>identifier</em>
+ * </pre>
+ *
+ * We allow local variable uses introduced by the {@link org.checkerframework.dataflow.cfg.CFGBuilder} without
+ * corresponding AST {@link Tree}s.
+ *
+ * @author Stefan Heule
+ *
+ */
+// TODO: don't use for parameters, as they don't have a tree
+public class LocalVariableNode extends Node {
+
+ protected Tree tree;
+ protected Node receiver;
+
+ public LocalVariableNode(Tree t) {
+ super(InternalUtils.typeOf(t));
+ // IdentifierTree for normal uses of the local variable or parameter,
+ // and VariableTree for the translation of an initializer block
+ assert t != null;
+ assert t instanceof IdentifierTree || t instanceof VariableTree;
+ tree = t;
+ this.receiver = null;
+ }
+
+ public LocalVariableNode(Tree t, Node receiver) {
+ this(t);
+ this.receiver = receiver;
+ }
+
+ public Element getElement() {
+ Element el;
+ if (tree instanceof IdentifierTree) {
+ el = TreeUtils.elementFromUse((IdentifierTree) tree);
+ } else {
+ assert tree instanceof VariableTree;
+ el = TreeUtils.elementFromDeclaration((VariableTree) tree);
+ }
+ return el;
+ }
+
+ public Node getReceiver() {
+ return receiver;
+ }
+
+ public String getName() {
+ if (tree instanceof IdentifierTree) {
+ return ((IdentifierTree) tree).getName().toString();
+ }
+ return ((VariableTree) tree).getName().toString();
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitLocalVariable(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getName().toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof LocalVariableNode)) {
+ return false;
+ }
+ LocalVariableNode other = (LocalVariableNode) obj;
+ return getName().equals(other.getName());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getName());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LongLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LongLiteralNode.java
new file mode 100644
index 0000000000..47f856a0d8
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/LongLiteralNode.java
@@ -0,0 +1,52 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a long literal. For example:
+ *
+ * <pre>
+ * <em>-3l</em>
+ * <em>0x80808080L</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class LongLiteralNode extends ValueLiteralNode {
+
+ public LongLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.LONG_LITERAL);
+ }
+
+ @Override
+ public Long getValue() {
+ return (Long) tree.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitLongLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a LongLiteralNode
+ if (obj == null || !(obj instanceof LongLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MarkerNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MarkerNode.java
new file mode 100644
index 0000000000..fa7238e1c6
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MarkerNode.java
@@ -0,0 +1,89 @@
+package org.checkerframework.dataflow.cfg.node;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * MarkerNodes are no-op Nodes used for debugging information.
+ * They can hold a Tree and a message, which will be part of the
+ * String representation of the MarkerNode.
+ *
+ * An example use case for MarkerNodes is representing switch
+ * statements.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class MarkerNode extends Node {
+
+ protected /*@Nullable*/ Tree tree;
+ protected String message;
+
+ public MarkerNode(/*@Nullable*/ Tree tree, String message, Types types) {
+ super(types.getNoType(TypeKind.NONE));
+ this.tree = tree;
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitMarker(this, p);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("marker ");
+ sb.append("(" + message + ")");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof MarkerNode)) {
+ return false;
+ }
+ MarkerNode other = (MarkerNode) obj;
+ if (tree == null && other.getTree() != null) {
+ return false;
+ }
+
+ return getTree().equals(other.getTree())
+ && getMessage().equals(other.getMessage());
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ if (tree != null) {
+ hash = HashCodeUtils.hash(tree);
+ }
+ return HashCodeUtils.hash(hash, getMessage());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodAccessNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodAccessNode.java
new file mode 100644
index 0000000000..75a8d681d3
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodAccessNode.java
@@ -0,0 +1,84 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.element.ExecutableElement;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.TreeUtils;
+
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a method access, including a method accesses:
+ *
+ * <pre>
+ * <em>expression</em> . <em>method</em> ()
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class MethodAccessNode extends Node {
+
+ protected ExpressionTree tree;
+ protected ExecutableElement method;
+ protected Node receiver;
+
+ // TODO: add method to get modifiers (static, access level, ..)
+
+ public MethodAccessNode(ExpressionTree tree, Node receiver) {
+ super(InternalUtils.typeOf(tree));
+ assert TreeUtils.isMethodAccess(tree);
+ this.tree = tree;
+ this.method = (ExecutableElement) TreeUtils.elementFromUse(tree);
+ this.receiver = receiver;
+ }
+
+ public ExecutableElement getMethod() {
+ return method;
+ }
+
+ public Node getReceiver() {
+ return receiver;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitMethodAccess(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getReceiver() + "." + method.getSimpleName();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof MethodAccessNode)) {
+ return false;
+ }
+ MethodAccessNode other = (MethodAccessNode) obj;
+ return getReceiver().equals(other.getReceiver())
+ && getMethod().equals(other.getMethod());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getReceiver(), getMethod());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(receiver);
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodInvocationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodInvocationNode.java
new file mode 100644
index 0000000000..797185a83b
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/MethodInvocationNode.java
@@ -0,0 +1,129 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.checkerframework.dataflow.cfg.node.AssignmentContext.MethodParameterContext;
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * A node for method invocation
+ *
+ * <pre>
+ * <em>target(arg1, arg2, ...)</em>
+ * </pre>
+ *
+ * CFGs may contain {@link MethodInvocationNode}s that correspond to no AST
+ * {@link Tree}, in which case, the tree field will be null.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class MethodInvocationNode extends Node {
+
+ protected MethodInvocationTree tree;
+ protected MethodAccessNode target;
+ protected List<Node> arguments;
+ protected TreePath treePath;
+
+ public MethodInvocationNode(MethodInvocationTree tree,
+ MethodAccessNode target, List<Node> arguments, TreePath treePath) {
+ super(tree != null ? InternalUtils.typeOf(tree) : target.getMethod().getReturnType());
+ this.tree = tree;
+ this.target = target;
+ this.arguments = arguments;
+ this.treePath = treePath;
+
+ // set assignment contexts for parameters
+ int i = 0;
+ for (Node arg : arguments) {
+ AssignmentContext ctx = new MethodParameterContext(target.getMethod(), i++);
+ arg.setAssignmentContext(ctx);
+ }
+ }
+
+ public MethodInvocationNode(MethodAccessNode target, List<Node> arguments,
+ TreePath treePath) {
+ this(null, target, arguments, treePath);
+ }
+
+ public MethodAccessNode getTarget() {
+ return target;
+ }
+
+ public List<Node> getArguments() {
+ return arguments;
+ }
+
+ public Node getArgument(int i) {
+ return arguments.get(i);
+ }
+
+ public TreePath getTreePath() {
+ return treePath;
+ }
+
+ @Override
+ public MethodInvocationTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitMethodInvocation(this, p);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(target);
+ sb.append("(");
+ boolean needComma = false;
+ for (Node arg : arguments) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(arg);
+ needComma = true;
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof MethodInvocationNode)) {
+ return false;
+ }
+ MethodInvocationNode other = (MethodInvocationNode) obj;
+
+ return getTarget().equals(other.getTarget())
+ && getArguments().equals(other.getArguments());
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ hash = HashCodeUtils.hash(target);
+ for (Node arg : arguments) {
+ hash = HashCodeUtils.hash(hash, arg.hashCode());
+ }
+ return hash;
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ List<Node> list = new LinkedList<Node>();
+ list.add(target);
+ list.addAll(arguments);
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NarrowingConversionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NarrowingConversionNode.java
new file mode 100644
index 0000000000..f0165d9589
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NarrowingConversionNode.java
@@ -0,0 +1,80 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.TypesUtils;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for the narrowing primitive conversion operation. See JLS 5.1.3 for
+ * the definition of narrowing primitive conversion.
+ *
+ * A {@link NarrowingConversionNode} does not correspond to any tree node in the
+ * parsed AST. It is introduced when a value of some primitive type appears in a
+ * context that requires a different primitive with more bits of precision.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class NarrowingConversionNode extends Node {
+
+ protected Tree tree;
+ protected Node operand;
+
+ public NarrowingConversionNode(Tree tree, Node operand, TypeMirror type) {
+ super(type);
+ assert TypesUtils.isPrimitive(type) : "non-primitive type in narrowing conversion";
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ public TypeMirror getType() {
+ return type;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNarrowingConversion(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "NarrowingConversion(" + getOperand() + ", " + type + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof NarrowingConversionNode)) {
+ return false;
+ }
+ NarrowingConversionNode other = (NarrowingConversionNode) obj;
+ return getOperand().equals(other.getOperand())
+ && TypesUtils.areSamePrimitiveTypes(getType(), other.getType());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/Node.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/Node.java
new file mode 100644
index 0000000000..69220ea484
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/Node.java
@@ -0,0 +1,171 @@
+package org.checkerframework.dataflow.cfg.node;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.cfg.CFGBuilder;
+import org.checkerframework.dataflow.cfg.block.Block;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import javax.lang.model.type.TypeMirror;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * A node in the abstract representation used for Java code inside a basic
+ * block.
+ *
+ * <p>
+ *
+ * The following invariants hold:
+ *
+ * <pre>
+ * block == null || block instanceof RegularBlock || block instanceof ExceptionBlock
+ * block instanceof RegularBlock &rArr; block.getContents().contains(this)
+ * block instanceof ExceptionBlock &rArr; block.getNode() == this
+ * block == null &hArr; "This object represents a parameter of the method."
+ * </pre>
+ *
+ * <pre>
+ * type != null
+ * tree != null &rArr; node.getType() == InternalUtils.typeOf(node.getTree())
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public abstract class Node {
+
+ /**
+ * The basic block this node belongs to (see invariant about this field
+ * above).
+ */
+ protected /*@Nullable*/ Block block;
+
+ /**
+ * Is this node an l-value?
+ */
+ protected boolean lvalue = false;
+
+ /**
+ * The assignment context of this node. See {@link AssignmentContext}.
+ */
+ protected /*@Nullable*/ AssignmentContext assignmentContext;
+
+ /**
+ * Does this node represent a tree that appears in the source code (true)
+ * or one that the CFG builder added while desugaring (false).
+ */
+ protected boolean inSource = true;
+
+ /**
+ * The type of this node. For {@link Node}s with {@link Tree}s, this type is
+ * the type of the {@link Tree}. Otherwise, it is the type is set by the
+ * {@link CFGBuilder}.
+ */
+ protected final TypeMirror type;
+
+ public Node(TypeMirror type) {
+ assert type != null;
+ this.type = type;
+ }
+
+ /**
+ * @return the basic block this node belongs to (or {@code null} if it
+ * represents the parameter of a method).
+ */
+ public /*@Nullable*/ Block getBlock() {
+ return block;
+ }
+
+ /** Set the basic block this node belongs to. */
+ public void setBlock(Block b) {
+ block = b;
+ }
+
+ /**
+ * Returns the {@link Tree} in the abstract syntax tree, or
+ * {@code null} if no corresponding tree exists. For instance, this is
+ * the case for an {@link ImplicitThisLiteralNode}.
+ *
+ * @return the corresponding {@link Tree} or {@code null}.
+ */
+ abstract public /*@Nullable*/ Tree getTree();
+
+ /**
+ * Returns a {@link TypeMirror} representing the type of a {@link Node} A
+ * {@link Node} will always have a type even when it has no {@link Tree}.
+ *
+ * @return a {@link TypeMirror} representing the type of this {@link Node}.
+ */
+ public TypeMirror getType() {
+ return type;
+ }
+
+ /**
+ * Accept method of the visitor pattern
+ *
+ * @param <R>
+ * Result type of the operation.
+ * @param <P>
+ * Parameter type.
+ * @param visitor
+ * The visitor to be applied to this node.
+ * @param p
+ * The parameter for this operation.
+ */
+ public abstract <R, P> R accept(NodeVisitor<R, P> visitor, P p);
+
+ public boolean isLValue() {
+ return lvalue;
+ }
+
+ /**
+ * Make this node an l-value.
+ */
+ public void setLValue() {
+ lvalue = true;
+ }
+
+ public boolean getInSource() {
+ return inSource;
+ }
+
+ public void setInSource(boolean inSrc) {
+ inSource = inSrc;
+ }
+
+ public AssignmentContext getAssignmentContext() {
+ return assignmentContext;
+ }
+
+ public void setAssignmentContext(AssignmentContext assignmentContext) {
+ this.assignmentContext = assignmentContext;
+ }
+
+ /**
+ * @return a collection containing all of the operand {@link Node}s of this
+ * {@link Node}.
+ */
+ public abstract Collection<Node> getOperands();
+
+ /**
+ * @return a collection containing all of the operand {@link Node}s of this
+ * {@link Node}, as well as (transitively) the operands of its
+ * operands.
+ */
+ public Collection<Node> getTransitiveOperands() {
+ LinkedList<Node> operands = new LinkedList<>(getOperands());
+ LinkedList<Node> transitiveOperands = new LinkedList<>();
+ while (!operands.isEmpty()) {
+ Node next = operands.removeFirst();
+ operands.addAll(next.getOperands());
+ transitiveOperands.add(next);
+ }
+ return transitiveOperands;
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java
new file mode 100644
index 0000000000..69cb61db6f
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NodeVisitor.java
@@ -0,0 +1,163 @@
+package org.checkerframework.dataflow.cfg.node;
+
+/**
+ * A visitor for a {@link Node} tree.
+ *
+ * @author Stefan Heule
+ *
+ * @param <R>
+ * Return type of the visitor. Use {@link Void} if the visitor does
+ * not have a return value.
+ * @param <P>
+ * Parameter type of the visitor. Use {@link Void} if the visitor
+ * does not have a parameter.
+ */
+public interface NodeVisitor<R, P> {
+ // Literals
+ R visitShortLiteral(ShortLiteralNode n, P p);
+
+ R visitIntegerLiteral(IntegerLiteralNode n, P p);
+
+ R visitLongLiteral(LongLiteralNode n, P p);
+
+ R visitFloatLiteral(FloatLiteralNode n, P p);
+
+ R visitDoubleLiteral(DoubleLiteralNode n, P p);
+
+ R visitBooleanLiteral(BooleanLiteralNode n, P p);
+
+ R visitCharacterLiteral(CharacterLiteralNode n, P p);
+
+ R visitStringLiteral(StringLiteralNode n, P p);
+
+ R visitNullLiteral(NullLiteralNode n, P p);
+
+ // Unary operations
+ R visitNumericalMinus(NumericalMinusNode n, P p);
+
+ R visitNumericalPlus(NumericalPlusNode n, P p);
+
+ R visitBitwiseComplement(BitwiseComplementNode n, P p);
+
+ R visitNullChk(NullChkNode n, P p);
+
+ // Binary operations
+ R visitStringConcatenate(StringConcatenateNode n, P p);
+
+ R visitNumericalAddition(NumericalAdditionNode n, P p);
+
+ R visitNumericalSubtraction(NumericalSubtractionNode n, P p);
+
+ R visitNumericalMultiplication(NumericalMultiplicationNode n, P p);
+
+ R visitIntegerDivision(IntegerDivisionNode n, P p);
+
+ R visitFloatingDivision(FloatingDivisionNode n, P p);
+
+ R visitIntegerRemainder(IntegerRemainderNode n, P p);
+
+ R visitFloatingRemainder(FloatingRemainderNode n, P p);
+
+ R visitLeftShift(LeftShiftNode n, P p);
+
+ R visitSignedRightShift(SignedRightShiftNode n, P p);
+
+ R visitUnsignedRightShift(UnsignedRightShiftNode n, P p);
+
+ R visitBitwiseAnd(BitwiseAndNode n, P p);
+
+ R visitBitwiseOr(BitwiseOrNode n, P p);
+
+ R visitBitwiseXor(BitwiseXorNode n, P p);
+
+ // Compound assignments
+ R visitStringConcatenateAssignment(StringConcatenateAssignmentNode n, P p);
+
+ // Comparison operations
+ R visitLessThan(LessThanNode n, P p);
+
+ R visitLessThanOrEqual(LessThanOrEqualNode n, P p);
+
+ R visitGreaterThan(GreaterThanNode n, P p);
+
+ R visitGreaterThanOrEqual(GreaterThanOrEqualNode n, P p);
+
+ R visitEqualTo(EqualToNode n, P p);
+
+ R visitNotEqual(NotEqualNode n, P p);
+
+ // Conditional operations
+ R visitConditionalAnd(ConditionalAndNode n, P p);
+
+ R visitConditionalOr(ConditionalOrNode n, P p);
+
+ R visitConditionalNot(ConditionalNotNode n, P p);
+
+ R visitTernaryExpression(TernaryExpressionNode n, P p);
+
+ R visitAssignment(AssignmentNode n, P p);
+
+ R visitLocalVariable(LocalVariableNode n, P p);
+
+ R visitVariableDeclaration(VariableDeclarationNode n, P p);
+
+ R visitFieldAccess(FieldAccessNode n, P p);
+
+ R visitMethodAccess(MethodAccessNode n, P p);
+
+ R visitArrayAccess(ArrayAccessNode n, P p);
+
+ R visitImplicitThisLiteral(ImplicitThisLiteralNode n, P p);
+
+ R visitExplicitThisLiteral(ExplicitThisLiteralNode n, P p);
+
+ R visitSuper(SuperNode n, P p);
+
+ R visitReturn(ReturnNode n, P p);
+
+ R visitStringConversion(StringConversionNode n, P p);
+
+ R visitNarrowingConversion(NarrowingConversionNode n, P p);
+
+ R visitWideningConversion(WideningConversionNode n, P p);
+
+ R visitInstanceOf(InstanceOfNode n, P p);
+
+ R visitTypeCast(TypeCastNode n, P p);
+
+ // Blocks
+
+ R visitSynchronized(SynchronizedNode n, P p);
+
+ // Statements
+ R visitAssertionError(AssertionErrorNode n, P p);
+
+ R visitThrow(ThrowNode n, P p);
+
+ // Cases
+ R visitCase(CaseNode n, P p);
+
+ // Method and constructor invocations
+ R visitMethodInvocation(MethodInvocationNode n, P p);
+
+ R visitObjectCreation(ObjectCreationNode n, P p);
+
+ R visitMemberReference(FunctionalInterfaceNode n, P p);
+
+ R visitArrayCreation(ArrayCreationNode n, P p);
+
+ // Type, package and class names
+ R visitArrayType(ArrayTypeNode n, P p);
+
+ R visitPrimitiveType(PrimitiveTypeNode n, P p);
+
+ R visitClassName(ClassNameNode n, P p);
+
+ R visitPackageName(PackageNameNode n, P p);
+
+ // Parameterized types
+ R visitParameterizedType(ParameterizedTypeNode n, P p);
+
+ // Marker nodes
+ R visitMarker(MarkerNode n, P p);
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NotEqualNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NotEqualNode.java
new file mode 100644
index 0000000000..c043e20dee
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NotEqualNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the not equal comparison:
+ *
+ * <pre>
+ * <em>expression</em> != <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class NotEqualNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public NotEqualNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.NOT_EQUAL_TO;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNotEqual(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " != " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof NotEqualNode)) {
+ return false;
+ }
+ NotEqualNode other = (NotEqualNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullChkNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullChkNode.java
new file mode 100644
index 0000000000..01ac008f40
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullChkNode.java
@@ -0,0 +1,72 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the unary 'nullchk' operation (generated by the Java compiler):
+ *
+ * <pre>
+ * &lt;*nullchk*&gt;<em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ */
+public class NullChkNode extends Node {
+
+ protected Tree tree;
+ protected Node operand;
+
+ public NullChkNode(Tree tree, Node operand) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.OTHER;
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNullChk(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(+ " + getOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof NumericalPlusNode)) {
+ return false;
+ }
+ NumericalPlusNode other = (NumericalPlusNode) obj;
+ return getOperand().equals(other.getOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+ }
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullLiteralNode.java
new file mode 100644
index 0000000000..66e67152cd
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NullLiteralNode.java
@@ -0,0 +1,51 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for the null literal.
+ *
+ * <pre>
+ * <em>null</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class NullLiteralNode extends ValueLiteralNode {
+
+ public NullLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.NULL_LITERAL);
+ }
+
+ @Override
+ public Void getValue() {
+ return (Void) tree.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNullLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a NullLiteralNode
+ if (obj == null || !(obj instanceof NullLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalAdditionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalAdditionNode.java
new file mode 100644
index 0000000000..d4192adf47
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalAdditionNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the numerical addition:
+ *
+ * <pre>
+ * <em>expression</em> + <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class NumericalAdditionNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public NumericalAdditionNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.PLUS
+ || tree.getKind() == Kind.PLUS_ASSIGNMENT;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNumericalAddition(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " + " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof NumericalAdditionNode)) {
+ return false;
+ }
+ NumericalAdditionNode other = (NumericalAdditionNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMinusNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMinusNode.java
new file mode 100644
index 0000000000..459f299262
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMinusNode.java
@@ -0,0 +1,73 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the unary minus operation:
+ *
+ * <pre>
+ * - <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class NumericalMinusNode extends Node {
+
+ protected Tree tree;
+ protected Node operand;
+
+ public NumericalMinusNode(Tree tree, Node operand) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.UNARY_MINUS;
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNumericalMinus(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(- " + getOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof NumericalMinusNode)) {
+ return false;
+ }
+ NumericalMinusNode other = (NumericalMinusNode) obj;
+ return getOperand().equals(other.getOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMultiplicationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMultiplicationNode.java
new file mode 100644
index 0000000000..947dc5dcde
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalMultiplicationNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the numerical multiplication:
+ *
+ * <pre>
+ * <em>expression</em> * <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class NumericalMultiplicationNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public NumericalMultiplicationNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.MULTIPLY;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNumericalMultiplication(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " * " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof NumericalMultiplicationNode)) {
+ return false;
+ }
+ NumericalMultiplicationNode other = (NumericalMultiplicationNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalPlusNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalPlusNode.java
new file mode 100644
index 0000000000..3d19b278a7
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalPlusNode.java
@@ -0,0 +1,73 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the unary plus operation:
+ *
+ * <pre>
+ * + <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class NumericalPlusNode extends Node {
+
+ protected Tree tree;
+ protected Node operand;
+
+ public NumericalPlusNode(Tree tree, Node operand) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.UNARY_PLUS;
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNumericalPlus(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(+ " + getOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof NumericalPlusNode)) {
+ return false;
+ }
+ NumericalPlusNode other = (NumericalPlusNode) obj;
+ return getOperand().equals(other.getOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalSubtractionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalSubtractionNode.java
new file mode 100644
index 0000000000..1526a18461
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/NumericalSubtractionNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the numerical subtraction:
+ *
+ * <pre>
+ * <em>expression</em> - <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class NumericalSubtractionNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public NumericalSubtractionNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.MINUS;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitNumericalSubtraction(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " - " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof NumericalSubtractionNode)) {
+ return false;
+ }
+ NumericalSubtractionNode other = (NumericalSubtractionNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ObjectCreationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ObjectCreationNode.java
new file mode 100644
index 0000000000..30f15edc17
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ObjectCreationNode.java
@@ -0,0 +1,106 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+import org.checkerframework.javacutil.InternalUtils;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.sun.source.tree.NewClassTree;
+
+/**
+ * A node for new object creation
+ *
+ * <pre>
+ * <em>new constructor(arg1, arg2, ...)</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ObjectCreationNode extends Node {
+
+ protected NewClassTree tree;
+ protected Node constructor;
+ protected List<Node> arguments;
+
+ public ObjectCreationNode(NewClassTree tree,
+ Node constructor,
+ List<Node> arguments) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ this.constructor = constructor;
+ this.arguments = arguments;
+ }
+
+ public Node getConstructor() {
+ return constructor;
+ }
+
+ public List<Node> getArguments() {
+ return arguments;
+ }
+
+ public Node getArgument(int i) {
+ return arguments.get(i);
+ }
+
+ @Override
+ public NewClassTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitObjectCreation(this, p);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("new " + constructor + "(");
+ boolean needComma = false;
+ for (Node arg : arguments) {
+ if (needComma) {
+ sb.append(", ");
+ }
+ sb.append(arg);
+ needComma = true;
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ObjectCreationNode)) {
+ return false;
+ }
+ ObjectCreationNode other = (ObjectCreationNode) obj;
+ if (constructor == null && other.getConstructor() != null) {
+ return false;
+ }
+
+ return getConstructor().equals(other.getConstructor())
+ && getArguments().equals(other.getArguments());
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = HashCodeUtils.hash(constructor);
+ for (Node arg : arguments) {
+ hash = HashCodeUtils.hash(hash, arg.hashCode());
+ }
+ return hash;
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(constructor);
+ list.addAll(arguments);
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PackageNameNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PackageNameNode.java
new file mode 100644
index 0000000000..3851126f44
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PackageNameNode.java
@@ -0,0 +1,110 @@
+package org.checkerframework.dataflow.cfg.node;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.TreeUtils;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.element.Element;
+
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node representing a package name used in an expression such as a
+ * constructor invocation
+ *
+ * <p>
+ * <em>package</em>.class.object(...)
+ * <p>
+ * parent.<em>package</em>.class.object(...)
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class PackageNameNode extends Node {
+
+ protected final Tree tree;
+ /** The package named by this node */
+ protected final Element element;
+
+ /** The parent name, if any. */
+ protected final /*@Nullable*/ PackageNameNode parent;
+
+ public PackageNameNode(IdentifierTree tree) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ this.element = TreeUtils.elementFromUse(tree);
+ this.parent = null;
+ }
+
+ public PackageNameNode(MemberSelectTree tree, PackageNameNode parent) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ this.element = TreeUtils.elementFromUse(tree);
+ this.parent = parent;
+ }
+
+ public Element getElement() {
+ return element;
+ }
+
+ public PackageNameNode getParent() {
+ return parent;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitPackageName(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getElement().getSimpleName().toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof PackageNameNode)) {
+ return false;
+ }
+ PackageNameNode other = (PackageNameNode) obj;
+ if (getParent() == null) {
+ return other.getParent() == null
+ && getElement().equals(other.getElement());
+ } else {
+ return getParent().equals(other.getParent())
+ && getElement().equals(other.getElement());
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (parent == null) {
+ return HashCodeUtils.hash(getElement());
+ }
+ return HashCodeUtils.hash(getElement(), getParent());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ if (parent == null) {
+ return Collections.emptyList();
+ }
+ return Collections.singleton((Node) parent);
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ParameterizedTypeNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ParameterizedTypeNode.java
new file mode 100644
index 0000000000..7939b41fa7
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ParameterizedTypeNode.java
@@ -0,0 +1,73 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a parameterized type occurring in an expression:
+ *
+ * <pre>
+ * <em>type&lt;arg1, arg2&gt;</em>
+ * </pre>
+ *
+ * Parameterized types don't represent any computation to be done
+ * at runtime, so we might choose to represent them differently by
+ * modifying the {@link Node}s in which parameterized types can occur, such
+ * as {@link ObjectCreationNode}s.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+
+public class ParameterizedTypeNode extends Node {
+
+ protected Tree tree;
+
+ public ParameterizedTypeNode(Tree t) {
+ super(InternalUtils.typeOf(t));
+ assert t instanceof ParameterizedTypeTree;
+ tree = t;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitParameterizedType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getTree().toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ParameterizedTypeNode)) {
+ return false;
+ }
+ ParameterizedTypeNode other = (ParameterizedTypeNode) obj;
+ return getTree().equals(other.getTree());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getTree());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PrimitiveTypeNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PrimitiveTypeNode.java
new file mode 100644
index 0000000000..69b185641d
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/PrimitiveTypeNode.java
@@ -0,0 +1,65 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.PrimitiveTypeTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node representing a primitive type used in an expression
+ * such as a field access
+ *
+ * <em>type</em> .class
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class PrimitiveTypeNode extends Node {
+
+ protected final PrimitiveTypeTree tree;
+
+ public PrimitiveTypeNode(PrimitiveTypeTree tree) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitPrimitiveType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return tree.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof PrimitiveTypeNode)) {
+ return false;
+ }
+ PrimitiveTypeNode other = (PrimitiveTypeNode) obj;
+ return getType().equals(other.getType());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getType());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ReturnNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ReturnNode.java
new file mode 100644
index 0000000000..dbc74c83e3
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ReturnNode.java
@@ -0,0 +1,101 @@
+package org.checkerframework.dataflow.cfg.node;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import org.checkerframework.dataflow.cfg.node.AssignmentContext.LambdaReturnContext;
+import org.checkerframework.dataflow.cfg.node.AssignmentContext.MethodReturnContext;
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ReturnTree;
+
+/**
+ * A node for a return statement:
+ *
+ * <pre>
+ * return
+ * return <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class ReturnNode extends Node {
+
+ protected ReturnTree tree;
+ protected /*@Nullable*/ Node result;
+
+ public ReturnNode(ReturnTree t, /*@Nullable*/ Node result, Types types, MethodTree methodTree) {
+ super(types.getNoType(TypeKind.NONE));
+ this.result = result;
+ tree = t;
+ result.setAssignmentContext(new MethodReturnContext(methodTree));
+ }
+
+ public ReturnNode(ReturnTree t, /*@Nullable*/ Node result, Types types, LambdaExpressionTree lambda, MethodSymbol methodSymbol) {
+ super(types.getNoType(TypeKind.NONE));
+ this.result = result;
+ tree = t;
+ result.setAssignmentContext(new LambdaReturnContext(methodSymbol));
+ }
+
+
+ public Node getResult() {
+ return result;
+ }
+
+ @Override
+ public ReturnTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitReturn(this, p);
+ }
+
+ @Override
+ public String toString() {
+ if (result != null) {
+ return "return " + result;
+ }
+ return "return";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ReturnNode)) {
+ return false;
+ }
+ ReturnNode other = (ReturnNode) obj;
+ if ((result == null) != (other.result == null)) {
+ return false;
+ }
+ return (result == null || result.equals(other.result));
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(result);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ if (result == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.singletonList(result);
+ }
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ShortLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ShortLiteralNode.java
new file mode 100644
index 0000000000..f61da82397
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ShortLiteralNode.java
@@ -0,0 +1,57 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a short literal. For example:
+ *
+ * <pre>
+ * <em>5</em>
+ * <em>0x8fff</em>
+ * </pre>
+ *
+ * Java source and the AST representation do not have "short" literals. They
+ * have integer literals that may be narrowed to shorts depending on context. If
+ * we use explicit NarrowingConversionNodes, do we need ShortLiteralNodes too?
+ * TODO: Decide this question.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ShortLiteralNode extends ValueLiteralNode {
+
+ public ShortLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.INT_LITERAL);
+ }
+
+ @Override
+ public Short getValue() {
+ return (Short) tree.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitShortLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a ShortLiteralNode
+ if (obj == null || !(obj instanceof ShortLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SignedRightShiftNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SignedRightShiftNode.java
new file mode 100644
index 0000000000..3683ab9a3c
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SignedRightShiftNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for bitwise right shift operations with sign extension:
+ *
+ * <pre>
+ * <em>expression</em> &gt;&gt; <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class SignedRightShiftNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public SignedRightShiftNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.RIGHT_SHIFT;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitSignedRightShift(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " >> " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof SignedRightShiftNode)) {
+ return false;
+ }
+ SignedRightShiftNode other = (SignedRightShiftNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateAssignmentNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateAssignmentNode.java
new file mode 100644
index 0000000000..d6c4c532b0
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateAssignmentNode.java
@@ -0,0 +1,60 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for the string concatenation compound assignment:
+ *
+ * <pre>
+ * <em>variable</em> += <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class StringConcatenateAssignmentNode extends Node {
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public StringConcatenateAssignmentNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.PLUS_ASSIGNMENT;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitStringConcatenateAssignment(this, p);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateNode.java
new file mode 100644
index 0000000000..e42d6ac7db
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConcatenateNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for string concatenation:
+ *
+ * <pre>
+ * <em>expression</em> + <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class StringConcatenateNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public StringConcatenateNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.PLUS;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitStringConcatenate(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " + " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof StringConcatenateNode)) {
+ return false;
+ }
+ StringConcatenateNode other = (StringConcatenateNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConversionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConversionNode.java
new file mode 100644
index 0000000000..2f62c8af9a
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringConversionNode.java
@@ -0,0 +1,82 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for the string conversion operation. See JLS 5.1.11 for the definition
+ * of string conversion.
+ *
+ * A {@link StringConversionNode} does not correspond to any tree node in the
+ * parsed AST. It is introduced when a value of non-string type appears in a
+ * context that requires a {@link String}, such as in a string concatenation. A
+ * {@link StringConversionNode} should be treated as a potential call to the
+ * toString method of its operand, but does not necessarily call any method
+ * because null is converted to the string "null".
+ *
+ * Conversion of primitive types to Strings requires first boxing and then
+ * string conversion.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class StringConversionNode extends Node {
+
+ protected Tree tree;
+ protected Node operand;
+
+ // TODO: The type of a string conversion should be a final
+ // TypeMirror representing java.lang.String. Currently we require
+ // the caller to pass in a TypeMirror instead of creating one
+ // through the javax.lang.model.type.Types interface.
+ public StringConversionNode(Tree tree, Node operand, TypeMirror type) {
+ super(type);
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitStringConversion(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "StringConversion(" + getOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof StringConversionNode)) {
+ return false;
+ }
+ StringConversionNode other = (StringConversionNode) obj;
+ return getOperand().equals(other.getOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringLiteralNode.java
new file mode 100644
index 0000000000..e8d9291c55
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/StringLiteralNode.java
@@ -0,0 +1,55 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for an string literal. For example:
+ *
+ * <pre>
+ * <em>"abc"</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ *
+ */
+public class StringLiteralNode extends ValueLiteralNode {
+
+ public StringLiteralNode(LiteralTree t) {
+ super(t);
+ assert t.getKind().equals(Tree.Kind.STRING_LITERAL);
+ }
+
+ @Override
+ public String getValue() {
+ return (String) tree.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitStringLiteral(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // test that obj is a StringLiteralNode
+ if (!(obj instanceof StringLiteralNode)) {
+ return false;
+ }
+ // super method compares values
+ return super.equals(obj);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "\"" + super.toString() + "\"";
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SuperNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SuperNode.java
new file mode 100644
index 0000000000..d6ce410285
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SuperNode.java
@@ -0,0 +1,71 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for a reference to 'super'.
+ *
+ * <pre>
+ * <em>super</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class SuperNode extends Node {
+
+ protected Tree tree;
+
+ public SuperNode(Tree t) {
+ super(InternalUtils.typeOf(t));
+ assert t instanceof IdentifierTree
+ && ((IdentifierTree) t).getName().contentEquals("super");
+ tree = t;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitSuper(this, p);
+ }
+
+ public String getName() {
+ return "super";
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof SuperNode)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getName());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SynchronizedNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SynchronizedNode.java
new file mode 100644
index 0000000000..609a0a20f8
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/SynchronizedNode.java
@@ -0,0 +1,91 @@
+package org.checkerframework.dataflow.cfg.node;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/*
+ * This represents the start and end of synchronized code block.
+ * If startOfBlock == true it is the node preceding a synchronized code block.
+ * Otherwise it is the node immediately after a synchronized code block.
+ */
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+
+import com.sun.source.tree.Tree;
+
+public class SynchronizedNode extends Node {
+
+ protected /*@Nullable*/ Tree tree;
+ protected Node expression;
+ protected boolean startOfBlock;
+
+ public SynchronizedNode(/*@Nullable*/ Tree tree, Node expression, boolean startOfBlock, Types types) {
+ super(types.getNoType(TypeKind.NONE));
+ this.tree = tree;
+ this.expression = expression;
+ this.startOfBlock = startOfBlock;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ public Node getExpression() {
+ return expression;
+ }
+
+ public boolean getIsStartOfBlock() {
+ return startOfBlock;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitSynchronized(this, p);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("synchronized ");
+ sb.append("(" + expression + ")");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof SynchronizedNode)) {
+ return false;
+ }
+ SynchronizedNode other = (SynchronizedNode) obj;
+ if (tree == null && other.getTree() != null) {
+ return false;
+ }
+
+ return getTree().equals(other.getTree())
+ && getExpression().equals(other.getExpression())
+ && startOfBlock == other.startOfBlock;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ if (tree != null) {
+ hash = HashCodeUtils.hash(tree);
+ }
+ hash = HashCodeUtils.hash(startOfBlock);
+ return HashCodeUtils.hash(hash, getExpression());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TernaryExpressionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TernaryExpressionNode.java
new file mode 100644
index 0000000000..514f85944d
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TernaryExpressionNode.java
@@ -0,0 +1,94 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.ConditionalExpressionTree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for a conditional expression:
+ *
+ * <pre>
+ * <em>expression</em> ? <em>expression</em> : <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class TernaryExpressionNode extends Node {
+
+ protected ConditionalExpressionTree tree;
+ protected Node condition;
+ protected Node thenOperand;
+ protected Node elseOperand;
+
+ public TernaryExpressionNode(ConditionalExpressionTree tree, Node condition,
+ Node thenOperand, Node elseOperand) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind().equals(Kind.CONDITIONAL_EXPRESSION);
+ this.tree = tree;
+ this.condition = condition;
+ this.thenOperand = thenOperand;
+ this.elseOperand = elseOperand;
+ }
+
+ public Node getConditionOperand() {
+ return condition;
+ }
+
+ public Node getThenOperand() {
+ return thenOperand;
+ }
+
+ public Node getElseOperand() {
+ return elseOperand;
+ }
+
+ @Override
+ public ConditionalExpressionTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitTernaryExpression(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getConditionOperand() + " ? " + getThenOperand() + " : "
+ + getElseOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof TernaryExpressionNode)) {
+ return false;
+ }
+ TernaryExpressionNode other = (TernaryExpressionNode) obj;
+ return getConditionOperand().equals(other.getConditionOperand())
+ && getThenOperand().equals(other.getThenOperand())
+ && getElseOperand().equals(other.getElseOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getConditionOperand(), getThenOperand(),
+ getElseOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getConditionOperand());
+ list.add(getThenOperand());
+ list.add(getElseOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThisLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThisLiteralNode.java
new file mode 100644
index 0000000000..f62d6ac675
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThisLiteralNode.java
@@ -0,0 +1,48 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+/**
+ * A node for a reference to 'this', either implicit or explicit.
+ *
+ * <pre>
+ * <em>this</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public abstract class ThisLiteralNode extends Node {
+
+ public ThisLiteralNode(TypeMirror type) {
+ super(type);
+ }
+
+ public String getName() {
+ return "this";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ThisLiteralNode)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getName());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThrowNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThrowNode.java
new file mode 100644
index 0000000000..f563cef96b
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ThrowNode.java
@@ -0,0 +1,74 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import com.sun.source.tree.ThrowTree;
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for exception throws:
+ *
+ * <pre>
+ * <em>throw</em> expr
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class ThrowNode extends Node {
+
+ protected ThrowTree tree;
+ protected Node expression;
+
+ public ThrowNode(ThrowTree tree,
+ Node expression, Types types) {
+ super(types.getNoType(TypeKind.NONE));
+ this.tree = tree;
+ this.expression = expression;
+ }
+
+ public Node getExpression() {
+ return expression;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitThrow(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "throw " + expression;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ThrowNode)) {
+ return false;
+ }
+ ThrowNode other = (ThrowNode) obj;
+ return getExpression().equals(other.getExpression());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(expression);
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(expression);
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TypeCastNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TypeCastNode.java
new file mode 100644
index 0000000000..ce287a5cb6
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/TypeCastNode.java
@@ -0,0 +1,76 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for the cast operator:
+ *
+ * (<em>Point</em>) <em>x</em>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class TypeCastNode extends Node {
+
+ protected Tree tree;
+ protected Node operand;
+
+ public TypeCastNode(Tree tree, Node operand, TypeMirror type) {
+ super(type);
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ public TypeMirror getType() {
+ return type;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitTypeCast(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getType() + ")" + getOperand();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof TypeCastNode)) {
+ return false;
+ }
+ TypeCastNode other = (TypeCastNode) obj;
+ // TODO: TypeMirror.equals may be too restrictive.
+ // Check whether Types.isSameType is the better comparison.
+ return getOperand().equals(other.getOperand())
+ && getType().equals(other.getType());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnsignedRightShiftNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnsignedRightShiftNode.java
new file mode 100644
index 0000000000..0ddea5b6c6
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/UnsignedRightShiftNode.java
@@ -0,0 +1,83 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+
+/**
+ * A node for bitwise right shift operations with zero extension:
+ *
+ * <pre>
+ * <em>expression</em> &gt;&gt;&gt; <em>expression</em>
+ * </pre>
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class UnsignedRightShiftNode extends Node {
+
+ protected Tree tree;
+ protected Node left;
+ protected Node right;
+
+ public UnsignedRightShiftNode(Tree tree, Node left, Node right) {
+ super(InternalUtils.typeOf(tree));
+ assert tree.getKind() == Kind.UNSIGNED_RIGHT_SHIFT;
+ this.tree = tree;
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node getLeftOperand() {
+ return left;
+ }
+
+ public Node getRightOperand() {
+ return right;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitUnsignedRightShift(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + getLeftOperand() + " >>> " + getRightOperand() + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof UnsignedRightShiftNode)) {
+ return false;
+ }
+ UnsignedRightShiftNode other = (UnsignedRightShiftNode) obj;
+ return getLeftOperand().equals(other.getLeftOperand())
+ && getRightOperand().equals(other.getRightOperand());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getLeftOperand(), getRightOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ LinkedList<Node> list = new LinkedList<Node>();
+ list.add(getLeftOperand());
+ list.add(getRightOperand());
+ return list;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ValueLiteralNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ValueLiteralNode.java
new file mode 100644
index 0000000000..75d18ea8f7
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/ValueLiteralNode.java
@@ -0,0 +1,80 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import com.sun.source.tree.LiteralTree;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * A node for a literals that have some form of value:
+ * <ul>
+ * <li>integer literal</li>
+ * <li>long literal</li>
+ * <li>char literal</li>
+ * <li>string literal</li>
+ * <li>float literal</li>
+ * <li>double literal</li>
+ * <li>boolean literal</li>
+ * <li>null literal</li>
+ * </ul>
+ *
+ * @author Stefan Heule
+ *
+ */
+public abstract class ValueLiteralNode extends Node {
+
+ protected final LiteralTree tree;
+
+ /**
+ * @return the value of the literal
+ */
+ abstract public /*@Nullable*/ Object getValue();
+
+ public ValueLiteralNode(LiteralTree tree) {
+ super(InternalUtils.typeOf(tree));
+ this.tree = tree;
+ }
+
+ @Override
+ public LiteralTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(getValue());
+ }
+
+ /**
+ * Compare the value of this nodes.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ValueLiteralNode)) {
+ return false;
+ }
+ ValueLiteralNode other = (ValueLiteralNode) obj;
+ Object val = getValue();
+ Object otherVal = other.getValue();
+ return ((val == null || otherVal == null) && val == otherVal) || val.equals(otherVal);
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getValue());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/VariableDeclarationNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/VariableDeclarationNode.java
new file mode 100644
index 0000000000..5841768e38
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/VariableDeclarationNode.java
@@ -0,0 +1,76 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.VariableTree;
+
+/**
+ * A node for a local variable declaration:
+ *
+ * <pre>
+ * <em>modifier</em> <em>type</em> <em>identifier</em>;
+ * </pre>
+ *
+ * Note: Does not have an initializer block, as that will be translated to a
+ * separate {@link AssignmentNode}.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class VariableDeclarationNode extends Node {
+
+ protected VariableTree tree;
+ protected String name;
+
+ // TODO: make modifier accessible
+
+ public VariableDeclarationNode(VariableTree t) {
+ super(InternalUtils.typeOf(t));
+ tree = t;
+ name = tree.getName().toString();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public VariableTree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitVariableDeclaration(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof VariableDeclarationNode)) {
+ return false;
+ }
+ VariableDeclarationNode other = (VariableDeclarationNode) obj;
+ return getName().equals(other.getName());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getName());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.emptyList();
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/WideningConversionNode.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/WideningConversionNode.java
new file mode 100644
index 0000000000..4724ec1066
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/node/WideningConversionNode.java
@@ -0,0 +1,80 @@
+package org.checkerframework.dataflow.cfg.node;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.lang.model.type.TypeMirror;
+
+import org.checkerframework.dataflow.util.HashCodeUtils;
+
+import org.checkerframework.javacutil.TypesUtils;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * A node for the widening primitive conversion operation. See JLS 5.1.2 for the
+ * definition of widening primitive conversion.
+ *
+ * A {@link WideningConversionNode} does not correspond to any tree node in the
+ * parsed AST. It is introduced when a value of some primitive type appears in a
+ * context that requires a different primitive with more bits of precision.
+ *
+ * @author Stefan Heule
+ * @author Charlie Garrett
+ *
+ */
+public class WideningConversionNode extends Node {
+
+ protected Tree tree;
+ protected Node operand;
+
+ public WideningConversionNode(Tree tree, Node operand, TypeMirror type) {
+ super(type);
+ assert TypesUtils.isPrimitive(type) : "non-primitive type in widening conversion";
+ this.tree = tree;
+ this.operand = operand;
+ }
+
+ public Node getOperand() {
+ return operand;
+ }
+
+ public TypeMirror getType() {
+ return type;
+ }
+
+ @Override
+ public Tree getTree() {
+ return tree;
+ }
+
+ @Override
+ public <R, P> R accept(NodeVisitor<R, P> visitor, P p) {
+ return visitor.visitWideningConversion(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "WideningConversion(" + getOperand() + ", " + type + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof WideningConversionNode)) {
+ return false;
+ }
+ WideningConversionNode other = (WideningConversionNode) obj;
+ return getOperand().equals(other.getOperand())
+ && TypesUtils.areSamePrimitiveTypes(getType(), other.getType());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeUtils.hash(getOperand());
+ }
+
+ @Override
+ public Collection<Node> getOperands() {
+ return Collections.singletonList(getOperand());
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/playground/ConstantPropagationPlayground.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/playground/ConstantPropagationPlayground.java
new file mode 100644
index 0000000000..1713f50e29
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/cfg/playground/ConstantPropagationPlayground.java
@@ -0,0 +1,32 @@
+package org.checkerframework.dataflow.cfg.playground;
+
+import org.checkerframework.dataflow.analysis.Analysis;
+import org.checkerframework.dataflow.cfg.JavaSource2CFGDOT;
+import org.checkerframework.dataflow.constantpropagation.Constant;
+import org.checkerframework.dataflow.constantpropagation.ConstantPropagationStore;
+import org.checkerframework.dataflow.constantpropagation.ConstantPropagationTransfer;
+
+public class ConstantPropagationPlayground {
+
+ /**
+ * Run constant propagation for a specific file and create a PDF of the CFG
+ * in the end.
+ */
+ public static void main(String[] args) {
+
+ /* Configuration: change as appropriate */
+ String inputFile = "cfg-input.java"; // input file name and path
+ String outputDir = "cfg"; // output directory
+ String method = "test"; // name of the method to analyze
+ String clazz = "Test"; // name of the class to consider
+
+ // run the analysis and create a PDF file
+ ConstantPropagationTransfer transfer = new ConstantPropagationTransfer();
+ // TODO: correct processing environment
+ Analysis<Constant, ConstantPropagationStore, ConstantPropagationTransfer> analysis = new Analysis<>(
+ null, transfer);
+ JavaSource2CFGDOT.generateDOTofCFG(inputFile, outputDir, method,
+ clazz, true, analysis);
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/Constant.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/Constant.java
new file mode 100644
index 0000000000..291ecf3a69
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/Constant.java
@@ -0,0 +1,101 @@
+package org.checkerframework.dataflow.constantpropagation;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import java.util.Objects;
+
+import org.checkerframework.dataflow.analysis.AbstractValue;
+
+public class Constant implements AbstractValue<Constant> {
+
+ /** What kind of abstract value is this? */
+ protected Type type;
+
+ /** The value of this abstract value (or null) */
+ protected /*@Nullable*/ Integer value;
+
+ public enum Type {
+ CONSTANT, TOP, BOTTOM,
+ }
+
+ public Constant(Type type) {
+ assert !type.equals(Type.CONSTANT);
+ this.type = type;
+ }
+
+ public Constant(Integer value) {
+ this.type = Type.CONSTANT;
+ this.value = value;
+ }
+
+ public boolean isTop() {
+ return type.equals(Type.TOP);
+ }
+
+ public boolean isBottom() {
+ return type.equals(Type.BOTTOM);
+ }
+
+ public boolean isConstant() {
+ return type.equals(Type.CONSTANT);
+ }
+
+ public Integer getValue() {
+ assert isConstant();
+ return value;
+ }
+
+ public Constant copy() {
+ if (isConstant()) {
+ return new Constant(value);
+ }
+ return new Constant(type);
+ }
+
+ @Override
+ public Constant leastUpperBound(Constant other) {
+ if (other.isBottom()) {
+ return this.copy();
+ }
+ if (this.isBottom()) {
+ return other.copy();
+ }
+ if (other.isTop() || this.isTop()) {
+ return new Constant(Type.TOP);
+ }
+ if (other.getValue().equals(getValue())) {
+ return this.copy();
+ }
+ return new Constant(Type.TOP);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof Constant)) {
+ return false;
+ }
+ Constant other = (Constant) obj;
+ return type == other.type && Objects.equals(value, other.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode() + (value != null ? value.hashCode() : 0);
+ }
+
+ @Override
+ public String toString() {
+ switch (type) {
+ case TOP:
+ return "T";
+ case BOTTOM:
+ return "-";
+ case CONSTANT:
+ return value.toString();
+ }
+ assert false;
+ return "???";
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationStore.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationStore.java
new file mode 100644
index 0000000000..da27a6eb21
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationStore.java
@@ -0,0 +1,165 @@
+package org.checkerframework.dataflow.constantpropagation;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.checkerframework.dataflow.analysis.FlowExpressions;
+import org.checkerframework.dataflow.analysis.Store;
+import org.checkerframework.dataflow.cfg.CFGVisualizer;
+import org.checkerframework.dataflow.cfg.node.IntegerLiteralNode;
+import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
+import org.checkerframework.dataflow.cfg.node.Node;
+import org.checkerframework.dataflow.constantpropagation.Constant.Type;
+
+public class ConstantPropagationStore implements
+ Store<ConstantPropagationStore> {
+
+ /** Information about variables gathered so far. */
+ Map<Node, Constant> contents;
+
+ public ConstantPropagationStore() {
+ contents = new HashMap<>();
+ }
+
+ protected ConstantPropagationStore(Map<Node, Constant> contents) {
+ this.contents = contents;
+ }
+
+ public Constant getInformation(Node n) {
+ if (contents.containsKey(n)) {
+ return contents.get(n);
+ }
+ return new Constant(Type.TOP);
+ }
+
+ public void mergeInformation(Node n, Constant val) {
+ Constant value;
+ if (contents.containsKey(n)) {
+ value = val.leastUpperBound(contents.get(n));
+ } else {
+ value = val;
+ }
+ // TODO: remove (only two nodes supported atm)
+ assert n instanceof IntegerLiteralNode
+ || n instanceof LocalVariableNode;
+ contents.put(n, value);
+ }
+
+ public void setInformation(Node n, Constant val) {
+ // TODO: remove (only two nodes supported atm)
+ assert n instanceof IntegerLiteralNode
+ || n instanceof LocalVariableNode;
+ contents.put(n, val);
+ }
+
+ @Override
+ public ConstantPropagationStore copy() {
+ return new ConstantPropagationStore(new HashMap<>(contents));
+ }
+
+ @Override
+ public ConstantPropagationStore leastUpperBound(
+ ConstantPropagationStore other) {
+ Map<Node, Constant> newContents = new HashMap<>();
+
+ // go through all of the information of the other class
+ for (Entry<Node, Constant> e : other.contents.entrySet()) {
+ Node n = e.getKey();
+ Constant otherVal = e.getValue();
+ if (contents.containsKey(n)) {
+ // merge if both contain information about a variable
+ newContents.put(n, otherVal.leastUpperBound(contents.get(n)));
+ } else {
+ // add new information
+ newContents.put(n, otherVal);
+ }
+ }
+
+ for (Entry<Node, Constant> e : contents.entrySet()) {
+ Node n = e.getKey();
+ Constant thisVal = e.getValue();
+ if (!other.contents.containsKey(n)) {
+ // add new information
+ newContents.put(n, thisVal);
+ }
+ }
+
+ return new ConstantPropagationStore(newContents);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof ConstantPropagationStore)) {
+ return false;
+ }
+ ConstantPropagationStore other = (ConstantPropagationStore) o;
+ // go through all of the information of the other object
+ for (Entry<Node, Constant> e : other.contents.entrySet()) {
+ Node n = e.getKey();
+ Constant otherVal = e.getValue();
+ if (otherVal.isBottom()) {
+ continue; // no information
+ }
+ if (contents.containsKey(n)) {
+ if (!otherVal.equals(contents.get(n))) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ // go through all of the information of the this object
+ for (Entry<Node, Constant> e : contents.entrySet()) {
+ Node n = e.getKey();
+ Constant thisVal = e.getValue();
+ if (thisVal.isBottom()) {
+ continue; // no information
+ }
+ if (other.contents.containsKey(n)) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int s = 0;
+ for (Entry<Node, Constant> e : contents.entrySet()) {
+ if (!e.getValue().isBottom()) {
+ s += e.hashCode();
+ }
+ }
+ return s;
+ }
+
+ @Override
+ public String toString() {
+ // only output local variable information
+ Map<Node, Constant> smallerContents = new HashMap<>();
+ for (Entry<Node, Constant> e : contents.entrySet()) {
+ if (e.getKey() instanceof LocalVariableNode) {
+ smallerContents.put(e.getKey(), e.getValue());
+ }
+ }
+ return smallerContents.toString();
+ }
+
+ @Override
+ public boolean canAlias(FlowExpressions.Receiver a,
+ FlowExpressions.Receiver b) {
+ return true;
+ }
+
+ @Override
+ public void visualize(CFGVisualizer<?, ConstantPropagationStore, ?> viz) {
+ // Do nothing since ConstantPropagationStore doesn't support visualize
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationTransfer.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationTransfer.java
new file mode 100644
index 0000000000..c0b2143d12
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/constantpropagation/ConstantPropagationTransfer.java
@@ -0,0 +1,88 @@
+package org.checkerframework.dataflow.constantpropagation;
+
+import org.checkerframework.dataflow.analysis.ConditionalTransferResult;
+import org.checkerframework.dataflow.analysis.RegularTransferResult;
+import org.checkerframework.dataflow.analysis.TransferFunction;
+import org.checkerframework.dataflow.analysis.TransferInput;
+import org.checkerframework.dataflow.analysis.TransferResult;
+import org.checkerframework.dataflow.cfg.UnderlyingAST;
+import org.checkerframework.dataflow.cfg.node.AbstractNodeVisitor;
+import org.checkerframework.dataflow.cfg.node.AssignmentNode;
+import org.checkerframework.dataflow.cfg.node.EqualToNode;
+import org.checkerframework.dataflow.cfg.node.IntegerLiteralNode;
+import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
+import org.checkerframework.dataflow.cfg.node.Node;
+
+import java.util.List;
+
+public class ConstantPropagationTransfer
+ extends
+ AbstractNodeVisitor<TransferResult<Constant, ConstantPropagationStore>, TransferInput<Constant, ConstantPropagationStore>>
+ implements TransferFunction<Constant, ConstantPropagationStore> {
+
+ @Override
+ public ConstantPropagationStore initialStore(UnderlyingAST underlyingAST,
+ List<LocalVariableNode> parameters) {
+ ConstantPropagationStore store = new ConstantPropagationStore();
+ return store;
+ }
+
+ @Override
+ public TransferResult<Constant, ConstantPropagationStore> visitLocalVariable(
+ LocalVariableNode node, TransferInput<Constant, ConstantPropagationStore> before) {
+ ConstantPropagationStore store = before.getRegularStore();
+ Constant value = store.getInformation(node);
+ return new RegularTransferResult<>(value, store);
+ }
+
+ @Override
+ public TransferResult<Constant, ConstantPropagationStore> visitNode(Node n,
+ TransferInput<Constant, ConstantPropagationStore> p) {
+ return new RegularTransferResult<>(null, p.getRegularStore());
+ }
+
+ @Override
+ public TransferResult<Constant, ConstantPropagationStore> visitAssignment(
+ AssignmentNode n,
+ TransferInput<Constant, ConstantPropagationStore> pi) {
+ ConstantPropagationStore p = pi.getRegularStore();
+ Node target = n.getTarget();
+ Constant info = null;
+ if (target instanceof LocalVariableNode) {
+ LocalVariableNode t = (LocalVariableNode) target;
+ info = p.getInformation(n.getExpression());
+ p.setInformation(t, info);
+ }
+ return new RegularTransferResult<>(info, p);
+ }
+
+ @Override
+ public TransferResult<Constant, ConstantPropagationStore> visitIntegerLiteral(
+ IntegerLiteralNode n,
+ TransferInput<Constant, ConstantPropagationStore> pi) {
+ ConstantPropagationStore p = pi.getRegularStore();
+ Constant c = new Constant(n.getValue());
+ p.setInformation(n, c);
+ return new RegularTransferResult<>(c, p);
+ }
+
+ @Override
+ public TransferResult<Constant, ConstantPropagationStore> visitEqualTo(
+ EqualToNode n, TransferInput<Constant, ConstantPropagationStore> pi) {
+ ConstantPropagationStore p = pi.getRegularStore();
+ ConstantPropagationStore old = p.copy();
+ Node left = n.getLeftOperand();
+ Node right = n.getRightOperand();
+ process(p, left, right);
+ process(p, right, left);
+ return new ConditionalTransferResult<>(null, p, old);
+ }
+
+ protected void process(ConstantPropagationStore p, Node a, Node b) {
+ Constant val = p.getInformation(a);
+ if (b instanceof LocalVariableNode && val.isConstant()) {
+ p.setInformation(b, val);
+ }
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Deterministic.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Deterministic.java
new file mode 100644
index 0000000000..fd797f8803
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Deterministic.java
@@ -0,0 +1,92 @@
+package org.checkerframework.dataflow.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A method is called <em>deterministic</em> if it returns the same value
+ * (according to {@code ==}) every time it is called with the same
+ * parameters and in the same environment. The parameters include the
+ * receiver, and the environment includes all of the Java heap (that is,
+ * all fields of all objects and all static variables).
+ * <p>
+ * This annotation is important to pluggable type-checking because, after a
+ * call to a {@code @Deterministic} method, flow-sensitive type refinement
+ * can assume that anything learned about the first invocation is true
+ * about subsequent invocations (so long as no non-{@code @}{@link
+ * SideEffectFree} method call intervenes). For example,
+ * the following code never suffers a null pointer
+ * exception, so the Nullness Checker need not issue a warning:
+ * <pre>{@code if (x.myDeterministicMethod() != null) {
+ x.myDeterministicMethod().hashCode();
+ }}</pre>
+ * <p>
+ * Note that {@code @Deterministic} guarantees that the result is
+ * identical according to {@code ==}, <b>not</b> equal according to
+ * {@code equals}. This means that writing {@code @Deterministic} on a
+ * method that returns a reference is often erroneous unless the
+ * returned value is cached or interned.
+ * <p>
+ * Also see {@link Pure}, which means both deterministic and {@link
+ * SideEffectFree}.
+ * <p>
+ * <b>Analysis:</b>
+ * The Checker Framework performs a conservative analysis to verify a
+ * {@code @Deterministic} annotation. The Checker Framework issues a
+ * warning if the method uses any of the following Java constructs:
+ * <ol>
+ * <li>Assignment to any expression, except for local variables (and method
+ * parameters).
+ * <li>A method invocation of a method that is not {@link Deterministic}.
+ * <li>Construction of a new object.
+ * <li>Catching any exceptions. This is to prevent a method to get a hold of
+ * newly created objects and using these objects (or some property thereof)
+ * to change their return value. For instance, the following method must be
+ * forbidden.
+ * <pre>{@code
+ &#64;Deterministic
+ int f() {
+ try {
+ int b = 0;
+ int a = 1/b;
+ } catch (Throwable t) {
+ return t.hashCode();
+ }
+ return 0;
+ }
+ }</pre>
+ * </ol>
+ * A constructor can be {@code @Pure}, but a constructor <em>invocation</em> is
+ * not deterministic since it returns a different new object each time.
+ * TODO: Side-effect-free constructors could be allowed to set their own fields.
+ * <p>
+ *
+ * Note that the rules for checking currently imply that every {@code
+ * Deterministic} method is also {@link SideEffectFree}. This might change
+ * in the future; in general, a deterministic method does not need to be
+ * side-effect-free.
+ * <p>
+ *
+ * These rules are conservative: any code that passes the checks is
+ * deterministic, but the Checker Framework may issue false positive
+ * warnings, for code that uses one of the forbidden constructs but is
+ * deterministic nonetheless.
+ * <p>
+ *
+ * In fact, the rules are so conservative that checking is currently
+ * disabled by default, but can be enabled via the
+ * {@code -AcheckPurityAnnotations} command-line option.
+ * <p>
+ *
+ * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and flow-sensitive analysis
+ *
+ * @author Stefan Heule
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
+public @interface Deterministic {
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Pure.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Pure.java
new file mode 100644
index 0000000000..a559c1db27
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/Pure.java
@@ -0,0 +1,37 @@
+package org.checkerframework.dataflow.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@code Pure} is a method annotation that means both {@link
+ * SideEffectFree} and {@link Deterministic}. The more important of these,
+ * when performing pluggable type-checking, is usually {@link
+ * SideEffectFree}.
+ *
+ * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and flow-sensitive analysis
+ *
+ * @author Stefan Heule
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
+public @interface Pure {
+ /**
+ * The type of purity.
+ */
+ public static enum Kind {
+ /** The method has no visible side-effects. */
+ SIDE_EFFECT_FREE,
+
+ /**
+ * The method returns exactly the same value when called in the same
+ * environment.
+ */
+ DETERMINISTIC
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/SideEffectFree.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/SideEffectFree.java
new file mode 100644
index 0000000000..eadcc4f49e
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/SideEffectFree.java
@@ -0,0 +1,63 @@
+package org.checkerframework.dataflow.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A method is called <em>side-effect-free</em> if it has no visible
+ * side-effects, such as setting a field of an object that existed before
+ * the method was called.
+ * <p>
+ * Only the visible side-effects are important. The method is allowed to cache
+ * the answer to a computationally expensive query, for instance. It is also
+ * allowed to modify newly-created objects, and a constructor is
+ * side-effect-free if it does not modify any objects that existed before
+ * it was called.
+ * <p>
+ * This annotation is important to pluggable type-checking because if some
+ * fact about an object is known before a call to such a method, then the
+ * fact is still known afterwards, even if the fact is about some non-final
+ * field. When any non-{@code @SideEffectFree} method is called, then a
+ * pluggable type-checker must assume that any field of any accessible
+ * object might have been modified, which annuls the effect of
+ * flow-sensitive type refinement and prevents the pluggable type-checker
+ * from making conclusions that are obvious to a programmer.
+ * <p>
+ * Also see {@link Pure}, which means both side-effect-free and {@link
+ * Deterministic}.
+ * <p>
+ * <b>Analysis:</b>
+ * The Checker Framework performs a conservative analysis to verify a
+ * {@code @SideEffectFree} annotation.
+ * The Checker Framework issues a warning
+ * if the method uses any of the following Java constructs:
+ * <ol>
+ * <li>Assignment to any expression, except for local variables and method
+ * parameters.
+ * <li>A method invocation of a method that is not {@code @SideEffectFree}.
+ * <li>Construction of a new object where the constructor is not {@code @SideEffectFree}.
+ * </ol>
+ * These rules are conservative: any code that passes the checks is
+ * side-effect-free, but the Checker Framework may issue false positive
+ * warnings, for code that uses one of the forbidden constructs but is
+ * side-effect-free nonetheless. In particular, a method that caches its
+ * result will be rejected.
+ * <p>
+ *
+ * In fact, the rules are so conservative that checking is currently
+ * disabled by default, but can be enabled via the
+ * {@code -AcheckPurityAnnotations} command-line option.
+ * <p>
+ *
+ * @checker_framework.manual #type-refinement-purity Side effects, determinism, purity, and flow-sensitive analysis
+ *
+ * @author Stefan Heule
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
+public @interface SideEffectFree {
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/TerminatesExecution.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/TerminatesExecution.java
new file mode 100644
index 0000000000..cd9d5d8c79
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/qual/TerminatesExecution.java
@@ -0,0 +1,38 @@
+package org.checkerframework.dataflow.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@code TerminatesExecution} is a method annotation that indicates that a
+ * method terminates the execution of the program. This can be used to
+ * annotate methods such as {@code System.exit()}.
+ * <p>
+
+ * The annotation enables flow-sensitive type refinement to be more
+ * precise. For example, after
+ * <pre>
+ * if (x == null) {
+ * System.err.println("Bad value supplied");
+ * System.exit(1);
+ * }
+ * </pre>
+ * the Nullness Checker can determine that {@code x} is non-null.
+ *
+ * <p>
+ * The annotation is a <em>trusted</em> annotation, meaning that it is not
+ * checked whether the annotated method really does terminate the program.
+ *
+ * @checker_framework.manual #type-refinement Automatic type refinement (flow-sensitive type qualifier inference)
+ *
+ * @author Stefan Heule
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
+public @interface TerminatesExecution {
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/HashCodeUtils.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/HashCodeUtils.java
new file mode 100644
index 0000000000..b8fa81af27
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/HashCodeUtils.java
@@ -0,0 +1,103 @@
+package org.checkerframework.dataflow.util;
+
+/**
+ * Utility class to implement the {@code hashCode} method.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class HashCodeUtils {
+
+ /** Odd prime number. */
+ private static int prime = 31;
+
+ /** Seed. */
+ private static int seed = 17;
+
+ /** Add a boolean value to a given hash. */
+ public static int hash(int hash, boolean item) {
+ return hash * prime + (item ? 1 : 0);
+ }
+
+ /** Add a char value to a given hash. */
+ public static int hash(int hash, char item) {
+ return hash * prime + item;
+ }
+
+ /** Add an int value to a given hash. */
+ public static int hash(int hash, int item) {
+ return hash * prime + item;
+ }
+
+ /** Add a long value to a given hash. */
+ public static int hash(int hash, long item) {
+ return hash * prime + (int) (item ^ (item >>> 32));
+ }
+
+ /** Add a float value to a given hash. */
+ public static int hash(int hash, float item) {
+ return hash * prime + Float.floatToIntBits(item);
+ }
+
+ /** Add a double value to a given hash. */
+ public static int hash(int hash, double item) {
+ long l = Double.doubleToLongBits(item);
+ return seed * prime + (int) (l ^ (l >>> 32));
+ }
+
+ /** Add an object to a given hash. */
+ public static int hash(int hash, Object item) {
+ if (item == null) {
+ return hash * prime;
+ }
+ return hash * prime + item.hashCode();
+ }
+
+ /** Hash a boolean value. */
+ public static int hash(boolean item) {
+ return (item ? 1 : 0);
+ }
+
+ /** Hash a char value. */
+ public static int hash(char item) {
+ return item;
+ }
+
+ /** Hash an int value. */
+ public static int hash(int item) {
+ return item;
+ }
+
+ /** Hash a long value. */
+ public static int hash(long item) {
+ return (int) (item ^ (item >>> 32));
+ }
+
+ /** Hash a float value. */
+ public static int hash(float item) {
+ return Float.floatToIntBits(item);
+ }
+
+ /** Hash a double value. */
+ public static int hash(double item) {
+ long l = Double.doubleToLongBits(item);
+ return (int) (l ^ (l >>> 32));
+ }
+
+ /** Hash an object. */
+ public static int hash(Object item) {
+ if (item == null) {
+ return 0;
+ }
+ return item.hashCode();
+ }
+
+ /** Hash multiple objects. */
+ public static int hash(Object... items) {
+ int result = seed;
+ for (Object item : items) {
+ result = result * prime + (item == null ? 0 : item.hashCode());
+ }
+ return result;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/MostlySingleton.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/MostlySingleton.java
new file mode 100644
index 0000000000..44bdbd6f61
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/MostlySingleton.java
@@ -0,0 +1,150 @@
+package org.checkerframework.dataflow.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A set that is more efficient than HashSet for 0 and 1 elements.
+ */
+final public class MostlySingleton<T> implements Set<T> {
+ private enum State {
+ EMPTY, SINGLETON, ANY
+ }
+ private State state = State.EMPTY;
+ private T value;
+ private HashSet<T> set;
+
+ @Override
+ public int size() {
+ switch (state) {
+ case EMPTY:
+ return 0;
+ case SINGLETON:
+ return 1;
+ case ANY:
+ return set.size();
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ switch (state) {
+ case EMPTY:
+ return false;
+ case SINGLETON:
+ return Objects.equals(o, value);
+ case ANY:
+ return set.contains(o);
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("fallthrough")
+ public boolean add(T e) {
+ switch (state) {
+ case EMPTY:
+ state = State.SINGLETON;
+ value = e;
+ return true;
+ case SINGLETON:
+ state = State.ANY;
+ set = new HashSet<T>();
+ set.add(value);
+ value = null;
+ // fallthrough
+ case ANY:
+ return set.add(e);
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ switch (state) {
+ case EMPTY:
+ return Collections.emptyIterator();
+ case SINGLETON:
+ return new Iterator<T>() {
+ private boolean hasNext = true;
+
+ @Override
+ public boolean hasNext() {
+ return hasNext;
+ }
+
+ @Override
+ public T next() {
+ if (hasNext) {
+ hasNext = false;
+ return value;
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ case ANY:
+ return set.iterator();
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public Object[] toArray() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <S> S[] toArray(S[] a) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends T> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/NodeUtils.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/NodeUtils.java
new file mode 100644
index 0000000000..3c2efc548e
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/NodeUtils.java
@@ -0,0 +1,45 @@
+package org.checkerframework.dataflow.util;
+
+import org.checkerframework.javacutil.TypesUtils;
+
+import org.checkerframework.dataflow.cfg.node.ConditionalOrNode;
+import org.checkerframework.dataflow.cfg.node.Node;
+
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.tree.JCTree;
+
+/**
+ * A utility class to operate on a given {@link Node}.
+ *
+ * @author Stefan Heule
+ *
+ */
+public class NodeUtils {
+
+ /**
+ * @return true iff {@code node} corresponds to a boolean typed
+ * expression (either the primitive type {@code boolean}, or
+ * class type {@link java.lang.Boolean})
+ */
+ public static boolean isBooleanTypeNode(Node node) {
+
+ if (node instanceof ConditionalOrNode) {
+ return true;
+ }
+
+ // not all nodes have an associated tree, but those are all not of a
+ // boolean type.
+ Tree tree = node.getTree();
+ if (tree == null) {
+ return false;
+ }
+
+ Type type = ((JCTree) tree).type;
+ if (TypesUtils.isBooleanType(type)) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityChecker.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityChecker.java
new file mode 100644
index 0000000000..5fafb7a8a1
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityChecker.java
@@ -0,0 +1,528 @@
+package org.checkerframework.dataflow.util;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey;
+*/
+
+import org.checkerframework.dataflow.qual.Deterministic;
+import org.checkerframework.dataflow.qual.Pure;
+import org.checkerframework.dataflow.qual.Pure.Kind;
+import org.checkerframework.dataflow.qual.SideEffectFree;
+
+import org.checkerframework.javacutil.AnnotationProvider;
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.Pair;
+import org.checkerframework.javacutil.TreeUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+
+import javax.lang.model.element.Element;
+
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.AssertTree;
+import com.sun.source.tree.AssignmentTree;
+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.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.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.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.NewArrayTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.ParenthesizedTree;
+import com.sun.source.tree.ReturnTree;
+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.TryTree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.tree.UnaryTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.tree.WhileLoopTree;
+import com.sun.source.util.SimpleTreeVisitor;
+import com.sun.tools.javac.tree.TreeScanner;
+
+/**
+ * A visitor that determines the purity (as defined by {@link
+ * org.checkerframework.dataflow.qual.SideEffectFree}, {@link org.checkerframework.dataflow.qual.Deterministic},
+ * and {@link org.checkerframework.dataflow.qual.Pure}) of a statement or expression. The
+ * entry point is method {@link #checkPurity}.
+ *
+ * @see SideEffectFree
+ * @see Deterministic
+ * @see Pure
+ *
+ * @author Stefan Heule
+ *
+ */
+public class PurityChecker {
+
+ /**
+ * Compute whether the given statement is
+ * side-effect-free, deterministic, or both.
+ * Returns a result that can be queried.
+ */
+ public static PurityResult checkPurity(Tree statement,
+ AnnotationProvider annoProvider, boolean assumeSideEffectFree) {
+ PurityCheckerHelper helper = new PurityCheckerHelper(annoProvider, assumeSideEffectFree);
+ PurityResult res = helper.scan(statement, new PurityResult());
+ return res;
+ }
+
+ /**
+ * Result of the {@link PurityChecker}.
+ * Can be queried queried regarding whether a given tree was
+ * side-effect-free, deterministic, or both; also gives reasons if
+ * the answer is "no".
+ */
+ public static class PurityResult {
+
+ protected final List<Pair<Tree, String>> notSeFreeReasons;
+ protected final List<Pair<Tree, String>> notDetReasons;
+ protected final List<Pair<Tree, String>> notBothReasons;
+ protected EnumSet<Pure.Kind> types;
+
+ public PurityResult() {
+ notSeFreeReasons = new ArrayList<>();
+ notDetReasons = new ArrayList<>();
+ notBothReasons = new ArrayList<>();
+ types = EnumSet.allOf(Pure.Kind.class);
+ }
+
+ public EnumSet<Pure.Kind> getTypes() {
+ return types;
+ }
+
+ /** Is the method pure w.r.t. a given set of types? */
+ public boolean isPure(Collection<Kind> kinds) {
+ return types.containsAll(kinds);
+ }
+
+ /**
+ * Get the {@code reason}s why the method is not side-effect-free.
+ */
+ public List<Pair<Tree, String>> getNotSeFreeReasons() {
+ return notSeFreeReasons;
+ }
+
+ /**
+ * Add {@code reason} as a reason why the method is not side-effect
+ * free.
+ */
+ public void addNotSeFreeReason(Tree t, String msgId) {
+ notSeFreeReasons.add(Pair.of(t, msgId));
+ types.remove(Kind.SIDE_EFFECT_FREE);
+ }
+
+ /**
+ * Get the {@code reason}s why the method is not deterministic.
+ */
+ public List<Pair<Tree, String>> getNotDetReasons() {
+ return notDetReasons;
+ }
+
+ /**
+ * Add {@code reason} as a reason why the method is not deterministic.
+ */
+ public void addNotDetReason(Tree t, String msgId) {
+ notDetReasons.add(Pair.of(t, msgId));
+ types.remove(Kind.DETERMINISTIC);
+ }
+
+ /**
+ * Get the {@code reason}s why the method is not both side-effect-free
+ * and deterministic.
+ */
+ public List<Pair<Tree, String>> getNotBothReasons() {
+ return notBothReasons;
+ }
+
+ /**
+ * Add {@code reason} as a reason why the method is not both side-effect
+ * free and deterministic.
+ */
+ public void addNotBothReason(Tree t, String msgId) {
+ notBothReasons.add(Pair.of(t, msgId));
+ types.remove(Kind.DETERMINISTIC);
+ types.remove(Kind.SIDE_EFFECT_FREE);
+ }
+ }
+
+ /**
+ * Helper class to keep {@link PurityChecker}'s interface clean. The
+ * implementation is heavily based on {@link TreeScanner}, but some parts of
+ * the AST are skipped (such as types or modifiers). Furthermore, scanning
+ * works differently in that the input parameter (usually named {@code p})
+ * gets "threaded through", instead of using {@code reduce}.
+ */
+ protected static class PurityCheckerHelper
+ extends SimpleTreeVisitor<PurityResult, PurityResult> {
+
+ protected final AnnotationProvider annoProvider;
+ /** True if all methods should be assumed to be @SideEffectFree,
+ * for the purposes of org.checkerframework.dataflow analysis. */
+ private final boolean assumeSideEffectFree;
+ protected /*@Nullable*/ List<Element> methodParameter;
+
+ public PurityCheckerHelper(AnnotationProvider annoProvider, boolean assumeSideEffectFree) {
+ this.annoProvider = annoProvider;
+ this.assumeSideEffectFree = assumeSideEffectFree;
+ }
+
+ /**
+ * Scan a single node.
+ */
+ public PurityResult scan(Tree node, PurityResult p) {
+ return node == null ? p : node.accept(this, p);
+ }
+
+ /**
+ * Scan a list of nodes.
+ */
+ public PurityResult scan(Iterable<? extends Tree> nodes, PurityResult p) {
+ PurityResult r = p;
+ if (nodes != null) {
+ for (Tree node : nodes) {
+ r = scan(node, r);
+ }
+ }
+ return r;
+ }
+
+ @Override
+ protected PurityResult defaultAction(Tree node, PurityResult p) {
+ assert false : "this type of tree is unexpected here";
+ return null;
+ }
+
+ @Override
+ public PurityResult visitClass(ClassTree node, PurityResult p) {
+ return p;
+ }
+
+ @Override
+ public PurityResult visitVariable(VariableTree node, PurityResult p) {
+ return scan(node.getInitializer(), p);
+ }
+
+ @Override
+ public PurityResult visitEmptyStatement(EmptyStatementTree node,
+ PurityResult p) {
+ return p;
+ }
+
+ @Override
+ public PurityResult visitBlock(BlockTree node, PurityResult p) {
+ return scan(node.getStatements(), p);
+ }
+
+ @Override
+ public PurityResult visitDoWhileLoop(DoWhileLoopTree node,
+ PurityResult p) {
+ PurityResult r = scan(node.getStatement(), p);
+ r = scan(node.getCondition(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitWhileLoop(WhileLoopTree node, PurityResult p) {
+ PurityResult r = scan(node.getCondition(), p);
+ r = scan(node.getStatement(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitForLoop(ForLoopTree node, PurityResult p) {
+ PurityResult r = scan(node.getInitializer(), p);
+ r = scan(node.getCondition(), r);
+ r = scan(node.getUpdate(), r);
+ r = scan(node.getStatement(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitEnhancedForLoop(EnhancedForLoopTree node,
+ PurityResult p) {
+ PurityResult r = scan(node.getVariable(), p);
+ r = scan(node.getExpression(), r);
+ r = scan(node.getStatement(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitLabeledStatement(LabeledStatementTree node,
+ PurityResult p) {
+ return scan(node.getStatement(), p);
+ }
+
+ @Override
+ public PurityResult visitSwitch(SwitchTree node, PurityResult p) {
+ PurityResult r = scan(node.getExpression(), p);
+ r = scan(node.getCases(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitCase(CaseTree node, PurityResult p) {
+ PurityResult r = scan(node.getExpression(), p);
+ r = scan(node.getStatements(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitSynchronized(SynchronizedTree node,
+ PurityResult p) {
+ PurityResult r = scan(node.getExpression(), p);
+ r = scan(node.getBlock(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitTry(TryTree node, PurityResult p) {
+ PurityResult r = scan(node.getResources(), p);
+ r = scan(node.getBlock(), r);
+ r = scan(node.getCatches(), r);
+ r = scan(node.getFinallyBlock(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitCatch(CatchTree node, PurityResult p) {
+ p.addNotDetReason(node, "catch");
+ PurityResult r = scan(node.getParameter(), p);
+ r = scan(node.getBlock(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitConditionalExpression(
+ ConditionalExpressionTree node, PurityResult p) {
+ PurityResult r = scan(node.getCondition(), p);
+ r = scan(node.getTrueExpression(), r);
+ r = scan(node.getFalseExpression(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitIf(IfTree node, PurityResult p) {
+ PurityResult r = scan(node.getCondition(), p);
+ r = scan(node.getThenStatement(), r);
+ r = scan(node.getElseStatement(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitExpressionStatement(
+ ExpressionStatementTree node, PurityResult p) {
+ return scan(node.getExpression(), p);
+ }
+
+ @Override
+ public PurityResult visitBreak(BreakTree node, PurityResult p) {
+ return p;
+ }
+
+ @Override
+ public PurityResult visitContinue(ContinueTree node, PurityResult p) {
+ return p;
+ }
+
+ @Override
+ public PurityResult visitReturn(ReturnTree node, PurityResult p) {
+ return scan(node.getExpression(), p);
+ }
+
+ @Override
+ public PurityResult visitThrow(ThrowTree node, PurityResult p) {
+ return scan(node.getExpression(), p);
+ }
+
+ @Override
+ public PurityResult visitAssert(AssertTree node, PurityResult p) {
+ PurityResult r = scan(node.getCondition(), p);
+ r = scan(node.getDetail(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitMethodInvocation(MethodInvocationTree node,
+ PurityResult p) {
+ Element elt = TreeUtils.elementFromUse(node);
+ String reason = "call";
+ if (!PurityUtils.hasPurityAnnotation(annoProvider, elt)) {
+ p.addNotBothReason(node, reason);
+ } else {
+ boolean det = PurityUtils.isDeterministic(annoProvider, elt);
+ boolean seFree = (assumeSideEffectFree
+ || PurityUtils.isSideEffectFree(annoProvider,
+ elt));
+ if (!det && !seFree) {
+ p.addNotBothReason(node, reason);
+ } else if (!det) {
+ p.addNotDetReason(node, reason);
+ } else if (!seFree) {
+ p.addNotSeFreeReason(node, reason);
+ }
+ }
+ PurityResult r = scan(node.getMethodSelect(), p);
+ r = scan(node.getArguments(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitNewClass(NewClassTree node, PurityResult p) {
+ Element methodElement = InternalUtils.symbol(node);
+ boolean sideEffectFree = (assumeSideEffectFree
+ || PurityUtils.isSideEffectFree(annoProvider,
+ methodElement));
+ if (sideEffectFree) {
+ p.addNotDetReason(node, "object.creation");
+ } else {
+ p.addNotBothReason(node, "object.creation");
+ }
+ PurityResult r = scan(node.getEnclosingExpression(), p);
+ r = scan(node.getArguments(), r);
+ r = scan(node.getClassBody(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitNewArray(NewArrayTree node, PurityResult p) {
+ PurityResult r = scan(node.getDimensions(), p);
+ r = scan(node.getInitializers(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitLambdaExpression(LambdaExpressionTree node,
+ PurityResult p) {
+ PurityResult r = scan(node.getParameters(), p);
+ r = scan(node.getBody(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitParenthesized(ParenthesizedTree node,
+ PurityResult p) {
+ return scan(node.getExpression(), p);
+ }
+
+ @Override
+ public PurityResult visitAssignment(AssignmentTree node, PurityResult p) {
+ ExpressionTree variable = node.getVariable();
+ p = assignmentCheck(p, variable);
+ PurityResult r = scan(variable, p);
+ r = scan(node.getExpression(), r);
+ return r;
+ }
+
+ protected PurityResult assignmentCheck(PurityResult p,
+ ExpressionTree variable) {
+ if (TreeUtils.isFieldAccess(variable)) {
+ // rhs is a field access
+ p.addNotBothReason(variable, "assign.field");
+ } else if (variable instanceof ArrayAccessTree) {
+ // rhs is array access
+ p.addNotBothReason(variable, "assign.array");
+ } else {
+ // rhs is a local variable
+ assert isLocalVariable(variable);
+ }
+ return p;
+ }
+
+ protected boolean isLocalVariable(ExpressionTree variable) {
+ return variable instanceof IdentifierTree
+ && !TreeUtils.isFieldAccess(variable);
+ }
+
+ @Override
+ public PurityResult visitCompoundAssignment(
+ CompoundAssignmentTree node, PurityResult p) {
+ ExpressionTree variable = node.getVariable();
+ p = assignmentCheck(p, variable);
+ PurityResult r = scan(variable, p);
+ r = scan(node.getExpression(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitUnary(UnaryTree node, PurityResult p) {
+ return scan(node.getExpression(), p);
+ }
+
+ @Override
+ public PurityResult visitBinary(BinaryTree node, PurityResult p) {
+ PurityResult r = scan(node.getLeftOperand(), p);
+ r = scan(node.getRightOperand(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitTypeCast(TypeCastTree node, PurityResult p) {
+ PurityResult r = scan(node.getExpression(), p);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitInstanceOf(InstanceOfTree node, PurityResult p) {
+ PurityResult r = scan(node.getExpression(), p);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitArrayAccess(ArrayAccessTree node,
+ PurityResult p) {
+ PurityResult r = scan(node.getExpression(), p);
+ r = scan(node.getIndex(), r);
+ return r;
+ }
+
+ @Override
+ public PurityResult visitMemberSelect(MemberSelectTree node,
+ PurityResult p) {
+ return scan(node.getExpression(), p);
+ }
+
+ @Override
+ public PurityResult visitMemberReference(MemberReferenceTree node,
+ PurityResult p) {
+ assert false : "this type of tree is unexpected here";
+ return null;
+ }
+
+ @Override
+ public PurityResult visitIdentifier(IdentifierTree node, PurityResult p) {
+ return p;
+ }
+
+ @Override
+ public PurityResult visitLiteral(LiteralTree node, PurityResult p) {
+ return p;
+ }
+ }
+
+}
diff --git a/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityUtils.java b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityUtils.java
new file mode 100644
index 0000000000..d9186a3b12
--- /dev/null
+++ b/third_party/checker_framework_dataflow/java/org/checkerframework/dataflow/util/PurityUtils.java
@@ -0,0 +1,107 @@
+package org.checkerframework.dataflow.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+
+import org.checkerframework.dataflow.qual.Deterministic;
+import org.checkerframework.dataflow.qual.Pure;
+import org.checkerframework.dataflow.qual.SideEffectFree;
+import org.checkerframework.dataflow.qual.Pure.Kind;
+
+import org.checkerframework.javacutil.AnnotationProvider;
+import org.checkerframework.javacutil.InternalUtils;
+
+import com.sun.source.tree.MethodTree;
+
+/**
+ * An utility class for working with the {@link SideEffectFree}, {@link
+ * Deterministic}, and {@link Pure} annotations.
+ *
+ * @see SideEffectFree
+ * @see Deterministic
+ * @see Pure
+ *
+ * @author Stefan Heule
+ *
+ */
+public class PurityUtils {
+
+ /** Does the method {@code tree} have any purity annotation? */
+ public static boolean hasPurityAnnotation(AnnotationProvider provider,
+ MethodTree tree) {
+ return !getPurityKinds(provider, tree).isEmpty();
+ }
+
+ /** Does the method {@code methodElement} have any purity annotation? */
+ public static boolean hasPurityAnnotation(AnnotationProvider provider,
+ Element methodElement) {
+ return !getPurityKinds(provider, methodElement).isEmpty();
+ }
+
+ /** Is the method {@code tree} deterministic? */
+ public static boolean isDeterministic(AnnotationProvider provider,
+ MethodTree tree) {
+ Element methodElement = InternalUtils.symbol(tree);
+ return isDeterministic(provider, methodElement);
+ }
+
+ /** Is the method {@code methodElement} deterministic? */
+ public static boolean isDeterministic(AnnotationProvider provider,
+ Element methodElement) {
+ List<Kind> kinds = getPurityKinds(provider, methodElement);
+ return kinds.contains(Kind.DETERMINISTIC);
+ }
+
+ /** Is the method {@code tree} side-effect-free? */
+ public static boolean isSideEffectFree(AnnotationProvider provider,
+ MethodTree tree) {
+ Element methodElement = InternalUtils.symbol(tree);
+ return isSideEffectFree(provider, methodElement);
+ }
+
+ /** Is the method {@code methodElement} side-effect-free? */
+ public static boolean isSideEffectFree(AnnotationProvider provider,
+ Element methodElement) {
+ List<Kind> kinds = getPurityKinds(provider, methodElement);
+ return kinds.contains(Kind.SIDE_EFFECT_FREE);
+ }
+
+ /**
+ * @return the types of purity of the method {@code tree}.
+ */
+ public static List<Pure.Kind> getPurityKinds(AnnotationProvider provider,
+ MethodTree tree) {
+ Element methodElement = InternalUtils.symbol(tree);
+ return getPurityKinds(provider, methodElement);
+ }
+
+ /**
+ * @return the types of purity of the method {@code methodElement}.
+ * TODO: should the return type be an EnumSet?
+ */
+ public static List<Pure.Kind> getPurityKinds(AnnotationProvider provider,
+ Element methodElement) {
+ AnnotationMirror pureAnnotation = provider.getDeclAnnotation(
+ methodElement, Pure.class);
+ AnnotationMirror sefAnnotation = provider.getDeclAnnotation(
+ methodElement, SideEffectFree.class);
+ AnnotationMirror detAnnotation = provider.getDeclAnnotation(
+ methodElement, Deterministic.class);
+
+ List<Pure.Kind> kinds = new ArrayList<>();
+ if (pureAnnotation != null) {
+ kinds.add(Kind.DETERMINISTIC);
+ kinds.add(Kind.SIDE_EFFECT_FREE);
+ }
+ if (sefAnnotation != null) {
+ kinds.add(Kind.SIDE_EFFECT_FREE);
+ }
+ if (detAnnotation != null) {
+ kinds.add(Kind.DETERMINISTIC);
+ }
+ return kinds;
+ }
+}