aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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
-rw-r--r--third_party/checker_framework_javacutil/LICENSE.txt417
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AbstractTypeProcessor.java220
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationProvider.java38
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java612
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicAnnotationProvider.java35
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicTypeProcessor.java42
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/CollectionUtils.java26
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ElementUtils.java425
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorHandler.java18
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorReporter.java41
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/InternalUtils.java403
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Pair.java69
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java381
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TreeUtils.java967
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypeAnnotationUtils.java627
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java446
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/dist/ManualTaglet.java129
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/DetachedVarSymbol.java45
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeBuilder.java687
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java144
135 files changed, 23185 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;
+ }
+}
diff --git a/third_party/checker_framework_javacutil/LICENSE.txt b/third_party/checker_framework_javacutil/LICENSE.txt
new file mode 100644
index 0000000000..c4b232d9bc
--- /dev/null
+++ b/third_party/checker_framework_javacutil/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_javacutil/java/org/checkerframework/javacutil/AbstractTypeProcessor.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AbstractTypeProcessor.java
new file mode 100644
index 0000000000..8db8da333c
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AbstractTypeProcessor.java
@@ -0,0 +1,220 @@
+package org.checkerframework.javacutil;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.processing.*;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.comp.CompileStates.CompileState;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Log;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+
+/**
+ * This class is an abstract annotation processor designed to be a
+ * convenient superclass for concrete "type processors", processors that
+ * require the type information in the processed source.
+ *
+ * <p>Type processing occurs in one round after the tool (e.g. Java compiler)
+ * analyzes the source (all sources taken as input to the tool and sources
+ * generated by other annotation processors).
+ *
+ * <p>The tool infrastructure will interact with classes extending this abstract
+ * class as follows.
+ *
+ * <p>
+ * 1-3 are Identical to the {@link Processor} life cycle.
+ * 4-5 are unique to {@code AbstractTypeProcessor} subclasses.
+ *
+ * <ol>
+ *
+ * <li>If an existing {@code Processor} object is not being used, to
+ * create an instance of a processor the tool calls the no-arg
+ * constructor of the processor class.
+ *
+ * <li>Next, the tool calls the {@link #init init} method with
+ * an appropriate {@code ProcessingEnvironment}.
+ *
+ * <li>Afterwards, the tool calls {@link #getSupportedAnnotationTypes
+ * getSupportedAnnotationTypes}, {@link #getSupportedOptions
+ * getSupportedOptions}, and {@link #getSupportedSourceVersion
+ * getSupportedSourceVersion}. These methods are only called once per
+ * run, not on each round.
+ *
+ *
+ * <li>For each class containing a supported annotation, the tool calls
+ * {@link #typeProcess(TypeElement, TreePath) typeProcess} method on the
+ * {@code Processor}. The class is guaranteed to be type-checked Java code
+ * and all the tree type and symbol information is resolved.
+ *
+ * <li>Finally, the tools calls the
+ * {@link #typeProcessingOver() typeProcessingOver} method
+ * on the {@code Processor}.
+ *
+ * </ol>
+ *
+ * <p>The tool is permitted to ask type processors to process a class once
+ * it is analyzed before the rest of classes are analyzed. The tool is also
+ * permitted to stop type processing immediately if any errors are raised,
+ * without invoking {@code typeProcessingOver}
+ *
+ * <p>A subclass may override any of the methods in this class, as long as the
+ * general {@link javax.annotation.processing.Processor Processor}
+ * contract is obeyed, with one notable exception.
+ * {@link #process(Set, RoundEnvironment)} may not be overridden, as it
+ * is called during the declaration annotation phase before classes are analyzed.
+ *
+ * @author Mahmood Ali
+ * @author Werner Dietl
+ */
+public abstract class AbstractTypeProcessor extends AbstractProcessor {
+ /**
+ * The set of fully-qualified element names that should be type-checked.
+ * We store the names of the elements, in order to prevent
+ * possible confusion between different Element instantiations.
+ */
+ private final Set<Name> elements = new HashSet<Name>();
+
+ /**
+ * Method {@link #typeProcessingStart()} must be invoked exactly once,
+ * before any invocation of {@link #typeProcess(TypeElement, TreePath)}.
+ */
+ private boolean hasInvokedTypeProcessingStart = false;
+
+ /**
+ * Method {@link #typeProcessingOver()} must be invoked exactly once,
+ * after the last invocation of {@link #typeProcess(TypeElement, TreePath)}.
+ */
+ private static boolean hasInvokedTypeProcessingOver = false;
+
+ /**
+ * The TaskListener registered for completion of attribution.
+ */
+ private final AttributionTaskListener listener = new AttributionTaskListener();
+
+ /**
+ * Constructor for subclasses to call.
+ */
+ protected AbstractTypeProcessor() { }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Register a TaskListener that will get called after FLOW.
+ */
+ @Override
+ public void init(ProcessingEnvironment env) {
+ super.init(env);
+ JavacTask.instance(env).addTaskListener(listener);
+ Context ctx = ((JavacProcessingEnvironment) processingEnv).getContext();
+ JavaCompiler compiler = JavaCompiler.instance(ctx);
+ compiler.shouldStopPolicyIfNoError = CompileState.max(compiler.shouldStopPolicyIfNoError,
+ CompileState.FLOW);
+ }
+
+ /**
+ * The use of this method is obsolete in type processors. The method is
+ * called during declaration annotation processing phase only.
+ * It registers the names of elements to process.
+ */
+ @Override
+ public final boolean process(Set<? extends TypeElement> annotations,
+ RoundEnvironment roundEnv) {
+ for (TypeElement elem : ElementFilter.typesIn(roundEnv.getRootElements())) {
+ elements.add(elem.getQualifiedName());
+ }
+ return false;
+ }
+
+ /**
+ * A method to be called once before the first call to typeProcess.
+ *
+ * <p>Subclasses may override this method to do any initialization work.
+ */
+ public void typeProcessingStart() {}
+
+ /**
+ * Processes a fully-analyzed class that contains a supported annotation
+ * (see {@link #getSupportedAnnotationTypes()}).
+ *
+ * <p>The passed class is always valid type-checked Java code.
+ *
+ * @param element element of the analyzed class
+ * @param tree the tree path to the element, with the leaf being a
+ * {@link ClassTree}
+ */
+ public abstract void typeProcess(TypeElement element, TreePath tree);
+
+ /**
+ * A method to be called once all the classes are processed and no error
+ * is reported.
+ *
+ * <p>Subclasses may override this method to do any aggregate analysis
+ * (e.g. generate report, persistence) or resource deallocation.
+ *
+ * <p>If an error (a Java error or a processor error) is reported, this
+ * method is not guaranteed to be invoked.
+ */
+ public void typeProcessingOver() { }
+
+ /**
+ * A task listener that invokes the processor whenever a class is fully
+ * analyzed.
+ */
+ private final class AttributionTaskListener implements TaskListener {
+
+ @Override
+ public void finished(TaskEvent e) {
+ if (e.getKind() != TaskEvent.Kind.ANALYZE) {
+ return;
+ }
+
+ if (!hasInvokedTypeProcessingStart) {
+ typeProcessingStart();
+ hasInvokedTypeProcessingStart = true;
+ }
+
+ Log log = Log.instance(((JavacProcessingEnvironment) processingEnv).getContext());
+
+ if (!hasInvokedTypeProcessingOver && elements.isEmpty() && log.nerrors == 0) {
+ typeProcessingOver();
+ hasInvokedTypeProcessingOver = true;
+ }
+
+ if (e.getTypeElement() == null) {
+ throw new AssertionError("event task without a type element");
+ }
+ if (e.getCompilationUnit() == null) {
+ throw new AssertionError("event task without compilation unit");
+ }
+
+ if (!elements.remove(e.getTypeElement().getQualifiedName())) {
+ return;
+ }
+
+ TypeElement elem = e.getTypeElement();
+ TreePath p = Trees.instance(processingEnv).getPath(elem);
+
+ typeProcess(elem, p);
+
+ if (!hasInvokedTypeProcessingOver && elements.isEmpty() && log.nerrors == 0) {
+ typeProcessingOver();
+ hasInvokedTypeProcessingOver = true;
+ }
+ }
+
+ @Override
+ public void started(TaskEvent e) { }
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationProvider.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationProvider.java
new file mode 100644
index 0000000000..580be40ef4
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationProvider.java
@@ -0,0 +1,38 @@
+package org.checkerframework.javacutil;
+
+import java.lang.annotation.Annotation;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+
+import com.sun.source.tree.Tree;
+
+/**
+ * An implementation of AnnotationProvider returns annotations on
+ * Java AST elements.
+ */
+public interface AnnotationProvider {
+
+ /**
+ * Returns the actual annotation mirror used to annotate this type,
+ * whose name equals the passed annotationName if one exists, null otherwise.
+ *
+ * @param anno annotation class
+ * @return the annotation mirror for anno
+ */
+ public AnnotationMirror getDeclAnnotation(Element elt,
+ Class<? extends Annotation> anno);
+
+ /**
+ * Return the annotation on {@code tree} that has the class
+ * {@code target}. If no annotation for the given target class exists,
+ * the result is {@code null}
+ *
+ * @param tree
+ * The tree of which the annotation is returned
+ * @param target
+ * The class of the annotation
+ */
+ public AnnotationMirror getAnnotationMirror(Tree tree,
+ Class<? extends Annotation> target);
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java
new file mode 100644
index 0000000000..19eb74df49
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/AnnotationUtils.java
@@ -0,0 +1,612 @@
+package org.checkerframework.javacutil;
+
+/*>>>
+import org.checkerframework.dataflow.qual.Pure;
+import org.checkerframework.dataflow.qual.SideEffectFree;
+import org.checkerframework.checker.nullness.qual.*;
+import org.checkerframework.checker.interning.qual.*;
+*/
+
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ModifiersTree;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.model.JavacElements;
+/**
+ * A utility class for working with annotations.
+ */
+public class AnnotationUtils {
+
+ // Class cannot be instantiated.
+ private AnnotationUtils() { throw new AssertionError("Class AnnotationUtils cannot be instantiated."); }
+
+ // TODO: hack to clear out static state.
+ // {@link org.checkerframework.qualframework.util.QualifierContext} should
+ // handle instantiation of utility classes.
+ public static void clear() {
+ annotationsFromNames.clear();
+ annotationMirrorNames.clear();
+ annotationMirrorSimpleNames.clear();
+ annotationClassNames.clear();
+ }
+
+ // **********************************************************************
+ // Factory Methods to create instances of AnnotationMirror
+ // **********************************************************************
+
+ /** Caching for annotation creation. */
+ private static final Map<CharSequence, AnnotationMirror> annotationsFromNames
+ = new HashMap<CharSequence, AnnotationMirror>();
+
+
+ private static final int ANNOTATION_CACHE_SIZE = 500;
+
+ /**
+ * Cache names of AnnotationMirrors for faster access. Values in
+ * the map are interned Strings, so they can be compared with ==.
+ */
+ private static final Map<AnnotationMirror, /*@Interned*/ String> annotationMirrorNames
+ = CollectionUtils.createLRUCache(ANNOTATION_CACHE_SIZE);
+
+ /**
+ * Cache simple names of AnnotationMirrors for faster access. Values in
+ * the map are interned Strings, so they can be compared with ==.
+ */
+ private static final Map<AnnotationMirror, /*@Interned*/ String> annotationMirrorSimpleNames
+ = CollectionUtils.createLRUCache(ANNOTATION_CACHE_SIZE);
+
+ /**
+ * Cache names of classes representing AnnotationMirrors for
+ * faster access. Values in the map are interned Strings, so they
+ * can be compared with ==.
+ */
+ private static final Map<Class<? extends Annotation>, /*@Interned*/ String> annotationClassNames
+ = new HashMap<Class<? extends Annotation>, /*@Interned*/ String>();
+
+ /**
+ * Creates an {@link AnnotationMirror} given by a particular
+ * fully-qualified name. getElementValues on the result returns an
+ * empty map.
+ *
+ * @param elements the element utilities to use
+ * @param name the name of the annotation to create
+ * @return an {@link AnnotationMirror} of type {@code} name
+ */
+ public static AnnotationMirror fromName(Elements elements, CharSequence name) {
+ if (annotationsFromNames.containsKey(name)) {
+ return annotationsFromNames.get(name);
+ }
+ final DeclaredType annoType = typeFromName(elements, name);
+ if (annoType == null) {
+ return null;
+ }
+ if (annoType.asElement().getKind() != ElementKind.ANNOTATION_TYPE) {
+ ErrorReporter.errorAbort(annoType + " is not an annotation");
+ return null; // dead code
+ }
+ AnnotationMirror result = new AnnotationMirror() {
+ String toString = "@" + annoType;
+
+ @Override
+ public DeclaredType getAnnotationType() {
+ return annoType;
+ }
+ @Override
+ public Map<? extends ExecutableElement, ? extends AnnotationValue>
+ getElementValues() {
+ return Collections.emptyMap();
+ }
+ /*@SideEffectFree*/
+ @Override
+ public String toString() {
+ return toString;
+ }
+ };
+ annotationsFromNames.put(name, result);
+ return result;
+ }
+
+ /**
+ * Creates an {@link AnnotationMirror} given by a particular annotation
+ * class.
+ *
+ * @param elements the element utilities to use
+ * @param clazz the annotation class
+ * @return an {@link AnnotationMirror} of type given type
+ */
+ public static AnnotationMirror fromClass(Elements elements, Class<? extends Annotation> clazz) {
+ return fromName(elements, clazz.getCanonicalName());
+ }
+
+ /**
+ * A utility method that converts a {@link CharSequence} (usually a {@link
+ * String}) into a {@link TypeMirror} named thereby.
+ *
+ * @param elements the element utilities to use
+ * @param name the name of a type
+ * @return the {@link TypeMirror} corresponding to that name
+ */
+ private static DeclaredType typeFromName(Elements elements, CharSequence name) {
+ /*@Nullable*/ TypeElement typeElt = elements.getTypeElement(name);
+ if (typeElt == null) {
+ return null;
+ }
+
+ return (DeclaredType) typeElt.asType();
+ }
+
+
+ // **********************************************************************
+ // Helper methods to handle annotations. mainly workaround
+ // AnnotationMirror.equals undesired property
+ // (I think the undesired property is that it's reference equality.)
+ // **********************************************************************
+
+ /**
+ * @return the fully-qualified name of an annotation as a String
+ */
+ public static final /*@Interned*/ String annotationName(AnnotationMirror annotation) {
+ if (annotationMirrorNames.containsKey(annotation)) {
+ return annotationMirrorNames.get(annotation);
+ }
+
+ final DeclaredType annoType = annotation.getAnnotationType();
+ final TypeElement elm = (TypeElement) annoType.asElement();
+ /*@Interned*/ String name = elm.getQualifiedName().toString().intern();
+ annotationMirrorNames.put(annotation, name);
+ return name;
+ }
+
+ /**
+ * @return the simple name of an annotation as a String
+ */
+ public static String annotationSimpleName(AnnotationMirror annotation) {
+ if (annotationMirrorSimpleNames.containsKey(annotation)) {
+ return annotationMirrorSimpleNames.get(annotation);
+ }
+
+ final DeclaredType annoType = annotation.getAnnotationType();
+ final TypeElement elm = (TypeElement) annoType.asElement();
+ /*@Interned*/ String name = elm.getSimpleName().toString().intern();
+ annotationMirrorSimpleNames.put(annotation, name);
+ return name;
+ }
+
+ /**
+ * Checks if both annotations are the same.
+ *
+ * Returns true iff both annotations are of the same type and have the
+ * same annotation values. This behavior differs from
+ * {@code AnnotationMirror.equals(Object)}. The equals method returns
+ * true iff both annotations are the same and annotate the same annotation
+ * target (e.g. field, variable, etc).
+ *
+ * @return true iff a1 and a2 are the same annotation
+ */
+ public static boolean areSame(/*@Nullable*/ AnnotationMirror a1, /*@Nullable*/ AnnotationMirror a2) {
+ if (a1 != null && a2 != null) {
+ if (annotationName(a1) != annotationName(a2)) {
+ return false;
+ }
+
+ Map<? extends ExecutableElement, ? extends AnnotationValue> elval1 = getElementValuesWithDefaults(a1);
+ Map<? extends ExecutableElement, ? extends AnnotationValue> elval2 = getElementValuesWithDefaults(a2);
+
+ return elval1.toString().equals(elval2.toString());
+ }
+
+ // only true, iff both are null
+ return a1 == a2;
+ }
+
+ /**
+ * @see #areSame(AnnotationMirror, AnnotationMirror)
+ * @return true iff a1 and a2 have the same annotation type
+ */
+ public static boolean areSameIgnoringValues(AnnotationMirror a1, AnnotationMirror a2) {
+ if (a1 != null && a2 != null) {
+ return annotationName(a1) == annotationName(a2);
+ }
+ return a1 == a2;
+ }
+
+ /**
+ * Checks that the annotation {@code am} has the name {@code aname}. Values
+ * are ignored.
+ */
+ public static boolean areSameByName(AnnotationMirror am, /*@Interned*/ String aname) {
+ // Both strings are interned.
+ return annotationName(am) == aname;
+ }
+
+ /**
+ * Checks that the annotation {@code am} has the name of {@code anno}.
+ * Values are ignored.
+ */
+ public static boolean areSameByClass(AnnotationMirror am,
+ Class<? extends Annotation> anno) {
+ /*@Interned*/ String canonicalName;
+ if (annotationClassNames.containsKey(anno)) {
+ canonicalName = annotationClassNames.get(anno);
+ } else {
+ canonicalName = anno.getCanonicalName().intern();
+ annotationClassNames.put(anno, canonicalName);
+ }
+ return areSameByName(am, canonicalName);
+ }
+
+ /**
+ * Checks that two collections contain the same annotations.
+ *
+ * @return true iff c1 and c2 contain the same annotations
+ */
+ public static boolean areSame(Collection<? extends AnnotationMirror> c1, Collection<? extends AnnotationMirror> c2) {
+ if (c1.size() != c2.size()) {
+ return false;
+ }
+ if (c1.size() == 1) {
+ return areSame(c1.iterator().next(), c2.iterator().next());
+ }
+
+ Set<AnnotationMirror> s1 = createAnnotationSet();
+ Set<AnnotationMirror> s2 = createAnnotationSet();
+ s1.addAll(c1);
+ s2.addAll(c2);
+
+ // depend on the fact that Set is an ordered set.
+ Iterator<AnnotationMirror> iter1 = s1.iterator();
+ Iterator<AnnotationMirror> iter2 = s2.iterator();
+
+ while (iter1.hasNext()) {
+ AnnotationMirror anno1 = iter1.next();
+ AnnotationMirror anno2 = iter2.next();
+ if (!areSame(anno1, anno2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks that the collection contains the annotation.
+ * Using Collection.contains does not always work, because it
+ * does not use areSame for comparison.
+ *
+ * @return true iff c contains anno, according to areSame
+ */
+ public static boolean containsSame(Collection<? extends AnnotationMirror> c, AnnotationMirror anno) {
+ for (AnnotationMirror an : c) {
+ if (AnnotationUtils.areSame(an, anno)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks that the collection contains the annotation.
+ * Using Collection.contains does not always work, because it
+ * does not use areSame for comparison.
+ *
+ * @return true iff c contains anno, according to areSameByClass
+ */
+ public static boolean containsSameByClass(Collection<? extends AnnotationMirror> c, Class<? extends Annotation> anno) {
+ for (AnnotationMirror an : c) {
+ if (AnnotationUtils.areSameByClass(an, anno)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks that the collection contains the annotation ignoring values.
+ * Using Collection.contains does not always work, because it
+ * does not use areSameIgnoringValues for comparison.
+ *
+ * @return true iff c contains anno, according to areSameIgnoringValues
+ */
+ public static boolean containsSameIgnoringValues(Collection<? extends AnnotationMirror> c, AnnotationMirror anno) {
+ for (AnnotationMirror an : c) {
+ if (AnnotationUtils.areSameIgnoringValues(an, anno)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static final Comparator<AnnotationMirror> ANNOTATION_ORDERING
+ = new Comparator<AnnotationMirror>() {
+ @Override
+ public int compare(AnnotationMirror a1, AnnotationMirror a2) {
+ String n1 = a1.toString();
+ String n2 = a2.toString();
+
+ return n1.compareTo(n2);
+ }
+ };
+
+ /**
+ * provide ordering for {@link AnnotationMirror} based on their fully
+ * qualified name. The ordering ignores annotation values when ordering.
+ *
+ * The ordering is meant to be used as {@link TreeSet} or {@link TreeMap}
+ * ordering. A {@link Set} should not contain two annotations that only
+ * differ in values.
+ */
+ public static Comparator<AnnotationMirror> annotationOrdering() {
+ return ANNOTATION_ORDERING;
+ }
+
+ /**
+ * Create a map suitable for storing {@link AnnotationMirror} as keys.
+ *
+ * It can store one instance of {@link AnnotationMirror} of a given
+ * declared type, regardless of the annotation element values.
+ *
+ * @param <V> the value of the map
+ * @return a new map with {@link AnnotationMirror} as key
+ */
+ public static <V> Map<AnnotationMirror, V> createAnnotationMap() {
+ return new TreeMap<AnnotationMirror, V>(annotationOrdering());
+ }
+
+ /**
+ * Constructs a {@link Set} suitable for storing {@link AnnotationMirror}s.
+ *
+ * It stores at most once instance of {@link AnnotationMirror} of a given
+ * type, regardless of the annotation element values.
+ *
+ * @return a new set to store {@link AnnotationMirror} as element
+ */
+ public static Set<AnnotationMirror> createAnnotationSet() {
+ return new TreeSet<AnnotationMirror>(annotationOrdering());
+ }
+
+ /** Returns true if the given annotation has a @Inherited meta-annotation. */
+ public static boolean hasInheritedMeta(AnnotationMirror anno) {
+ return anno.getAnnotationType().asElement().getAnnotation(Inherited.class) != null;
+ }
+
+
+ // **********************************************************************
+ // Extractors for annotation values
+ // **********************************************************************
+
+ /**
+ * Returns the values of an annotation's attributes, including defaults.
+ * The method with the same name in JavacElements cannot be used directly,
+ * because it includes a cast to Attribute.Compound, which doesn't hold
+ * for annotations generated by the Checker Framework.
+ *
+ * @see AnnotationMirror#getElementValues()
+ * @see JavacElements#getElementValuesWithDefaults(AnnotationMirror)
+ *
+ * @param ad annotation to examine
+ * @return the values of the annotation's elements, including defaults
+ */
+ public static Map<? extends ExecutableElement, ? extends AnnotationValue>
+ getElementValuesWithDefaults(AnnotationMirror ad) {
+ Map<ExecutableElement, AnnotationValue> valMap
+ = new HashMap<ExecutableElement, AnnotationValue>();
+ if (ad.getElementValues() != null) {
+ valMap.putAll(ad.getElementValues());
+ }
+ for (ExecutableElement meth :
+ ElementFilter.methodsIn(ad.getAnnotationType().asElement().getEnclosedElements())) {
+ AnnotationValue defaultValue = meth.getDefaultValue();
+ if (defaultValue != null && !valMap.containsKey(meth)) {
+ valMap.put(meth, defaultValue);
+ }
+ }
+ return valMap;
+ }
+
+
+ /**
+ * Verify whether the attribute with the name {@code name} exists in
+ * the annotation {@code anno}.
+ *
+ * @param anno the annotation to examine
+ * @param name the name of the attribute
+ * @return whether the attribute exists in anno
+ */
+ public static <T> boolean hasElementValue(AnnotationMirror anno, CharSequence name) {
+ Map<? extends ExecutableElement, ? extends AnnotationValue> valmap = anno.getElementValues();
+ for (ExecutableElement elem : valmap.keySet()) {
+ if (elem.getSimpleName().contentEquals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the attribute with the name {@code name} of the annotation
+ * {@code anno}. The result is expected to have type {@code expectedType}.
+ *
+ * <p>
+ * <em>Note 1</em>: The method does not work well for attributes of an array
+ * type (as it would return a list of {@link AnnotationValue}s). Use
+ * {@code getElementValueArray} instead.
+ *
+ * <p>
+ * <em>Note 2</em>: The method does not work for attributes of an enum type,
+ * as the AnnotationValue is a VarSymbol and would be cast to the enum type,
+ * which doesn't work. Use {@code getElementValueEnum} instead.
+ *
+ *
+ * @param anno the annotation to disassemble
+ * @param name the name of the attribute to access
+ * @param expectedType the expected type used to cast the return type
+ * @param useDefaults whether to apply default values to the attribute
+ * @return the value of the attribute with the given name
+ */
+ public static <T> T getElementValue(AnnotationMirror anno, CharSequence name,
+ Class<T> expectedType, boolean useDefaults) {
+ Map<? extends ExecutableElement, ? extends AnnotationValue> valmap;
+ if (useDefaults) {
+ valmap = getElementValuesWithDefaults(anno);
+ } else {
+ valmap = anno.getElementValues();
+ }
+ for (ExecutableElement elem : valmap.keySet()) {
+ if (elem.getSimpleName().contentEquals(name)) {
+ AnnotationValue val = valmap.get(elem);
+ return expectedType.cast(val.getValue());
+ }
+ }
+ ErrorReporter.errorAbort("No element with name \'" + name + "\' in annotation " + anno);
+ return null; // dead code
+ }
+
+ /**
+ * Version that is suitable for Enum elements.
+ */
+ public static <T extends Enum<T>> T getElementValueEnum(
+ AnnotationMirror anno, CharSequence name, Class<T> t,
+ boolean useDefaults) {
+ VarSymbol vs = getElementValue(anno, name, VarSymbol.class, useDefaults);
+ T value = Enum.valueOf(t, vs.getSimpleName().toString());
+ return value;
+ }
+
+ /**
+ * Get the attribute with the name {@code name} of the annotation
+ * {@code anno}, where the attribute has an array type. One element of the
+ * result is expected to have type {@code expectedType}.
+ *
+ * Parameter useDefaults is used to determine whether default values
+ * should be used for annotation values. Finding defaults requires
+ * more computation, so should be false when no defaulting is needed.
+ *
+ * @param anno the annotation to disassemble
+ * @param name the name of the attribute to access
+ * @param expectedType the expected type used to cast the return type
+ * @param useDefaults whether to apply default values to the attribute
+ * @return the value of the attribute with the given name
+ */
+ public static <T> List<T> getElementValueArray(AnnotationMirror anno,
+ CharSequence name, Class<T> expectedType, boolean useDefaults) {
+ @SuppressWarnings("unchecked")
+ List<AnnotationValue> la = getElementValue(anno, name, List.class, useDefaults);
+ List<T> result = new ArrayList<T>(la.size());
+ for (AnnotationValue a : la) {
+ result.add(expectedType.cast(a.getValue()));
+ }
+ return result;
+ }
+
+ /**
+ * Get the attribute with the name {@code name} of the annotation
+ * {@code anno}, or the default value if no attribute is present explicitly,
+ * where the attribute has an array type and the elements are {@code Enum}s.
+ * One element of the result is expected to have type {@code expectedType}.
+ */
+ public static <T extends Enum<T>> List<T> getElementValueEnumArray(
+ AnnotationMirror anno, CharSequence name, Class<T> t,
+ boolean useDefaults) {
+ @SuppressWarnings("unchecked")
+ List<AnnotationValue> la = getElementValue(anno, name, List.class, useDefaults);
+ List<T> result = new ArrayList<T>(la.size());
+ for (AnnotationValue a : la) {
+ T value = Enum.valueOf(t, a.getValue().toString());
+ result.add(value);
+ }
+ return result;
+ }
+
+ /**
+ * Get the Name of the class that is referenced by attribute 'name'.
+ * This is a convenience method for the most common use-case.
+ * Like getElementValue(anno, name, ClassType.class).getQualifiedName(), but
+ * this method ensures consistent use of the qualified name.
+ */
+ public static Name getElementValueClassName(AnnotationMirror anno, CharSequence name,
+ boolean useDefaults) {
+ Type.ClassType ct = getElementValue(anno, name, Type.ClassType.class, useDefaults);
+ // TODO: Is it a problem that this returns the type parameters too? Should I cut them off?
+ return ct.asElement().getQualifiedName();
+ }
+
+ /**
+ * Get the Class that is referenced by attribute 'name'.
+ * This method uses Class.forName to load the class. It returns
+ * null if the class wasn't found.
+ */
+ public static Class<?> getElementValueClass(AnnotationMirror anno, CharSequence name,
+ boolean useDefaults) {
+ Name cn = getElementValueClassName(anno, name, useDefaults);
+ try {
+ Class<?> cls = Class.forName(cn.toString());
+ return cls;
+ } catch (ClassNotFoundException e) {
+ ErrorReporter.errorAbort("Could not load class '" + cn + "' for field '" + name +
+ "' in annotation " + anno, e);
+ return null; // dead code
+ }
+ }
+
+ /**
+ * See checkers.types.QualifierHierarchy#updateMappingToMutableSet(QualifierHierarchy, Map, Object, AnnotationMirror)
+ * (Not linked because it is in an independent project.
+ */
+ public static <T> void updateMappingToImmutableSet(Map<T, Set<AnnotationMirror>> map,
+ T key, Set<AnnotationMirror> newQual) {
+
+ Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet();
+ // TODO: if T is also an AnnotationMirror, should we use areSame?
+ if (!map.containsKey(key)) {
+ result.addAll(newQual);
+ } else {
+ result.addAll(map.get(key));
+ result.addAll(newQual);
+ }
+ map.put(key, Collections.unmodifiableSet(result));
+ }
+
+ /**
+ * Returns the annotations explicitly written on a constructor result.
+ * Callers should check that {@code constructorDeclaration} is in fact a declaration
+ * of a constructor.
+ *
+ * @param constructorDeclaration declaration tree of constructor
+ * @return set of annotations explicit on the resulting type of the constructor
+ */
+ public static Set<AnnotationMirror> getExplicitAnnotationsOnConstructorResult(MethodTree constructorDeclaration) {
+ Set<AnnotationMirror> annotationSet = AnnotationUtils.createAnnotationSet();
+ ModifiersTree modifiersTree = constructorDeclaration.getModifiers();
+ if (modifiersTree != null) {
+ List<? extends AnnotationTree> annotationTrees = modifiersTree.getAnnotations();
+ annotationSet.addAll(InternalUtils.annotationsFromTypeAnnotationTrees(annotationTrees));
+ }
+ return annotationSet;
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicAnnotationProvider.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicAnnotationProvider.java
new file mode 100644
index 0000000000..b885b5cbea
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicAnnotationProvider.java
@@ -0,0 +1,35 @@
+package org.checkerframework.javacutil;
+
+import java.lang.annotation.Annotation;
+
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+
+import com.sun.source.tree.Tree;
+
+public class BasicAnnotationProvider implements AnnotationProvider {
+
+ @Override
+ public AnnotationMirror getDeclAnnotation(Element elt,
+ Class<? extends Annotation> anno) {
+ List<? extends AnnotationMirror> annotationMirrors = elt
+ .getAnnotationMirrors();
+
+ // Then look at the real annotations.
+ for (AnnotationMirror am : annotationMirrors) {
+ if (AnnotationUtils.areSameByClass(am, anno)) {
+ return am;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public AnnotationMirror getAnnotationMirror(Tree tree,
+ Class<? extends Annotation> target) {
+ return null;
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicTypeProcessor.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicTypeProcessor.java
new file mode 100644
index 0000000000..1fa1e6cb82
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/BasicTypeProcessor.java
@@ -0,0 +1,42 @@
+package org.checkerframework.javacutil;
+
+import javax.lang.model.element.TypeElement;
+
+import com.sun.source.tree.CompilationUnitTree;
+
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+
+/**
+ * Process the types in an AST in a trivial manner, with hooks for derived classes
+ * to actually do something.
+ */
+public abstract class BasicTypeProcessor extends AbstractTypeProcessor {
+ /** The source tree that's being scanned. */
+ protected CompilationUnitTree currentRoot;
+
+ /**
+ * Create a TreePathScanner at the given root.
+ */
+ protected abstract TreePathScanner<?, ?> createTreePathScanner(CompilationUnitTree root);
+
+ /**
+ * Visit the tree path for the type element.
+ */
+ @Override
+ public void typeProcess(TypeElement e, TreePath p) {
+ currentRoot = p.getCompilationUnit();
+
+ TreePathScanner<?, ?> scanner = null;
+ try {
+ scanner = createTreePathScanner(currentRoot);
+ scanner.scan(p, null);
+ } catch (Throwable t) {
+ System.err.println("BasicTypeProcessor.typeProcess: unexpected Throwable (" +
+ t.getClass().getSimpleName() + ") when processing "
+ + currentRoot.getSourceFile().getName() +
+ (t.getMessage()!=null ? "; message: " + t.getMessage() : ""));
+ }
+ }
+
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/CollectionUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/CollectionUtils.java
new file mode 100644
index 0000000000..eaaa69115d
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/CollectionUtils.java
@@ -0,0 +1,26 @@
+package org.checkerframework.javacutil;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Utility methods related to Java Collections
+ */
+public class CollectionUtils {
+
+ /**
+ * A Utility method for creating LRU cache
+ * @param size size of the cache
+ * @return a new cache with the provided size
+ */
+ public static <K, V> Map<K, V> createLRUCache(final int size) {
+ return new LinkedHashMap<K, V>() {
+
+ private static final long serialVersionUID = 5261489276168775084L;
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K, V> entry) {
+ return size() > size;
+ }
+ };
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ElementUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ElementUtils.java
new file mode 100644
index 0000000000..19f0500585
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ElementUtils.java
@@ -0,0 +1,425 @@
+package org.checkerframework.javacutil;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import static com.sun.tools.javac.code.Flags.ABSTRACT;
+import static com.sun.tools.javac.code.Flags.EFFECTIVELY_FINAL;
+import static com.sun.tools.javac.code.Flags.FINAL;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+
+import com.sun.tools.javac.code.Symbol;
+
+/**
+ * A Utility class for analyzing {@code Element}s.
+ */
+public class ElementUtils {
+
+ // Class cannot be instantiated.
+ private ElementUtils() { throw new AssertionError("Class ElementUtils cannot be instantiated."); }
+
+ /**
+ * Returns the innermost type element enclosing the given element
+ *
+ * @param elem the enclosed element of a class
+ * @return the innermost type element
+ */
+ public static TypeElement enclosingClass(final Element elem) {
+ Element result = elem;
+ while (result != null && !result.getKind().isClass()
+ && !result.getKind().isInterface()) {
+ /*@Nullable*/ Element encl = result.getEnclosingElement();
+ result = encl;
+ }
+ return (TypeElement) result;
+ }
+
+ /**
+ * Returns the innermost package element enclosing the given element.
+ * The same effect as {@link javax.lang.model.util.Elements#getPackageOf(Element)}.
+ * Returns the element itself if it is a package.
+ *
+ * @param elem the enclosed element of a package
+ * @return the innermost package element
+ *
+ */
+ public static PackageElement enclosingPackage(final Element elem) {
+ Element result = elem;
+ while (result != null && result.getKind() != ElementKind.PACKAGE) {
+ /*@Nullable*/ Element encl = result.getEnclosingElement();
+ result = encl;
+ }
+ return (PackageElement) result;
+ }
+
+ /**
+ * Returns the "parent" package element for the given package element.
+ * For package "A.B" it gives "A".
+ * For package "A" it gives the default package.
+ * For the default package it returns null;
+ *
+ * Note that packages are not enclosed within each other, we have to manually climb
+ * the namespaces. Calling "enclosingPackage" on a package element returns the
+ * package element itself again.
+ *
+ * @param elem the package to start from
+ * @return the parent package element
+ *
+ */
+ public static PackageElement parentPackage(final Elements e, final PackageElement elem) {
+ // The following might do the same thing:
+ // ((Symbol) elt).owner;
+ // TODO: verify and see whether the change is worth it.
+ String fqnstart = elem.getQualifiedName().toString();
+ String fqn = fqnstart;
+ if (fqn != null && !fqn.isEmpty() && fqn.contains(".")) {
+ fqn = fqn.substring(0, fqn.lastIndexOf('.'));
+ return e.getPackageElement(fqn);
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the element is a static element: whether it is a static
+ * field, static method, or static class
+ *
+ * @return true if element is static
+ */
+ public static boolean isStatic(Element element) {
+ return element.getModifiers().contains(Modifier.STATIC);
+ }
+
+ /**
+ * Returns true if the element is a final element: a final field, final
+ * method, or final class
+ *
+ * @return true if the element is final
+ */
+ public static boolean isFinal(Element element) {
+ return element.getModifiers().contains(Modifier.FINAL);
+ }
+
+ /**
+ * Returns true if the element is a effectively final element.
+ *
+ * @return true if the element is effectively final
+ */
+ public static boolean isEffectivelyFinal(Element element) {
+ Symbol sym = (Symbol) element;
+ if (sym.getEnclosingElement().getKind() == ElementKind.METHOD &&
+ (sym.getEnclosingElement().flags() & ABSTRACT) != 0) {
+ return true;
+ }
+ return (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0;
+ }
+
+ /**
+ * Returns the {@code TypeMirror} for usage of Element as a value. It
+ * returns the return type of a method element, the class type of a
+ * constructor, or simply the type mirror of the element itself.
+ *
+ * @return the type for the element used as a value
+ */
+ public static TypeMirror getType(Element element) {
+ if (element.getKind() == ElementKind.METHOD) {
+ return ((ExecutableElement)element).getReturnType();
+ } else if (element.getKind() == ElementKind.CONSTRUCTOR) {
+ return enclosingClass(element).asType();
+ } else {
+ return element.asType();
+ }
+ }
+
+ /**
+ * Returns the qualified name of the inner most class enclosing
+ * the provided {@code Element}
+ *
+ * @param element
+ * an element enclosed by a class, or a
+ * {@code TypeElement}
+ * @return the qualified {@code Name} of the innermost class
+ * enclosing the element
+ */
+ public static /*@Nullable*/ Name getQualifiedClassName(Element element) {
+ if (element.getKind() == ElementKind.PACKAGE) {
+ PackageElement elem = (PackageElement) element;
+ return elem.getQualifiedName();
+ }
+
+ TypeElement elem = enclosingClass(element);
+ if (elem == null) {
+ return null;
+ }
+
+ return elem.getQualifiedName();
+ }
+
+ /**
+ * Returns a verbose name that identifies the element.
+ */
+ public static String getVerboseName(Element elt) {
+ if (elt.getKind() == ElementKind.PACKAGE ||
+ elt.getKind().isClass() ||
+ elt.getKind().isInterface()) {
+ return getQualifiedClassName(elt).toString();
+ } else {
+ return getQualifiedClassName(elt) + "." + elt.toString();
+ }
+ }
+
+ /**
+ * Check if the element is an element for 'java.lang.Object'
+ *
+ * @param element the type element
+ * @return true iff the element is java.lang.Object element
+ */
+ public static boolean isObject(TypeElement element) {
+ return element.getQualifiedName().contentEquals("java.lang.Object");
+ }
+
+ /**
+ * Returns true if the element is a constant time reference
+ */
+ public static boolean isCompileTimeConstant(Element elt) {
+ return elt != null
+ && (elt.getKind() == ElementKind.FIELD || elt.getKind() == ElementKind.LOCAL_VARIABLE)
+ && ((VariableElement)elt).getConstantValue() != null;
+ }
+
+ /**
+ * Returns true if the element is declared in ByteCode.
+ * Always return false if elt is a package.
+ */
+ public static boolean isElementFromByteCode(Element elt) {
+ if (elt == null) {
+ return false;
+ }
+
+ if (elt instanceof Symbol.ClassSymbol) {
+ Symbol.ClassSymbol clss = (Symbol.ClassSymbol) elt;
+ if (null != clss.classfile) {
+ // The class file could be a .java file
+ return clss.classfile.getName().endsWith(".class");
+ } else {
+ return false;
+ }
+ }
+ return isElementFromByteCode(elt.getEnclosingElement(), elt);
+ }
+
+ /**
+ * Returns true if the element is declared in ByteCode.
+ * Always return false if elt is a package.
+ */
+ private static boolean isElementFromByteCode(Element elt, Element orig) {
+ if (elt == null) {
+ return false;
+ }
+ if (elt instanceof Symbol.ClassSymbol) {
+ Symbol.ClassSymbol clss = (Symbol.ClassSymbol) elt;
+ if (null != clss.classfile) {
+ // The class file could be a .java file
+ return (clss.classfile.getName().endsWith(".class") ||
+ clss.classfile.getName().endsWith(".class)") ||
+ clss.classfile.getName().endsWith(".class)]"));
+ } else {
+ return false;
+ }
+ }
+ return isElementFromByteCode(elt.getEnclosingElement(), elt);
+ }
+
+ /**
+ * Returns the field of the class
+ */
+ public static VariableElement findFieldInType(TypeElement type, String name) {
+ for (VariableElement field: ElementFilter.fieldsIn(type.getEnclosedElements())) {
+ if (field.getSimpleName().toString().equals(name)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ public static Set<VariableElement> findFieldsInType(TypeElement type, Collection<String> names) {
+ Set<VariableElement> results = new HashSet<VariableElement>();
+ for (VariableElement field: ElementFilter.fieldsIn(type.getEnclosedElements())) {
+ if (names.contains(field.getSimpleName().toString())) {
+ results.add(field);
+ }
+ }
+ return results;
+ }
+
+ public static boolean isError(Element element) {
+ return element.getClass().getName().equals("com.sun.tools.javac.comp.Resolve$SymbolNotFoundError");
+ }
+
+ /**
+ * Does the given element need a receiver for accesses?
+ * For example, an access to a local variable does not require a receiver.
+ *
+ * @param element the element to test
+ * @return whether the element requires a receiver for accesses
+ */
+ public static boolean hasReceiver(Element element) {
+ return (element.getKind().isField() ||
+ element.getKind() == ElementKind.METHOD ||
+ element.getKind() == ElementKind.CONSTRUCTOR)
+ && !ElementUtils.isStatic(element);
+ }
+
+ /**
+ * Determine all type elements for the classes and interfaces referenced
+ * in the extends/implements clauses of the given type element.
+ * TODO: can we learn from the implementation of
+ * com.sun.tools.javac.model.JavacElements.getAllMembers(TypeElement)?
+ */
+ public static List<TypeElement> getSuperTypes(Elements elements, TypeElement type) {
+
+ List<TypeElement> superelems = new ArrayList<TypeElement>();
+ if (type == null) {
+ return superelems;
+ }
+
+ // Set up a stack containing type, which is our starting point.
+ Deque<TypeElement> stack = new ArrayDeque<TypeElement>();
+ stack.push(type);
+
+ while (!stack.isEmpty()) {
+ TypeElement current = stack.pop();
+
+ // For each direct supertype of the current type element, if it
+ // hasn't already been visited, push it onto the stack and
+ // add it to our superelems set.
+ TypeMirror supertypecls = current.getSuperclass();
+ if (supertypecls.getKind() != TypeKind.NONE) {
+ TypeElement supercls = (TypeElement) ((DeclaredType)supertypecls).asElement();
+ if (!superelems.contains(supercls)) {
+ stack.push(supercls);
+ superelems.add(supercls);
+ }
+ }
+ for (TypeMirror supertypeitf : current.getInterfaces()) {
+ TypeElement superitf = (TypeElement) ((DeclaredType)supertypeitf).asElement();
+ if (!superelems.contains(superitf)) {
+ stack.push(superitf);
+ superelems.add(superitf);
+ }
+ }
+ }
+
+ // Include java.lang.Object as implicit superclass for all classes and interfaces.
+ TypeElement jlobject = elements.getTypeElement("java.lang.Object");
+ if (!superelems.contains(jlobject)) {
+ superelems.add(jlobject);
+ }
+
+ return Collections.<TypeElement>unmodifiableList(superelems);
+ }
+
+ /**
+ * Return all fields declared in the given type or any superclass/interface.
+ * TODO: should this use javax.lang.model.util.Elements.getAllMembers(TypeElement)
+ * instead of our own getSuperTypes?
+ */
+ public static List<VariableElement> getAllFieldsIn(Elements elements, TypeElement type) {
+ List<VariableElement> fields = new ArrayList<VariableElement>();
+ fields.addAll(ElementFilter.fieldsIn(type.getEnclosedElements()));
+ List<TypeElement> alltypes = getSuperTypes(elements, type);
+ for (TypeElement atype : alltypes) {
+ fields.addAll(ElementFilter.fieldsIn(atype.getEnclosedElements()));
+ }
+ return Collections.<VariableElement>unmodifiableList(fields);
+ }
+
+ /**
+ * Return all methods declared in the given type or any superclass/interface.
+ * Note that no constructors will be returned.
+ * TODO: should this use javax.lang.model.util.Elements.getAllMembers(TypeElement)
+ * instead of our own getSuperTypes?
+ */
+ public static List<ExecutableElement> getAllMethodsIn(Elements elements, TypeElement type) {
+ List<ExecutableElement> meths = new ArrayList<ExecutableElement>();
+ meths.addAll(ElementFilter.methodsIn(type.getEnclosedElements()));
+
+ List<TypeElement> alltypes = getSuperTypes(elements, type);
+ for (TypeElement atype : alltypes) {
+ meths.addAll(ElementFilter.methodsIn(atype.getEnclosedElements()));
+ }
+ return Collections.<ExecutableElement>unmodifiableList(meths);
+ }
+
+ public static boolean isTypeDeclaration(Element elt) {
+ switch (elt.getKind()) {
+ // These tree kinds are always declarations. Uses of the declared
+ // types have tree kind IDENTIFIER.
+ case ANNOTATION_TYPE:
+ case CLASS:
+ case ENUM:
+ case INTERFACE:
+ case TYPE_PARAMETER:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Check that a method Element matches a signature.
+ *
+ * Note: Matching the receiver type must be done elsewhere as
+ * the Element receiver type is only populated when annotated.
+ *
+ * @param method the method Element
+ * @param methodName the name of the method
+ * @param parameters the formal parameters' Classes
+ * @return true if the method matches
+ */
+ public static boolean matchesElement(ExecutableElement method,
+ String methodName,
+ Class<?> ... parameters) {
+
+ if (!method.getSimpleName().toString().equals(methodName)) {
+ return false;
+ }
+
+ if (method.getParameters().size() != parameters.length) {
+ return false;
+ } else {
+ for (int i = 0; i < method.getParameters().size(); i++) {
+ if (!method.getParameters().get(i).asType().toString().equals(
+ parameters[i].getName())) {
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorHandler.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorHandler.java
new file mode 100644
index 0000000000..9e3c27a7b9
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorHandler.java
@@ -0,0 +1,18 @@
+package org.checkerframework.javacutil;
+
+/**
+ * An implementation of the ErrorHandler interface can be registered
+ * with the ErrorReporter class to change the default behavior on
+ * errors.
+ */
+public interface ErrorHandler {
+
+ /**
+ * Log an error message and abort processing.
+ *
+ * @param msg the error message to log
+ */
+ public void errorAbort(String msg);
+
+ public void errorAbort(String msg, Throwable cause);
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorReporter.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorReporter.java
new file mode 100644
index 0000000000..309f1bb4ee
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/ErrorReporter.java
@@ -0,0 +1,41 @@
+package org.checkerframework.javacutil;
+
+/**
+ * Handle errors detected in utility classes. By default, the error reporter
+ * throws a RuntimeException, but clients of the utility library may register
+ * a handler to change the behavior. For example, type checkers can direct
+ * errors to the org.checkerframework.framework.source.SourceChecker class.
+ */
+public class ErrorReporter {
+
+ protected static ErrorHandler handler = null;
+
+ /**
+ * Register a handler to customize error reporting.
+ */
+ public static void setHandler(ErrorHandler h) {
+ handler = h;
+ }
+
+ /**
+ * Log an error message and abort processing.
+ * Call this method instead of raising an exception.
+ *
+ * @param msg the error message to log
+ */
+ public static void errorAbort(String msg) {
+ if (handler != null) {
+ handler.errorAbort(msg);
+ } else {
+ throw new RuntimeException(msg, new Throwable());
+ }
+ }
+
+ public static void errorAbort(String msg, Throwable cause) {
+ if (handler != null) {
+ handler.errorAbort(msg, cause);
+ } else {
+ throw new RuntimeException(msg, cause);
+ }
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/InternalUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/InternalUtils.java
new file mode 100644
index 0000000000..dbff45169b
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/InternalUtils.java
@@ -0,0 +1,403 @@
+package org.checkerframework.javacutil;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.Elements;
+
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.AnnotatedType;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotatedType;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
+import com.sun.tools.javac.tree.JCTree.JCMemberReference;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCNewArray;
+import com.sun.tools.javac.tree.JCTree.JCNewClass;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.TreeInfo;
+import com.sun.tools.javac.util.Context;
+
+/*>>>
+ import org.checkerframework.checker.nullness.qual.*;
+ */
+
+/**
+ * Static utility methods used by annotation abstractions in this package. Some
+ * methods in this class depend on the use of Sun javac internals; any procedure
+ * in the Checker Framework that uses a non-public API should be placed here.
+ */
+public class InternalUtils {
+
+ // Class cannot be instantiated.
+ private InternalUtils() {
+ throw new AssertionError("Class InternalUtils cannot be instantiated.");
+ }
+
+ /**
+ * Gets the {@link Element} ("symbol") for the given Tree API node.
+ *
+ * @param tree the {@link Tree} node to get the symbol for
+ * @throws IllegalArgumentException
+ * if {@code tree} is null or is not a valid javac-internal tree
+ * (JCTree)
+ * @return the {@code {@link Symbol}} for the given tree, or null if one
+ * could not be found
+ */
+ public static /*@Nullable*/ Element symbol(Tree tree) {
+ if (tree == null) {
+ ErrorReporter.errorAbort("InternalUtils.symbol: tree is null");
+ return null; // dead code
+ }
+
+ if (!(tree instanceof JCTree)) {
+ ErrorReporter.errorAbort("InternalUtils.symbol: tree is not a valid Javac tree");
+ return null; // dead code
+ }
+
+ if (TreeUtils.isExpressionTree(tree)) {
+ tree = TreeUtils.skipParens((ExpressionTree) tree);
+ }
+
+ switch (tree.getKind()) {
+ case VARIABLE:
+ case METHOD:
+ case CLASS:
+ case ENUM:
+ case INTERFACE:
+ case ANNOTATION_TYPE:
+ case TYPE_PARAMETER:
+ return TreeInfo.symbolFor((JCTree) tree);
+
+ // symbol() only works on MethodSelects, so we need to get it manually
+ // for method invocations.
+ case METHOD_INVOCATION:
+ return TreeInfo.symbol(((JCMethodInvocation) tree).getMethodSelect());
+
+ case ASSIGNMENT:
+ return TreeInfo.symbol((JCTree)((AssignmentTree)tree).getVariable());
+
+ case ARRAY_ACCESS:
+ return symbol(((ArrayAccessTree)tree).getExpression());
+
+ case NEW_CLASS:
+ return ((JCNewClass)tree).constructor;
+
+ case MEMBER_REFERENCE:
+ // TreeInfo.symbol, which is used in the default case, didn't handle
+ // member references until JDK8u20. So handle it here.
+ return ((JCMemberReference) tree).sym;
+
+ default:
+ return TreeInfo.symbol((JCTree) tree);
+ }
+ }
+
+ /**
+ * Determines whether or not the node referred to by the given
+ * {@link TreePath} is an anonymous constructor (the constructor for an
+ * anonymous class.
+ *
+ * @param method the {@link TreePath} for a node that may be an anonymous
+ * constructor
+ * @return true if the given path points to an anonymous constructor, false
+ * if it does not
+ */
+ public static boolean isAnonymousConstructor(final MethodTree method) {
+ /*@Nullable*/ Element e = InternalUtils.symbol(method);
+ if (e == null || !(e instanceof Symbol)) {
+ return false;
+ }
+
+ if ((((/*@NonNull*/ Symbol)e).flags() & Flags.ANONCONSTR) != 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * indicates whether it should return the constructor that gets invoked
+ * in cases of anonymous classes
+ */
+ private static final boolean RETURN_INVOKE_CONSTRUCTOR = true;
+
+ /**
+ * Determines the symbol for a constructor given an invocation via
+ * {@code new}.
+ *
+ * If the tree is a declaration of an anonymous class, then method returns
+ * constructor that gets invoked in the extended class, rather than the
+ * anonymous constructor implicitly added by the constructor (JLS 15.9.5.1)
+ *
+ * @param tree the constructor invocation
+ * @return the {@link ExecutableElement} corresponding to the constructor
+ * call in {@code tree}
+ */
+ public static ExecutableElement constructor(NewClassTree tree) {
+
+ if (!(tree instanceof JCTree.JCNewClass)) {
+ ErrorReporter.errorAbort("InternalUtils.constructor: not a javac internal tree");
+ return null; // dead code
+ }
+
+ JCNewClass newClassTree = (JCNewClass) tree;
+
+ if (RETURN_INVOKE_CONSTRUCTOR && tree.getClassBody() != null) {
+ // anonymous constructor bodies should contain exactly one statement
+ // in the form:
+ // super(arg1, ...)
+ // or
+ // o.super(arg1, ...)
+ //
+ // which is a method invocation (!) to the actual constructor
+
+ // the method call is guaranteed to return nonnull
+ JCMethodDecl anonConstructor =
+ (JCMethodDecl) TreeInfo.declarationFor(newClassTree.constructor, newClassTree);
+ assert anonConstructor != null;
+ assert anonConstructor.body.stats.size() == 1;
+ JCExpressionStatement stmt = (JCExpressionStatement) anonConstructor.body.stats.head;
+ JCTree.JCMethodInvocation superInvok = (JCMethodInvocation) stmt.expr;
+ return (ExecutableElement) TreeInfo.symbol(superInvok.meth);
+ }
+
+ Element e = newClassTree.constructor;
+
+ assert e instanceof ExecutableElement;
+
+ return (ExecutableElement) e;
+ }
+
+ public final static List<AnnotationMirror> annotationsFromTypeAnnotationTrees(List<? extends AnnotationTree> annos) {
+ List<AnnotationMirror> annotations = new ArrayList<AnnotationMirror>(annos.size());
+ for (AnnotationTree anno : annos) {
+ annotations.add(((JCAnnotation)anno).attribute);
+ }
+ return annotations;
+ }
+
+ public final static List<? extends AnnotationMirror> annotationsFromTree(AnnotatedTypeTree node) {
+ return annotationsFromTypeAnnotationTrees(((JCAnnotatedType)node).annotations);
+ }
+
+ public final static List<? extends AnnotationMirror> annotationsFromTree(TypeParameterTree node) {
+ return annotationsFromTypeAnnotationTrees(((JCTypeParameter)node).annotations);
+ }
+
+ public final static List<? extends AnnotationMirror> annotationsFromArrayCreation(NewArrayTree node, int level) {
+
+ assert node instanceof JCNewArray;
+ final JCNewArray newArray = ((JCNewArray) node);
+
+ if (level == -1) {
+ return annotationsFromTypeAnnotationTrees(newArray.annotations);
+ }
+
+ if (newArray.dimAnnotations.length() > 0
+ && (level >= 0)
+ && (level < newArray.dimAnnotations.size()))
+ return annotationsFromTypeAnnotationTrees(newArray.dimAnnotations.get(level));
+
+ return Collections.emptyList();
+ }
+
+ public static TypeMirror typeOf(Tree tree) {
+ return ((JCTree) tree).type;
+ }
+
+ /**
+ * Returns whether a TypeVariable represents a captured type.
+ */
+ public static boolean isCaptured(TypeVariable typeVar) {
+ if (typeVar instanceof AnnotatedType) {
+ return ((Type.TypeVar) ((Type.AnnotatedType) typeVar).unannotatedType()).isCaptured();
+ }
+ return ((Type.TypeVar) typeVar).isCaptured();
+ }
+
+ /**
+ * Returns whether a TypeMirror represents a class type.
+ */
+ public static boolean isClassType(TypeMirror type) {
+ return (type instanceof Type.ClassType);
+ }
+
+ /**
+ * Returns the least upper bound of two {@link TypeMirror}s,
+ * ignoring any annotations on the types.
+ *
+ * Wrapper around Types.lub to add special handling for
+ * null types, primitives, and wildcards.
+ *
+ * @param processingEnv the {@link ProcessingEnvironment} to use.
+ * @param tm1 a {@link TypeMirror}.
+ * @param tm2 a {@link TypeMirror}.
+ * @return the least upper bound of {@code tm1} and {@code tm2}.
+ */
+ public static TypeMirror leastUpperBound(
+ ProcessingEnvironment processingEnv, TypeMirror tm1, TypeMirror tm2) {
+ Type t1 = ((Type) tm1).unannotatedType();
+ Type t2 = ((Type) tm2).unannotatedType();
+ JavacProcessingEnvironment javacEnv = (JavacProcessingEnvironment) processingEnv;
+ Types types = Types.instance(javacEnv.getContext());
+ if (types.isSameType(t1, t2)) {
+ // Special case if the two types are equal.
+ return t1;
+ }
+ // Handle the 'null' type manually (not done by types.lub).
+ if (t1.getKind() == TypeKind.NULL) {
+ return t2;
+ }
+ if (t2.getKind() == TypeKind.NULL) {
+ return t1;
+ }
+ // Special case for primitives.
+ if (TypesUtils.isPrimitive(t1) || TypesUtils.isPrimitive(t2)) {
+ if (types.isAssignable(t1, t2)) {
+ return t2;
+ } else if (types.isAssignable(t2, t1)) {
+ return t1;
+ } else {
+ return processingEnv.getTypeUtils().getNoType(TypeKind.NONE);
+ }
+ }
+ if (t1.getKind() == TypeKind.WILDCARD) {
+ WildcardType wc1 = (WildcardType) t1;
+ Type bound = (Type) wc1.getExtendsBound();
+ if (bound == null) {
+ // Implicit upper bound of java.lang.Object
+ Elements elements = processingEnv.getElementUtils();
+ return elements.getTypeElement("java.lang.Object").asType();
+ }
+ t1 = bound;
+ }
+ if (t2.getKind() == TypeKind.WILDCARD) {
+ WildcardType wc2 = (WildcardType) t2;
+ Type bound = (Type) wc2.getExtendsBound();
+ if (bound == null) {
+ // Implicit upper bound of java.lang.Object
+ Elements elements = processingEnv.getElementUtils();
+ return elements.getTypeElement("java.lang.Object").asType();
+ }
+ t2 = bound;
+ }
+ return types.lub(t1, t2);
+ }
+
+ /**
+ * Returns the greatest lower bound of two {@link TypeMirror}s,
+ * ignoring any annotations on the types.
+ *
+ * Wrapper around Types.glb to add special handling for
+ * null types, primitives, and wildcards.
+ *
+ *
+ * @param processingEnv the {@link ProcessingEnvironment} to use.
+ * @param tm1 a {@link TypeMirror}.
+ * @param tm2 a {@link TypeMirror}.
+ * @return the greatest lower bound of {@code tm1} and {@code tm2}.
+ */
+ public static TypeMirror greatestLowerBound(
+ ProcessingEnvironment processingEnv, TypeMirror tm1, TypeMirror tm2) {
+ Type t1 = ((Type) tm1).unannotatedType();
+ Type t2 = ((Type) tm2).unannotatedType();
+ JavacProcessingEnvironment javacEnv = (JavacProcessingEnvironment) processingEnv;
+ Types types = Types.instance(javacEnv.getContext());
+ if (types.isSameType(t1, t2)) {
+ // Special case if the two types are equal.
+ return t1;
+ }
+ // Handle the 'null' type manually.
+ if (t1.getKind() == TypeKind.NULL) {
+ return t1;
+ }
+ if (t2.getKind() == TypeKind.NULL) {
+ return t2;
+ }
+ // Special case for primitives.
+ if (TypesUtils.isPrimitive(t1) || TypesUtils.isPrimitive(t2)) {
+ if (types.isAssignable(t1, t2)) {
+ return t1;
+ } else if (types.isAssignable(t2, t1)) {
+ return t2;
+ } else {
+ // Javac types.glb returns TypeKind.Error when the GLB does
+ // not exist, but we can't create one. Use TypeKind.NONE
+ // instead.
+ return processingEnv.getTypeUtils().getNoType(TypeKind.NONE);
+ }
+ }
+ if (t1.getKind() == TypeKind.WILDCARD) {
+ return t2;
+ }
+ if (t2.getKind() == TypeKind.WILDCARD) {
+ return t1;
+ }
+
+ // If neither type is a primitive type, null type, or wildcard
+ // and if the types are not the same, use javac types.glb
+ return types.glb(t1, t2);
+ }
+
+ /**
+ * Returns the return type of a method, where the "raw" return type of that
+ * method is given (i.e., the return type might still contain unsubstituted
+ * type variables), given the receiver of the method call.
+ */
+ public static TypeMirror substituteMethodReturnType(TypeMirror methodType,
+ TypeMirror substitutedReceiverType) {
+ if (methodType.getKind() != TypeKind.TYPEVAR) {
+ return methodType;
+ }
+ // TODO: find a nicer way to substitute type variables
+ String t = methodType.toString();
+ Type finalReceiverType = (Type) substitutedReceiverType;
+ int i = 0;
+ for (TypeSymbol typeParam : finalReceiverType.tsym.getTypeParameters()) {
+ if (t.equals(typeParam.toString())) {
+ return finalReceiverType.getTypeArguments().get(i);
+ }
+ i++;
+ }
+ assert false;
+ return null;
+ }
+
+ /** Helper function to extract the javac Context from the
+ * javac processing environment.
+ *
+ * @param env the processing environment
+ * @return the javac Context
+ */
+ public static Context getJavacContext(ProcessingEnvironment env) {
+ return ((JavacProcessingEnvironment)env).getContext();
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Pair.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Pair.java
new file mode 100644
index 0000000000..0d8a862f08
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Pair.java
@@ -0,0 +1,69 @@
+package org.checkerframework.javacutil;
+
+/*>>>
+import org.checkerframework.dataflow.qual.Pure;
+import org.checkerframework.dataflow.qual.SideEffectFree;
+*/
+
+/**
+ * Simple pair class for multiple returns.
+ *
+ * TODO: as class is immutable, use @Covariant annotation.
+ */
+public class Pair<V1, V2> {
+ public final V1 first;
+ public final V2 second;
+
+ private Pair(V1 v1, V2 v2) {
+ this.first = v1;
+ this.second = v2;
+ }
+
+ public static <V1, V2> Pair<V1, V2> of(V1 v1, V2 v2) {
+ return new Pair<V1, V2>(v1, v2);
+ }
+
+ /*@SideEffectFree*/
+ @Override
+ public String toString() {
+ return "Pair(" + first + ", " + second + ")";
+ }
+
+ private int hashCode = -1;
+
+ /*@Pure*/
+ @Override
+ public int hashCode() {
+ if (hashCode == -1) {
+ hashCode = 31;
+ if (first != null) {
+ hashCode += 17 * first.hashCode();
+ }
+ if (second != null) {
+ hashCode += 17 * second.hashCode();
+ }
+ }
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Pair)) {
+ return false;
+ }
+ @SuppressWarnings("unchecked")
+ Pair<V1, V2> other = (Pair<V1, V2>) o;
+ if (this.first == null) {
+ if (other.first != null) return false;
+ } else {
+ if (!this.first.equals(other.first)) return false;
+ }
+ if (this.second == null) {
+ if (other.second != null) return false;
+ } else {
+ if (!this.second.equals(other.second)) return false;
+ }
+
+ return true;
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java
new file mode 100644
index 0000000000..45d959fe02
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java
@@ -0,0 +1,381 @@
+package org.checkerframework.javacutil;
+
+import static com.sun.tools.javac.code.Kinds.PCK;
+import static com.sun.tools.javac.code.Kinds.TYP;
+import static com.sun.tools.javac.code.Kinds.VAR;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacScope;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.PackageSymbol;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.DeferredAttr;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.comp.Resolve;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Names;
+
+/**
+ * A Utility class to find symbols corresponding to string references.
+ */
+public class Resolver {
+ private final Resolve resolve;
+ private final Names names;
+ private final Trees trees;
+ private final Log log;
+
+ private static final Method FIND_METHOD;
+ private static final Method FIND_VAR;
+ private static final Method FIND_IDENT;
+ private static final Method FIND_IDENT_IN_TYPE;
+ private static final Method FIND_IDENT_IN_PACKAGE;
+ private static final Method FIND_TYPE;
+
+ private static final Class<?> ACCESSERROR;
+ // Note that currently access(...) is defined in InvalidSymbolError, a superclass of AccessError
+ private static final Method ACCESSERROR_ACCESS;
+
+ static {
+ try {
+ FIND_METHOD = Resolve.class.getDeclaredMethod("findMethod",
+ Env.class, Type.class, Name.class, List.class, List.class,
+ boolean.class, boolean.class, boolean.class);
+ FIND_METHOD.setAccessible(true);
+
+ FIND_VAR = Resolve.class.getDeclaredMethod("findVar",
+ Env.class, Name.class);
+ FIND_VAR.setAccessible(true);
+
+ FIND_IDENT = Resolve.class.getDeclaredMethod(
+ "findIdent", Env.class, Name.class, int.class);
+ FIND_IDENT.setAccessible(true);
+
+ FIND_IDENT_IN_TYPE = Resolve.class.getDeclaredMethod(
+ "findIdentInType", Env.class, Type.class, Name.class,
+ int.class);
+ FIND_IDENT_IN_TYPE.setAccessible(true);
+
+ FIND_IDENT_IN_PACKAGE = Resolve.class.getDeclaredMethod(
+ "findIdentInPackage", Env.class, TypeSymbol.class, Name.class,
+ int.class);
+ FIND_IDENT_IN_PACKAGE.setAccessible(true);
+
+ FIND_TYPE = Resolve.class.getDeclaredMethod(
+ "findType", Env.class, Name.class);
+ FIND_TYPE.setAccessible(true);
+ } catch (Exception e) {
+ Error err = new AssertionError(
+ "Compiler 'Resolve' class doesn't contain required 'find' method");
+ err.initCause(e);
+ throw err;
+ }
+
+ try {
+ ACCESSERROR = Class.forName("com.sun.tools.javac.comp.Resolve$AccessError");
+ ACCESSERROR_ACCESS = ACCESSERROR.getMethod("access", Name.class, TypeSymbol.class);
+ ACCESSERROR_ACCESS.setAccessible(true);
+ } catch (ClassNotFoundException e) {
+ ErrorReporter.errorAbort("Compiler 'Resolve$AccessError' class could not be retrieved.", e);
+ // Unreachable code - needed so the compiler does not warn about a possibly uninitialized final field.
+ throw new AssertionError();
+ } catch (NoSuchMethodException e) {
+ ErrorReporter.errorAbort("Compiler 'Resolve$AccessError' class doesn't contain required 'access' method", e);
+ // Unreachable code - needed so the compiler does not warn about a possibly uninitialized final field.
+ throw new AssertionError();
+ }
+ }
+
+ public Resolver(ProcessingEnvironment env) {
+ Context context = ((JavacProcessingEnvironment) env).getContext();
+ this.resolve = Resolve.instance(context);
+ this.names = Names.instance(context);
+ this.trees = Trees.instance(env);
+ this.log = Log.instance(context);
+ }
+
+ /**
+ * Finds the package with name {@code name}.
+ *
+ * @param name
+ * The name of the package.
+ * @param path
+ * The tree path to the local scope.
+ * @return the {@code PackageSymbol} for the package if it is found,
+ * {@code null} otherwise
+ */
+ public PackageSymbol findPackage(String name, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ Element res = wrapInvocationOnResolveInstance(FIND_IDENT, env,
+ names.fromString(name), PCK);
+ // findIdent will return a PackageSymbol even for a symbol that is not a package,
+ // such as a.b.c.MyClass.myStaticField. "exists()" must be called on it to ensure
+ // that it exists.
+ if (res.getKind() == ElementKind.PACKAGE) {
+ PackageSymbol ps = (PackageSymbol) res;
+ return ps.exists() ? ps : null;
+ } else {
+ return null;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the field with name {@code name} in a given type.
+ *
+ * <p>
+ * The method adheres to all the rules of Java's scoping (while also
+ * considering the imports) for name resolution.
+ *
+ * @param name
+ * The name of the field.
+ * @param type
+ * The type of the receiver (i.e., the type in which to look for
+ * the field).
+ * @param path
+ * The tree path to the local scope.
+ * @return the element for the field
+ */
+ public VariableElement findField(String name, TypeMirror type, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ Element res = wrapInvocationOnResolveInstance(FIND_IDENT_IN_TYPE, env, type,
+ names.fromString(name), VAR);
+ if (res.getKind() == ElementKind.FIELD) {
+ return (VariableElement) res;
+ } else if (res.getKind() == ElementKind.OTHER && ACCESSERROR.isInstance(res)) {
+ // Return the inaccessible field that was found
+ return (VariableElement) wrapInvocation(res, ACCESSERROR_ACCESS, null, null);
+ } else {
+ // Most likely didn't find the field and the Element is a SymbolNotFoundError
+ return null;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the local variable with name {@code name} in the given scope.
+ *
+ * @param name
+ * The name of the local variable.
+ * @param path
+ * The tree path to the local scope.
+ * @return the element for the local variable
+ */
+ public VariableElement findLocalVariableOrParameter(String name, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ Element res = wrapInvocationOnResolveInstance(FIND_VAR, env,
+ names.fromString(name));
+ if (res.getKind() == ElementKind.LOCAL_VARIABLE
+ || res.getKind() == ElementKind.PARAMETER) {
+ return (VariableElement) res;
+ } else {
+ // Most likely didn't find the variable and the Element is a SymbolNotFoundError
+ return null;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the class literal with name {@code name}.
+ *
+ * <p>
+ * The method adheres to all the rules of Java's scoping (while also
+ * considering the imports) for name resolution.
+ *
+ * @param name
+ * The name of the class.
+ * @param path
+ * The tree path to the local scope.
+ * @return the element for the class
+ */
+ public Element findClass(String name, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ return wrapInvocationOnResolveInstance(FIND_TYPE, env, names.fromString(name));
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the class with name {@code name} in a given package.
+ *
+ * @param name
+ * The name of the class.
+ * @param pck
+ * The PackageSymbol for the package.
+ * @param path
+ * The tree path to the local scope.
+ * @return the {@code ClassSymbol} for the class if it is found,
+ * {@code null} otherwise
+ */
+ public ClassSymbol findClassInPackage(String name, PackageSymbol pck, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ Element res = wrapInvocationOnResolveInstance(FIND_IDENT_IN_PACKAGE, env, pck,
+ names.fromString(name), TYP);
+ if (res.getKind() == ElementKind.CLASS) {
+ return (ClassSymbol) res;
+ } else {
+ return null;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the method element for a given name and list of expected parameter
+ * types.
+ *
+ * <p>
+ * The method adheres to all the rules of Java's scoping (while also
+ * considering the imports) for name resolution.
+ *
+ * @param methodName
+ * Name of the method to find.
+ * @param receiverType
+ * Type of the receiver of the method
+ * @param path
+ * Tree path.
+ * @return the method element (if found)
+ */
+ public Element findMethod(String methodName, TypeMirror receiverType,
+ TreePath path, java.util.List<TypeMirror> argumentTypes) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+
+ Type site = (Type) receiverType;
+ Name name = names.fromString(methodName);
+ List<Type> argtypes = List.nil();
+ for (TypeMirror a : argumentTypes) {
+ argtypes = argtypes.append((Type) a);
+ }
+ List<Type> typeargtypes = List.nil();
+ boolean allowBoxing = true;
+ boolean useVarargs = false;
+ boolean operator = true;
+
+ try {
+ // For some reason we have to set our own method context, which is rather ugly.
+ // TODO: find a nicer way to do this.
+ Object methodContext = buildMethodContext();
+ Object oldContext = getField(resolve, "currentResolutionContext");
+ setField(resolve, "currentResolutionContext", methodContext);
+ Element result = wrapInvocationOnResolveInstance(FIND_METHOD, env, site, name, argtypes,
+ typeargtypes, allowBoxing, useVarargs, operator);
+ setField(resolve, "currentResolutionContext", oldContext);
+ return result;
+ } catch (Throwable t) {
+ Error err = new AssertionError("Unexpected Reflection error");
+ err.initCause(t);
+ throw err;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Build an instance of {@code Resolve$MethodResolutionContext}.
+ */
+ protected Object buildMethodContext() throws ClassNotFoundException,
+ InstantiationException, IllegalAccessException,
+ InvocationTargetException, NoSuchFieldException {
+ // Class is not accessible, instantiate reflectively.
+ Class<?> methCtxClss = Class.forName("com.sun.tools.javac.comp.Resolve$MethodResolutionContext");
+ Constructor<?> constructor = methCtxClss.getDeclaredConstructors()[0];
+ constructor.setAccessible(true);
+ Object methodContext = constructor.newInstance(resolve);
+ // we need to also initialize the fields attrMode and step
+ setField(methodContext, "attrMode", DeferredAttr.AttrMode.CHECK);
+ @SuppressWarnings("rawtypes")
+ List<?> phases = (List) getField(resolve, "methodResolutionSteps");
+ setField(methodContext, "step", phases.get(1));
+ return methodContext;
+ }
+
+ /** Reflectively set a field. */
+ private void setField(Object receiver, String fieldName,
+ Object value) throws NoSuchFieldException,
+ IllegalAccessException {
+ Field f = receiver.getClass().getDeclaredField(fieldName);
+ f.setAccessible(true);
+ f.set(receiver, value);
+ }
+
+ /** Reflectively get the value of a field. */
+ private Object getField(Object receiver, String fieldName) throws NoSuchFieldException,
+ IllegalAccessException {
+ Field f = receiver.getClass().getDeclaredField(fieldName);
+ f.setAccessible(true);
+ return f.get(receiver);
+ }
+
+ private Symbol wrapInvocationOnResolveInstance(Method method, Object... args) {
+ return wrapInvocation(resolve, method, args);
+ }
+
+ private Symbol wrapInvocation(Object receiver, Method method, Object... args) {
+ try {
+ return (Symbol) method.invoke(receiver, args);
+ } catch (IllegalAccessException e) {
+ Error err = new AssertionError("Unexpected Reflection error");
+ err.initCause(e);
+ throw err;
+ } catch (IllegalArgumentException e) {
+ Error err = new AssertionError("Unexpected Reflection error");
+ err.initCause(e);
+ throw err;
+ } catch (InvocationTargetException e) {
+ Error err = new AssertionError("Unexpected Reflection error");
+ err.initCause(e);
+ throw err;
+ }
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TreeUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TreeUtils.java
new file mode 100644
index 0000000000..5ceb5450dd
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TreeUtils.java
@@ -0,0 +1,967 @@
+package org.checkerframework.javacutil;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+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.util.ElementFilter;
+
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompoundAssignmentTree;
+import com.sun.source.tree.ExpressionStatementTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.ParenthesizedTree;
+import com.sun.source.tree.PrimitiveTypeTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.tree.JCTree;
+
+/**
+ * A utility class made for helping to analyze a given {@code Tree}.
+ */
+// TODO: This class needs significant restructuring
+public final class TreeUtils {
+
+ // Class cannot be instantiated.
+ private TreeUtils() { throw new AssertionError("Class TreeUtils cannot be instantiated."); }
+
+ /**
+ * Checks if the provided method is a constructor method or no.
+ *
+ * @param tree
+ * a tree defining the method
+ * @return true iff tree describes a constructor
+ */
+ public static boolean isConstructor(final MethodTree tree) {
+ return tree.getName().contentEquals("<init>");
+ }
+
+ /**
+ * Checks if the method invocation is a call to super.
+ *
+ * @param tree
+ * a tree defining a method invocation
+ *
+ * @return true iff tree describes a call to super
+ */
+ public static boolean isSuperCall(MethodInvocationTree tree) {
+ return isNamedMethodCall("super", tree);
+ }
+
+ /**
+ * Checks if the method invocation is a call to this.
+ *
+ * @param tree
+ * a tree defining a method invocation
+ *
+ * @return true iff tree describes a call to this
+ */
+ public static boolean isThisCall(MethodInvocationTree tree) {
+ return isNamedMethodCall("this", tree);
+
+ }
+
+ protected static boolean isNamedMethodCall(String name, MethodInvocationTree tree) {
+ /*@Nullable*/ ExpressionTree mst = tree.getMethodSelect();
+ assert mst != null; /*nninvariant*/
+
+ if (mst.getKind() == Tree.Kind.IDENTIFIER ) {
+ return ((IdentifierTree)mst).getName().contentEquals(name);
+ }
+
+ if (mst.getKind() == Tree.Kind.MEMBER_SELECT) {
+ MemberSelectTree selectTree = (MemberSelectTree)mst;
+
+ if (selectTree.getExpression().getKind() != Tree.Kind.IDENTIFIER) {
+ return false;
+ }
+
+ return ((IdentifierTree) selectTree.getExpression()).getName()
+ .contentEquals(name);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the tree is a tree that 'looks like' either an access
+ * of a field or an invocation of a method that are owned by the same
+ * accessing instance.
+ *
+ * It would only return true if the access tree is of the form:
+ * <pre>
+ * field
+ * this.field
+ *
+ * method()
+ * this.method()
+ * </pre>
+ *
+ * It does not perform any semantical check to differentiate between
+ * fields and local variables; local methods or imported static methods.
+ *
+ * @param tree expression tree representing an access to object member
+ * @return {@code true} iff the member is a member of {@code this} instance
+ */
+ public static boolean isSelfAccess(final ExpressionTree tree) {
+ ExpressionTree tr = TreeUtils.skipParens(tree);
+ // If method invocation check the method select
+ if (tr.getKind() == Tree.Kind.ARRAY_ACCESS) {
+ return false;
+ }
+
+ if (tree.getKind() == Tree.Kind.METHOD_INVOCATION) {
+ tr = ((MethodInvocationTree)tree).getMethodSelect();
+ }
+ tr = TreeUtils.skipParens(tr);
+ if (tr.getKind() == Tree.Kind.TYPE_CAST) {
+ tr = ((TypeCastTree)tr).getExpression();
+ }
+ tr = TreeUtils.skipParens(tr);
+
+ if (tr.getKind() == Tree.Kind.IDENTIFIER) {
+ return true;
+ }
+
+ if (tr.getKind() == Tree.Kind.MEMBER_SELECT) {
+ tr = ((MemberSelectTree)tr).getExpression();
+ if (tr.getKind() == Tree.Kind.IDENTIFIER) {
+ Name ident = ((IdentifierTree)tr).getName();
+ return ident.contentEquals("this") ||
+ ident.contentEquals("super");
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets the first enclosing tree in path, of the specified kind.
+ *
+ * @param path the path defining the tree node
+ * @param kind the kind of the desired tree
+ * @return the enclosing tree of the given type as given by the path
+ */
+ public static Tree enclosingOfKind(final TreePath path, final Tree.Kind kind) {
+ return enclosingOfKind(path, EnumSet.of(kind));
+ }
+
+ /**
+ * Gets the first enclosing tree in path, with any one of the specified kinds.
+ *
+ * @param path the path defining the tree node
+ * @param kinds the set of kinds of the desired tree
+ * @return the enclosing tree of the given type as given by the path
+ */
+ public static Tree enclosingOfKind(final TreePath path, final Set<Tree.Kind> kinds) {
+ TreePath p = path;
+
+ while (p != null) {
+ Tree leaf = p.getLeaf();
+ assert leaf != null; /*nninvariant*/
+ if (kinds.contains(leaf.getKind())) {
+ return leaf;
+ }
+ p = p.getParentPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets path to the first enclosing class tree, where class is
+ * defined by the classTreeKinds method.
+ *
+ * @param path the path defining the tree node
+ * @return the path to the enclosing class tree
+ */
+ public static TreePath pathTillClass(final TreePath path) {
+ return pathTillOfKind(path, classTreeKinds());
+ }
+
+ /**
+ * Gets path to the first enclosing tree of the specified kind.
+ *
+ * @param path the path defining the tree node
+ * @param kind the kind of the desired tree
+ * @return the path to the enclosing tree of the given type
+ */
+ public static TreePath pathTillOfKind(final TreePath path, final Tree.Kind kind) {
+ return pathTillOfKind(path, EnumSet.of(kind));
+ }
+
+ /**
+ * Gets path to the first enclosing tree with any one of the specified kinds.
+ *
+ * @param path the path defining the tree node
+ * @param kinds the set of kinds of the desired tree
+ * @return the path to the enclosing tree of the given type
+ */
+ public static TreePath pathTillOfKind(final TreePath path, final Set<Tree.Kind> kinds) {
+ TreePath p = path;
+
+ while (p != null) {
+ Tree leaf = p.getLeaf();
+ assert leaf != null; /*nninvariant*/
+ if (kinds.contains(leaf.getKind())) {
+ return p;
+ }
+ p = p.getParentPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the first enclosing tree in path, of the specified class
+ *
+ * @param path the path defining the tree node
+ * @param treeClass the class of the desired tree
+ * @return the enclosing tree of the given type as given by the path
+ */
+ public static <T extends Tree> T enclosingOfClass(final TreePath path, final Class<T> treeClass) {
+ TreePath p = path;
+
+ while (p != null) {
+ Tree leaf = p.getLeaf();
+ if (treeClass.isInstance(leaf)) {
+ return treeClass.cast(leaf);
+ }
+ p = p.getParentPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the enclosing class of the tree node defined by the given
+ * {@code {@link TreePath}}. It returns a {@link Tree}, from which
+ * {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can be
+ * obtained.
+ *
+ * @param path the path defining the tree node
+ * @return the enclosing class (or interface) as given by the path, or null
+ * if one does not exist
+ */
+ public static /*@Nullable*/ ClassTree enclosingClass(final /*@Nullable*/ TreePath path) {
+ return (ClassTree) enclosingOfKind(path, classTreeKinds());
+ }
+
+ /**
+ * Gets the enclosing variable of a tree node defined by the given
+ * {@link TreePath}.
+ *
+ * @param path the path defining the tree node
+ * @return the enclosing variable as given by the path, or null if one does not exist
+ */
+ public static VariableTree enclosingVariable(final TreePath path) {
+ return (VariableTree) enclosingOfKind(path, Tree.Kind.VARIABLE);
+ }
+
+ /**
+ * Gets the enclosing method of the tree node defined by the given
+ * {@code {@link TreePath}}. It returns a {@link Tree}, from which an
+ * {@code checkers.types.AnnotatedTypeMirror} or {@link Element} can be
+ * obtained.
+ *
+ * @param path the path defining the tree node
+ * @return the enclosing method as given by the path, or null if one does
+ * not exist
+ */
+ public static /*@Nullable*/ MethodTree enclosingMethod(final /*@Nullable*/ TreePath path) {
+ return (MethodTree) enclosingOfKind(path, Tree.Kind.METHOD);
+ }
+
+ public static /*@Nullable*/ BlockTree enclosingTopLevelBlock(TreePath path) {
+ TreePath parpath = path.getParentPath();
+ while (parpath!=null && parpath.getLeaf().getKind() != Tree.Kind.CLASS) {
+ path = parpath;
+ parpath = parpath.getParentPath();
+ }
+ if (path.getLeaf().getKind() == Tree.Kind.BLOCK) {
+ return (BlockTree) path.getLeaf();
+ }
+ return null;
+ }
+
+
+ /**
+ * If the given tree is a parenthesized tree, it returns the enclosed
+ * non-parenthesized tree. Otherwise, it returns the same tree.
+ *
+ * @param tree an expression tree
+ * @return the outermost non-parenthesized tree enclosed by the given tree
+ */
+ public static ExpressionTree skipParens(final ExpressionTree tree) {
+ ExpressionTree t = tree;
+ while (t.getKind() == Tree.Kind.PARENTHESIZED)
+ t = ((ParenthesizedTree)t).getExpression();
+ return t;
+ }
+
+ /**
+ * Returns the tree with the assignment context for the treePath
+ * leaf node. (Does not handle pseudo-assignment of an argument to
+ * a parameter or a receiver expression to a receiver.)
+ *
+ * The assignment context for the {@code treePath} is the leaf of its parent,
+ * if the leaf is one of the following trees:
+ * <ul>
+ * <li>AssignmentTree </li>
+ * <li>CompoundAssignmentTree </li>
+ * <li>MethodInvocationTree</li>
+ * <li>NewArrayTree</li>
+ * <li>NewClassTree</li>
+ * <li>ReturnTree</li>
+ * <li>VariableTree</li>
+ * </ul>
+ *
+ * If the leaf is a ConditionalExpressionTree or ParenthesizedTree, then recur on the leaf.
+ *
+ * Otherwise, null is returned.
+ *
+ * @return the assignment context as described
+ */
+ public static Tree getAssignmentContext(final TreePath treePath) {
+ TreePath parentPath = treePath.getParentPath();
+
+ if (parentPath == null) {
+ return null;
+ }
+
+ Tree parent = parentPath.getLeaf();
+ switch (parent.getKind()) {
+ case PARENTHESIZED:
+ case CONDITIONAL_EXPRESSION:
+ return getAssignmentContext(parentPath);
+ case ASSIGNMENT:
+ case METHOD_INVOCATION:
+ case NEW_ARRAY:
+ case NEW_CLASS:
+ case RETURN:
+ case VARIABLE:
+ return parent;
+ default:
+ // 11 Tree.Kinds are CompoundAssignmentTrees,
+ // so use instanceof rather than listing all 11.
+ if (parent instanceof CompoundAssignmentTree) {
+ return parent;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Gets the element for a class corresponding to a declaration.
+ *
+ * @return the element for the given class
+ */
+ public static final TypeElement elementFromDeclaration(ClassTree node) {
+ TypeElement elt = (TypeElement) InternalUtils.symbol(node);
+ return elt;
+ }
+
+ /**
+ * Gets the element for a method corresponding to a declaration.
+ *
+ * @return the element for the given method
+ */
+ public static final ExecutableElement elementFromDeclaration(MethodTree node) {
+ ExecutableElement elt = (ExecutableElement) InternalUtils.symbol(node);
+ return elt;
+ }
+
+ /**
+ * Gets the element for a variable corresponding to its declaration.
+ *
+ * @return the element for the given variable
+ */
+ public static final VariableElement elementFromDeclaration(VariableTree node) {
+ VariableElement elt = (VariableElement) InternalUtils.symbol(node);
+ return elt;
+ }
+
+ /**
+ * Gets the element for the declaration corresponding to this use of an element.
+ * To get the element for a declaration, use {@link
+ * Trees#getElement(TreePath)} instead.
+ *
+ * TODO: remove this method, as it really doesn't do anything.
+ *
+ * @param node the tree corresponding to a use of an element
+ * @return the element for the corresponding declaration
+ */
+ public static final Element elementFromUse(ExpressionTree node) {
+ return InternalUtils.symbol(node);
+ }
+
+ // Specialization for return type.
+ public static final ExecutableElement elementFromUse(MethodInvocationTree node) {
+ return (ExecutableElement) elementFromUse((ExpressionTree) node);
+ }
+
+ // Specialization for return type.
+ public static final ExecutableElement elementFromUse(NewClassTree node) {
+ return (ExecutableElement) elementFromUse((ExpressionTree) node);
+ }
+
+
+ /**
+ * Determine whether the given ExpressionTree has an underlying element.
+ *
+ * @param node the ExpressionTree to test
+ * @return whether the tree refers to an identifier, member select, or method invocation
+ */
+ public static final boolean isUseOfElement(ExpressionTree node) {
+ node = TreeUtils.skipParens(node);
+ switch (node.getKind()) {
+ case IDENTIFIER:
+ case MEMBER_SELECT:
+ case METHOD_INVOCATION:
+ case NEW_CLASS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * @return the name of the invoked method
+ */
+ public static final Name methodName(MethodInvocationTree node) {
+ ExpressionTree expr = node.getMethodSelect();
+ if (expr.getKind() == Tree.Kind.IDENTIFIER) {
+ return ((IdentifierTree)expr).getName();
+ } else if (expr.getKind() == Tree.Kind.MEMBER_SELECT) {
+ return ((MemberSelectTree)expr).getIdentifier();
+ }
+ ErrorReporter.errorAbort("TreeUtils.methodName: cannot be here: " + node);
+ return null; // dead code
+ }
+
+ /**
+ * @return true if the first statement in the body is a self constructor
+ * invocation within a constructor
+ */
+ public static final boolean containsThisConstructorInvocation(MethodTree node) {
+ if (!TreeUtils.isConstructor(node)
+ || node.getBody().getStatements().isEmpty())
+ return false;
+
+ StatementTree st = node.getBody().getStatements().get(0);
+ if (!(st instanceof ExpressionStatementTree)
+ || !(((ExpressionStatementTree)st).getExpression() instanceof MethodInvocationTree))
+ return false;
+
+ MethodInvocationTree invocation = (MethodInvocationTree)
+ ((ExpressionStatementTree)st).getExpression();
+
+ return "this".contentEquals(TreeUtils.methodName(invocation));
+ }
+
+ public static final Tree firstStatement(Tree tree) {
+ Tree first;
+ if (tree.getKind() == Tree.Kind.BLOCK) {
+ BlockTree block = (BlockTree)tree;
+ if (block.getStatements().isEmpty()) {
+ first = block;
+ } else {
+ first = block.getStatements().iterator().next();
+ }
+ } else {
+ first = tree;
+ }
+ return first;
+ }
+
+ /**
+ * Determine whether the given class contains an explicit constructor.
+ *
+ * @param node a class tree
+ * @return true, iff there is an explicit constructor
+ */
+ public static boolean hasExplicitConstructor(ClassTree node) {
+ TypeElement elem = TreeUtils.elementFromDeclaration(node);
+
+ for ( ExecutableElement ee : ElementFilter.constructorsIn(elem.getEnclosedElements())) {
+ MethodSymbol ms = (MethodSymbol) ee;
+ long mod = ms.flags();
+
+ if ((mod & Flags.SYNTHETIC) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the tree is of a diamond type.
+ * In contrast to the implementation in TreeInfo, this version
+ * works on Trees.
+ *
+ * @see com.sun.tools.javac.tree.TreeInfo#isDiamond(JCTree)
+ */
+ public static final boolean isDiamondTree(Tree tree) {
+ switch (tree.getKind()) {
+ case ANNOTATED_TYPE: return isDiamondTree(((AnnotatedTypeTree)tree).getUnderlyingType());
+ case PARAMETERIZED_TYPE: return ((ParameterizedTypeTree)tree).getTypeArguments().isEmpty();
+ case NEW_CLASS: return isDiamondTree(((NewClassTree)tree).getIdentifier());
+ default: return false;
+ }
+ }
+
+ /**
+ * Returns true if the tree represents a {@code String} concatenation
+ * operation
+ */
+ public static final boolean isStringConcatenation(Tree tree) {
+ return (tree.getKind() == Tree.Kind.PLUS
+ && TypesUtils.isString(InternalUtils.typeOf(tree)));
+ }
+
+ /**
+ * Returns true if the compound assignment tree is a string concatenation
+ */
+ public static final boolean isStringCompoundConcatenation(CompoundAssignmentTree tree) {
+ return (tree.getKind() == Tree.Kind.PLUS_ASSIGNMENT
+ && TypesUtils.isString(InternalUtils.typeOf(tree)));
+ }
+
+ /**
+ * Returns true if the node is a constant-time expression.
+ *
+ * A tree is a constant-time expression if it is:
+ * <ol>
+ * <li>a literal tree
+ * <li>a reference to a final variable initialized with a compile time
+ * constant
+ * <li>a String concatenation of two compile time constants
+ * </ol>
+ */
+ public static boolean isCompileTimeString(ExpressionTree node) {
+ ExpressionTree tree = TreeUtils.skipParens(node);
+ if (tree instanceof LiteralTree) {
+ return true;
+ }
+
+ if (TreeUtils.isUseOfElement(tree)) {
+ Element elt = TreeUtils.elementFromUse(tree);
+ return ElementUtils.isCompileTimeConstant(elt);
+ } else if (TreeUtils.isStringConcatenation(tree)) {
+ BinaryTree binOp = (BinaryTree) tree;
+ return isCompileTimeString(binOp.getLeftOperand())
+ && isCompileTimeString(binOp.getRightOperand());
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the receiver tree of a field access or a method invocation
+ */
+ public static ExpressionTree getReceiverTree(ExpressionTree expression) {
+ ExpressionTree receiver = TreeUtils.skipParens(expression);
+
+ if (!(receiver.getKind() == Tree.Kind.METHOD_INVOCATION
+ || receiver.getKind() == Tree.Kind.MEMBER_SELECT
+ || receiver.getKind() == Tree.Kind.IDENTIFIER
+ || receiver.getKind() == Tree.Kind.ARRAY_ACCESS)) {
+ // No receiver tree for anything but these four kinds.
+ return null;
+ }
+
+ if (receiver.getKind() == Tree.Kind.METHOD_INVOCATION) {
+ // Trying to handle receiver calls to trees of the form
+ // ((m).getArray())
+ // returns the type of 'm' in this case
+ receiver = ((MethodInvocationTree)receiver).getMethodSelect();
+
+ if (receiver.getKind() == Tree.Kind.IDENTIFIER) {
+ // It's a method call "m(foo)" without an explicit receiver
+ return null;
+ } else if (receiver.getKind() == Tree.Kind.MEMBER_SELECT) {
+ receiver = ((MemberSelectTree)receiver).getExpression();
+ } else {
+ // Otherwise, e.g. a NEW_CLASS: nothing to do.
+ }
+ } else if (receiver.getKind() == Tree.Kind.IDENTIFIER) {
+ // It's a field access on implicit this or a local variable/parameter.
+ return null;
+ } else if (receiver.getKind() == Tree.Kind.ARRAY_ACCESS) {
+ return TreeUtils.skipParens(((ArrayAccessTree)receiver).getExpression());
+ } else if (receiver.getKind() == Tree.Kind.MEMBER_SELECT) {
+ receiver = ((MemberSelectTree)receiver).getExpression();
+ // Avoid int.class
+ if (receiver instanceof PrimitiveTypeTree) {
+ return null;
+ }
+ }
+
+ // Receiver is now really just the receiver tree.
+ return TreeUtils.skipParens(receiver);
+ }
+
+ // TODO: What about anonymous classes?
+ // Adding Tree.Kind.NEW_CLASS here doesn't work, because then a
+ // tree gets cast to ClassTree when it is actually a NewClassTree,
+ // for example in enclosingClass above.
+ private final static Set<Tree.Kind> classTreeKinds = EnumSet.of(
+ Tree.Kind.CLASS,
+ Tree.Kind.ENUM,
+ Tree.Kind.INTERFACE,
+ Tree.Kind.ANNOTATION_TYPE
+ );
+
+ public static Set<Tree.Kind> classTreeKinds() {
+ return classTreeKinds;
+ }
+
+ /**
+ * Is the given tree kind a class, i.e. a class, enum,
+ * interface, or annotation type.
+ *
+ * @param tree the tree to test
+ * @return true, iff the given kind is a class kind
+ */
+ public static boolean isClassTree(Tree tree) {
+ return classTreeKinds().contains(tree.getKind());
+ }
+
+ private final static Set<Tree.Kind> typeTreeKinds = EnumSet.of(
+ Tree.Kind.PRIMITIVE_TYPE,
+ Tree.Kind.PARAMETERIZED_TYPE,
+ Tree.Kind.TYPE_PARAMETER,
+ Tree.Kind.ARRAY_TYPE,
+ Tree.Kind.UNBOUNDED_WILDCARD,
+ Tree.Kind.EXTENDS_WILDCARD,
+ Tree.Kind.SUPER_WILDCARD,
+ Tree.Kind.ANNOTATED_TYPE
+ );
+
+ public static Set<Tree.Kind> typeTreeKinds() {
+ return typeTreeKinds;
+ }
+
+ /**
+ * Is the given tree a type instantiation?
+ *
+ * TODO: this is an under-approximation: e.g. an identifier could
+ * be either a type use or an expression. How can we distinguish.
+ *
+ * @param tree the tree to test
+ * @return true, iff the given tree is a type
+ */
+ public static boolean isTypeTree(Tree tree) {
+ return typeTreeKinds().contains(tree.getKind());
+ }
+
+ /**
+ * Returns true if the given element is an invocation of the method, or
+ * of any method that overrides that one.
+ */
+ public static boolean isMethodInvocation(Tree tree, ExecutableElement method, ProcessingEnvironment env) {
+ if (!(tree instanceof MethodInvocationTree)) {
+ return false;
+ }
+ MethodInvocationTree methInvok = (MethodInvocationTree)tree;
+ ExecutableElement invoked = TreeUtils.elementFromUse(methInvok);
+ return isMethod(invoked, method, env);
+ }
+
+ /** Returns true if the given element is, or overrides, method. */
+ private static boolean isMethod(ExecutableElement questioned, ExecutableElement method, ProcessingEnvironment env) {
+ return (questioned.equals(method)
+ || env.getElementUtils().overrides(questioned, method,
+ (TypeElement)questioned.getEnclosingElement()));
+ }
+
+ /**
+ * Returns the ExecutableElement for a method declaration of
+ * methodName, in class typeName, with params parameters.
+ *
+ * TODO: to precisely resolve method overloading, we should use parameter types and not just
+ * the number of parameters!
+ */
+ public static ExecutableElement getMethod(String typeName, String methodName, int params, ProcessingEnvironment env) {
+ TypeElement mapElt = env.getElementUtils().getTypeElement(typeName);
+ for (ExecutableElement exec : ElementFilter.methodsIn(mapElt.getEnclosedElements())) {
+ if (exec.getSimpleName().contentEquals(methodName)
+ && exec.getParameters().size() == params)
+ return exec;
+ }
+ ErrorReporter.errorAbort("TreeUtils.getMethod: shouldn't be here!");
+ return null; // dead code
+ }
+
+ /**
+ * Determine whether the given expression is either "this" or an outer
+ * "C.this".
+ *
+ * <p>
+ * TODO: Should this also handle "super"?
+ */
+ public static final boolean isExplicitThisDereference(ExpressionTree tree) {
+ if (tree.getKind() == Tree.Kind.IDENTIFIER
+ && ((IdentifierTree)tree).getName().contentEquals("this")) {
+ // Explicit this reference "this"
+ return true;
+ }
+
+ if (tree.getKind() != Tree.Kind.MEMBER_SELECT) {
+ return false;
+ }
+
+ MemberSelectTree memSelTree = (MemberSelectTree) tree;
+ if (memSelTree.getIdentifier().contentEquals("this")) {
+ // Outer this reference "C.this"
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether {@code tree} is a class literal, such
+ * as
+ *
+ * <pre>
+ * <em>Object</em> . <em>class</em>
+ * </pre>
+ *
+ * @return true iff if tree is a class literal
+ */
+ public static boolean isClassLiteral(Tree tree) {
+ if (tree.getKind() != Tree.Kind.MEMBER_SELECT) {
+ return false;
+ }
+ return "class".equals(((MemberSelectTree) tree).getIdentifier().toString());
+ }
+
+ /**
+ * Determine whether {@code tree} is a field access expressions, such
+ * as
+ *
+ * <pre>
+ * <em>f</em>
+ * <em>obj</em> . <em>f</em>
+ * </pre>
+ *
+ * @return true iff if tree is a field access expression (implicit or
+ * explicit)
+ */
+ public static boolean isFieldAccess(Tree tree) {
+ if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
+ // explicit field access
+ MemberSelectTree memberSelect = (MemberSelectTree) tree;
+ Element el = TreeUtils.elementFromUse(memberSelect);
+ return el.getKind().isField();
+ } else if (tree.getKind().equals(Tree.Kind.IDENTIFIER)) {
+ // implicit field access
+ IdentifierTree ident = (IdentifierTree) tree;
+ Element el = TreeUtils.elementFromUse(ident);
+ return el.getKind().isField()
+ && !ident.getName().contentEquals("this") && !ident.getName().contentEquals("super");
+ }
+ return false;
+ }
+
+ /**
+ * Compute the name of the field that the field access {@code tree}
+ * accesses. Requires {@code tree} to be a field access, as determined
+ * by {@code isFieldAccess}.
+ *
+ * @return the name of the field accessed by {@code tree}.
+ */
+ public static String getFieldName(Tree tree) {
+ assert isFieldAccess(tree);
+ if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
+ MemberSelectTree mtree = (MemberSelectTree) tree;
+ return mtree.getIdentifier().toString();
+ } else {
+ IdentifierTree itree = (IdentifierTree) tree;
+ return itree.getName().toString();
+ }
+ }
+
+ /**
+ * Determine whether {@code tree} refers to a method element, such
+ * as
+ *
+ * <pre>
+ * <em>m</em>(...)
+ * <em>obj</em> . <em>m</em>(...)
+ * </pre>
+ *
+ * @return true iff if tree is a method access expression (implicit or
+ * explicit)
+ */
+ public static boolean isMethodAccess(Tree tree) {
+ if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
+ // explicit method access
+ MemberSelectTree memberSelect = (MemberSelectTree) tree;
+ Element el = TreeUtils.elementFromUse(memberSelect);
+ return el.getKind() == ElementKind.METHOD
+ || el.getKind() == ElementKind.CONSTRUCTOR;
+ } else if (tree.getKind().equals(Tree.Kind.IDENTIFIER)) {
+ // implicit method access
+ IdentifierTree ident = (IdentifierTree) tree;
+ // The field "super" and "this" are also legal methods
+ if (ident.getName().contentEquals("super")
+ || ident.getName().contentEquals("this")) {
+ return true;
+ }
+ Element el = TreeUtils.elementFromUse(ident);
+ return el.getKind() == ElementKind.METHOD
+ || el.getKind() == ElementKind.CONSTRUCTOR;
+ }
+ return false;
+ }
+
+ /**
+ * Compute the name of the method that the method access {@code tree}
+ * accesses. Requires {@code tree} to be a method access, as determined
+ * by {@code isMethodAccess}.
+ *
+ * @return the name of the method accessed by {@code tree}.
+ */
+ public static String getMethodName(Tree tree) {
+ assert isMethodAccess(tree);
+ if (tree.getKind().equals(Tree.Kind.MEMBER_SELECT)) {
+ MemberSelectTree mtree = (MemberSelectTree) tree;
+ return mtree.getIdentifier().toString();
+ } else {
+ IdentifierTree itree = (IdentifierTree) tree;
+ return itree.getName().toString();
+ }
+ }
+
+ /**
+ * @return {@code true} if and only if {@code tree} can have a type
+ * annotation.
+ *
+ * TODO: is this implementation precise enough? E.g. does
+ * a .class literal work correctly?
+ */
+ public static boolean canHaveTypeAnnotation(Tree tree) {
+ return ((JCTree) tree).type != null;
+ }
+
+ /**
+ * Returns true if and only if the given {@code tree} represents a field
+ * access of the given {@link VariableElement}.
+ */
+ public static boolean isSpecificFieldAccess(Tree tree, VariableElement var) {
+ if (tree instanceof MemberSelectTree) {
+ MemberSelectTree memSel = (MemberSelectTree) tree;
+ Element field = TreeUtils.elementFromUse(memSel);
+ return field.equals(var);
+ } else if (tree instanceof IdentifierTree) {
+ IdentifierTree idTree = (IdentifierTree) tree;
+ Element field = TreeUtils.elementFromUse(idTree);
+ return field.equals(var);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the VariableElement for a field declaration.
+ *
+ * @param typeName the class where the field is declared
+ * @param fieldName the name of the field
+ * @param env the processing environment
+ * @return the VariableElement for typeName.fieldName
+ */
+ public static VariableElement getField(String typeName, String fieldName, ProcessingEnvironment env) {
+ TypeElement mapElt = env.getElementUtils().getTypeElement(typeName);
+ for (VariableElement var : ElementFilter.fieldsIn(mapElt.getEnclosedElements())) {
+ if (var.getSimpleName().contentEquals(fieldName)) {
+ return var;
+ }
+ }
+ ErrorReporter.errorAbort("TreeUtils.getField: shouldn't be here!");
+ return null; // dead code
+ }
+
+ /** Determine whether the given tree represents an ExpressionTree.
+ *
+ * TODO: is there a nicer way than an instanceof?
+ *
+ * @param tree the Tree to test
+ * @return whether the tree is an ExpressionTree
+ */
+ public static boolean isExpressionTree(Tree tree) {
+ return tree instanceof ExpressionTree;
+ }
+
+ /**
+ * @param node the method invocation to check
+ * @return true if this is a super call to the {@link Enum} constructor
+ */
+ public static boolean isEnumSuper(MethodInvocationTree node) {
+ ExecutableElement ex = TreeUtils.elementFromUse(node);
+ Name name = ElementUtils.getQualifiedClassName(ex);
+ boolean correctClass = "java.lang.Enum".contentEquals(name);
+ boolean correctMethod = "<init>".contentEquals(ex.getSimpleName());
+ return correctClass && correctMethod;
+ }
+
+ /** Determine whether the given tree represents a declaration of a type
+ * (including type parameters).
+ *
+ * @param node the Tree to test
+ * @return true if the tree is a type declaration
+ */
+ public static boolean isTypeDeclaration(Tree node) {
+ switch (node.getKind()) {
+ // These tree kinds are always declarations. Uses of the declared
+ // types have tree kind IDENTIFIER.
+ case ANNOTATION_TYPE:
+ case CLASS:
+ case ENUM:
+ case INTERFACE:
+ case TYPE_PARAMETER:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * @see Object#getClass()
+ * @return true iff invocationTree is an instance of getClass()
+ */
+ public static boolean isGetClassInvocation(MethodInvocationTree invocationTree) {
+ final Element declarationElement = elementFromUse(invocationTree);
+ String ownerName = ElementUtils.getQualifiedClassName(declarationElement.getEnclosingElement()).toString();
+ return ownerName.equals("java.lang.Object")
+ && declarationElement.getSimpleName().toString().equals("getClass");
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypeAnnotationUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypeAnnotationUtils.java
new file mode 100644
index 0000000000..c7faceb53a
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypeAnnotationUtils.java
@@ -0,0 +1,627 @@
+package org.checkerframework.javacutil;
+
+import java.lang.reflect.InvocationTargetException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Map;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import com.sun.tools.javac.code.Attribute;
+import com.sun.tools.javac.code.Attribute.TypeCompound;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.TargetType;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.TypeAnnotationPosition;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree.JCLambda;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Pair;
+
+/**
+ * A collection of helper methods related to type annotation handling.
+ *
+ * @see AnnotationUtils
+ */
+public class TypeAnnotationUtils {
+
+ // Class cannot be instantiated.
+ private TypeAnnotationUtils() { throw new AssertionError("Class TypeAnnotationUtils cannot be instantiated."); }
+
+ /**
+ * Check whether a TypeCompound is contained in a list of TypeCompounds.
+ *
+ * @param list the input list of TypeCompounds
+ * @param tc the TypeCompound to find
+ * @return true, iff a TypeCompound equal to tc is contained in list
+ */
+ public static boolean isTypeCompoundContained(Types types, List<TypeCompound> list, TypeCompound tc) {
+ for (Attribute.TypeCompound rawat : list) {
+ if (contentEquals(rawat.type.tsym.name, tc.type.tsym.name) &&
+ // TODO: in previous line, it would be nicer to use reference equality:
+ // rawat.type == tc.type &&
+ // or at least "isSameType":
+ // types.isSameType(rawat.type, tc.type) &&
+ // but each fails in some cases.
+ rawat.values.equals(tc.values) &&
+ isSameTAPositionExceptTreePos(rawat.position, tc.position)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean contentEquals(Name n1, Name n2) {
+ // Views of underlying bytes, not copies as with Name#contentEquals
+ ByteBuffer b1 = ByteBuffer.wrap(n1.getByteArray(), n1.getByteOffset(), n1.getByteLength());
+ ByteBuffer b2 = ByteBuffer.wrap(n2.getByteArray(), n2.getByteOffset(), n2.getByteLength());
+
+ return b1.equals(b2);
+ }
+
+ /**
+ * Compare two TypeAnnotationPositions for equality.
+ *
+ * @param p1 the first position
+ * @param p2 the second position
+ * @return true, iff the two positions are equal
+ */
+ public static boolean isSameTAPosition(TypeAnnotationPosition p1,
+ TypeAnnotationPosition p2) {
+ return isSameTAPositionExceptTreePos(p1, p2) && p1.pos == p2.pos;
+ }
+
+ public static boolean isSameTAPositionExceptTreePos(TypeAnnotationPosition p1,
+ TypeAnnotationPosition p2) {
+ return p1.isValidOffset == p2.isValidOffset &&
+ p1.bound_index == p2.bound_index &&
+ p1.exception_index == p2.exception_index &&
+ p1.location.equals(p2.location) &&
+ Arrays.equals(p1.lvarIndex, p2.lvarIndex) &&
+ Arrays.equals(p1.lvarLength, p2.lvarLength) &&
+ Arrays.equals(p1.lvarOffset, p2.lvarOffset) &&
+ p1.offset == p2.offset &&
+ p1.onLambda == p2.onLambda &&
+ p1.parameter_index == p2.parameter_index &&
+ p1.type == p2.type &&
+ p1.type_index == p2.type_index;
+ }
+
+ /**
+ * Returns a newly created Attribute.Compound corresponding to an
+ * argument AnnotationMirror.
+ *
+ * @param am an AnnotationMirror, which may be part of an AST or an internally
+ * created subclass
+ * @return a new Attribute.Compound corresponding to the AnnotationMirror
+ */
+ public static Attribute.Compound createCompoundFromAnnotationMirror(ProcessingEnvironment env,
+ AnnotationMirror am) {
+ // Create a new Attribute to match the AnnotationMirror.
+ List<Pair<Symbol.MethodSymbol, Attribute>> values = List.nil();
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
+ am.getElementValues().entrySet()) {
+ Attribute attribute = attributeFromAnnotationValue(env, entry.getKey(), entry.getValue());
+ values = values.append(new Pair<>((Symbol.MethodSymbol)entry.getKey(),
+ attribute));
+ }
+ return new Attribute.Compound((Type.ClassType)am.getAnnotationType(), values);
+ }
+
+ /**
+ * Returns a newly created Attribute.TypeCompound corresponding to an
+ * argument AnnotationMirror.
+ *
+ * @param am an AnnotationMirror, which may be part of an AST or an internally
+ * created subclass
+ * @param tapos the type annotation position to use
+ * @return a new Attribute.TypeCompound corresponding to the AnnotationMirror
+ */
+ public static Attribute.TypeCompound createTypeCompoundFromAnnotationMirror(ProcessingEnvironment env,
+ AnnotationMirror am, TypeAnnotationPosition tapos) {
+ // Create a new Attribute to match the AnnotationMirror.
+ List<Pair<Symbol.MethodSymbol, Attribute>> values = List.nil();
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
+ am.getElementValues().entrySet()) {
+ Attribute attribute = attributeFromAnnotationValue(env, entry.getKey(), entry.getValue());
+ values = values.append(new Pair<>((Symbol.MethodSymbol)entry.getKey(),
+ attribute));
+ }
+ return new Attribute.TypeCompound((Type.ClassType)am.getAnnotationType(), values, tapos);
+ }
+
+ /**
+ * Returns a newly created Attribute corresponding to an argument
+ * AnnotationValue.
+ *
+ * @param meth the ExecutableElement that is assigned the value, needed for empty arrays
+ * @param av an AnnotationValue, which may be part of an AST or an internally
+ * created subclass
+ * @return a new Attribute corresponding to the AnnotationValue
+ */
+ public static Attribute attributeFromAnnotationValue(ProcessingEnvironment env, ExecutableElement meth, AnnotationValue av) {
+ return av.accept(new AttributeCreator(env, meth), null);
+ }
+
+ private static class AttributeCreator implements AnnotationValueVisitor<Attribute, Void> {
+ private final ProcessingEnvironment processingEnv;
+ private final Types modelTypes;
+ private final Elements elements;
+ private final com.sun.tools.javac.code.Types javacTypes;
+
+ private final ExecutableElement meth;
+
+ public AttributeCreator(ProcessingEnvironment env, ExecutableElement meth) {
+ this.processingEnv = env;
+ Context context = ((JavacProcessingEnvironment)env).getContext();
+ this.elements = env.getElementUtils();
+ this.modelTypes = env.getTypeUtils();
+ this.javacTypes = com.sun.tools.javac.code.Types.instance(context);
+
+ this.meth = meth;
+ }
+
+ @Override
+ public Attribute visit(AnnotationValue av, Void p) {
+ return av.accept(this, p);
+ }
+
+ @Override
+ public Attribute visit(AnnotationValue av) {
+ return visit(av, null);
+ }
+
+ @Override
+ public Attribute visitBoolean(boolean b, Void p) {
+ TypeMirror booleanType = modelTypes.getPrimitiveType(TypeKind.BOOLEAN);
+ return new Attribute.Constant((Type) booleanType, b ? 1 : 0);
+ }
+
+ @Override
+ public Attribute visitByte(byte b, Void p) {
+ TypeMirror byteType = modelTypes.getPrimitiveType(TypeKind.BYTE);
+ return new Attribute.Constant((Type)byteType, b);
+ }
+
+ @Override
+ public Attribute visitChar(char c, Void p) {
+ TypeMirror charType = modelTypes.getPrimitiveType(TypeKind.CHAR);
+ return new Attribute.Constant((Type)charType, c);
+ }
+
+ @Override
+ public Attribute visitDouble(double d, Void p) {
+ TypeMirror doubleType = modelTypes.getPrimitiveType(TypeKind.DOUBLE);
+ return new Attribute.Constant((Type)doubleType, d);
+ }
+
+ @Override
+ public Attribute visitFloat(float f, Void p) {
+ TypeMirror floatType = modelTypes.getPrimitiveType(TypeKind.FLOAT);
+ return new Attribute.Constant((Type)floatType, f);
+ }
+
+ @Override
+ public Attribute visitInt(int i, Void p) {
+ TypeMirror intType = modelTypes.getPrimitiveType(TypeKind.INT);
+ return new Attribute.Constant((Type)intType, i);
+ }
+
+ @Override
+ public Attribute visitLong(long i, Void p) {
+ TypeMirror longType = modelTypes.getPrimitiveType(TypeKind.LONG);
+ return new Attribute.Constant((Type)longType, i);
+ }
+
+ @Override
+ public Attribute visitShort(short s, Void p) {
+ TypeMirror shortType = modelTypes.getPrimitiveType(TypeKind.SHORT);
+ return new Attribute.Constant((Type)shortType, s);
+ }
+
+ @Override
+ public Attribute visitString(String s, Void p) {
+ TypeMirror stringType = elements.getTypeElement("java.lang.String").asType();
+ return new Attribute.Constant((Type)stringType, s);
+ }
+
+ @Override
+ public Attribute visitType(TypeMirror t, Void p) {
+ if (t instanceof Type) {
+ return new Attribute.Class(javacTypes, (Type)t);
+ }
+
+ assert false : "Unexpected type of TypeMirror: " + t.getClass();
+ return null;
+ }
+
+ @Override
+ public Attribute visitEnumConstant(VariableElement c, Void p) {
+ if (c instanceof Symbol.VarSymbol) {
+ Symbol.VarSymbol sym = (Symbol.VarSymbol) c;
+ if (sym.getKind() == ElementKind.ENUM_CONSTANT) {
+ return new Attribute.Enum(sym.type, sym);
+ }
+ }
+
+ assert false : "Unexpected type of VariableElement: " + c.getClass();
+ return null;
+ }
+
+ @Override
+ public Attribute visitAnnotation(AnnotationMirror a, Void p) {
+ return createCompoundFromAnnotationMirror(processingEnv, a);
+ }
+
+ @Override
+ public Attribute visitArray(java.util.List<? extends AnnotationValue> vals, Void p) {
+ if (!vals.isEmpty()) {
+ List<Attribute> valAttrs = List.nil();
+ for (AnnotationValue av : vals) {
+ valAttrs = valAttrs.append(av.accept(this, p));
+ }
+ ArrayType arrayType = modelTypes.getArrayType(valAttrs.get(0).type);
+ return new Attribute.Array((Type)arrayType, valAttrs);
+ } else {
+ return new Attribute.Array((Type) meth.getReturnType(), List.<Attribute>nil());
+ }
+ }
+
+ @Override
+ public Attribute visitUnknown(AnnotationValue av, Void p) {
+ assert false : "Unexpected type of AnnotationValue: " + av.getClass();
+ return null;
+ }
+ }
+
+ /**
+ * An interface to abstract a Java 8 and a Java 9 version of how
+ * to get a RET reference.
+ * These methods must then be implemented using reflection in order to
+ * compile in either setting.
+ * Note that we cannot use lambda for this as long as we want to
+ * support Java 7.
+ */
+ interface Call8or9<RET> {
+ RET call8() throws Throwable;
+ RET call9() throws Throwable;
+ }
+
+ /**
+ * Use the SourceVersion to decide whether to call the Java 8 or Java 9 version.
+ * Catch all exceptions and abort if one occurs - the reflection code should
+ * never break once fully debugged.
+ *
+ * @param tc the TAPCall abstraction to encapsulate two methods
+ * @return the created TypeAnnotationPosition
+ */
+ private static <RET> RET call8or9(Call8or9<RET> tc) {
+ try {
+ boolean hasNine;
+ try {
+ hasNine = SourceVersion.valueOf("RELEASE_9") != null;
+ } catch (IllegalArgumentException iae) {
+ hasNine = false;
+ }
+ if (hasNine) {
+ return tc.call9();
+ } else {
+ boolean hasEight;
+ try {
+ hasEight = SourceVersion.valueOf("RELEASE_8") != null;
+ } catch (IllegalArgumentException iae) {
+ hasEight = false;
+ }
+ if (hasEight) {
+ return tc.call8();
+ } else {
+ assert false : "Checker Framework needs a Java 8 or 9 javac.";
+ return null;
+ }
+ }
+ } catch (Throwable t) {
+ assert false : "Checker Framework internal error: " + t;
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ public static TypeAnnotationPosition unknownTAPosition() {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException {
+ return TypeAnnotationPosition.class.newInstance();
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getField("unknown")
+ .get(null);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition methodReturnTAPosition(final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_RETURN);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("methodReturn", int.class)
+ .invoke(null, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition methodReceiverTAPosition(final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_RECEIVER);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("methodReceiver", int.class)
+ .invoke(null, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition methodParameterTAPosition(final int pidx, final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_FORMAL_PARAMETER);
+ TypeAnnotationPosition.class.getField("parameter_index").set(tapos, pidx);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("methodParameter", int.class, int.class)
+ .invoke(null, pidx, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition methodThrowsTAPosition(final int tidx, final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.THROWS);
+ TypeAnnotationPosition.class.getField("type_index").set(tapos, tidx);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("methodThrows", List.class, JCLambda.class, int.class, int.class)
+ .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tidx, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition fieldTAPosition(final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.FIELD);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("field", int.class)
+ .invoke(null, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition classExtendsTAPosition(final int implidx, final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.CLASS_EXTENDS);
+ TypeAnnotationPosition.class.getField("type_index").set(tapos, implidx);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("classExtends", int.class, int.class)
+ .invoke(null, implidx, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition typeParameterTAPosition(final int tpidx, final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.CLASS_TYPE_PARAMETER);
+ TypeAnnotationPosition.class.getField("parameter_index").set(tapos, tpidx);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("typeParameter", List.class, JCLambda.class, int.class, int.class)
+ .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tpidx, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition methodTypeParameterTAPosition(final int tpidx, final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_TYPE_PARAMETER);
+ TypeAnnotationPosition.class.getField("parameter_index").set(tapos, tpidx);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("methodTypeParameter", List.class, JCLambda.class, int.class, int.class)
+ .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tpidx, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition typeParameterBoundTAPosition(final int tpidx, final int bndidx, final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.CLASS_TYPE_PARAMETER_BOUND);
+ TypeAnnotationPosition.class.getField("parameter_index").set(tapos, tpidx);
+ TypeAnnotationPosition.class.getField("bound_index").set(tapos, bndidx);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("typeParameterBound", List.class, JCLambda.class, int.class, int.class, int.class)
+ .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tpidx, bndidx, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition methodTypeParameterBoundTAPosition(final int tpidx, final int bndidx, final int pos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition tapos = TypeAnnotationPosition.class.newInstance();
+ TypeAnnotationPosition.class.getField("type").set(tapos, TargetType.METHOD_TYPE_PARAMETER_BOUND);
+ TypeAnnotationPosition.class.getField("parameter_index").set(tapos, tpidx);
+ TypeAnnotationPosition.class.getField("bound_index").set(tapos, bndidx);
+ TypeAnnotationPosition.class.getField("pos").set(tapos, pos);
+ return tapos;
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("methodTypeParameterBound", List.class, JCLambda.class, int.class, int.class, int.class)
+ .invoke(null, TypeAnnotationPosition.class.getField("emptyPath").get(null), null, tpidx, bndidx, pos);
+ }
+ }
+ );
+ }
+
+ public static TypeAnnotationPosition copyTAPosition(final TypeAnnotationPosition tapos) {
+ return call8or9(
+ new Call8or9<TypeAnnotationPosition>() {
+ @Override
+ public TypeAnnotationPosition call8() throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ return copyTAPosition8(tapos);
+ }
+ @Override
+ public TypeAnnotationPosition call9() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, InvocationTargetException, NoSuchMethodException {
+ return (TypeAnnotationPosition) TypeAnnotationPosition.class
+ .getMethod("copy", TypeAnnotationPosition.class)
+ .invoke(null, tapos);
+ }
+ }
+ );
+ }
+
+ private static TypeAnnotationPosition copyTAPosition8(TypeAnnotationPosition tapos) throws InstantiationException, IllegalAccessException, IllegalArgumentException, NoSuchFieldException, SecurityException {
+ TypeAnnotationPosition res = TypeAnnotationPosition.class.newInstance();
+ res.isValidOffset = tapos.isValidOffset;
+ TypeAnnotationPosition.class.getField("bound_index").set(res, tapos.bound_index);
+ res.exception_index = tapos.exception_index;
+ res.location = List.from(tapos.location);
+ if (tapos.lvarIndex != null) {
+ res.lvarIndex = Arrays.copyOf(tapos.lvarIndex, tapos.lvarIndex.length);
+ }
+ if (tapos.lvarLength != null) {
+ res.lvarLength = Arrays.copyOf(tapos.lvarLength, tapos.lvarLength.length);
+ }
+ if (tapos.lvarOffset != null) {
+ res.lvarOffset = Arrays.copyOf(tapos.lvarOffset, tapos.lvarOffset.length);
+ }
+ res.offset = tapos.offset;
+ TypeAnnotationPosition.class.getField("onLambda").set(res, tapos.onLambda);
+ TypeAnnotationPosition.class.getField("parameter_index").set(res, tapos.parameter_index);
+ TypeAnnotationPosition.class.getField("pos").set(res, tapos.pos);
+ TypeAnnotationPosition.class.getField("type").set(res, tapos.type);
+ TypeAnnotationPosition.class.getField("type_index").set(res, tapos.type_index);
+ return res;
+ }
+
+ public static Type unannotatedType(final Type in) {
+ return call8or9(
+ new Call8or9<Type>() {
+ @Override
+ public Type call8() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+ return (Type) Type.class
+ .getMethod("unannotatedType")
+ .invoke(in);
+ }
+ @Override
+ public Type call9() {
+ return in;
+ }
+ }
+ );
+ }
+
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java
new file mode 100644
index 0000000000..949bf1362f
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java
@@ -0,0 +1,446 @@
+package org.checkerframework.javacutil;
+
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.model.JavacTypes;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Context;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import static com.sun.tools.javac.code.TypeTag.WILDCARD;
+
+/**
+ * A utility class that helps with {@link TypeMirror}s.
+ *
+ */
+// TODO: This class needs significant restructuring
+public final class TypesUtils {
+
+ // Class cannot be instantiated
+ private TypesUtils() { throw new AssertionError("Class TypesUtils cannot be instantiated."); }
+
+ /**
+ * Gets the fully qualified name for a provided type. It returns an empty
+ * name if type is an anonymous type.
+ *
+ * @param type the declared type
+ * @return the name corresponding to that type
+ */
+ public static Name getQualifiedName(DeclaredType type) {
+ TypeElement element = (TypeElement) type.asElement();
+ return element.getQualifiedName();
+ }
+
+ /**
+ * Checks if the type represents a java.lang.Object declared type.
+ *
+ * @param type the type
+ * @return true iff type represents java.lang.Object
+ */
+ public static boolean isObject(TypeMirror type) {
+ return isDeclaredOfName(type, "java.lang.Object");
+ }
+
+ /**
+ * Checks if the type represents a java.lang.Class declared type.
+ *
+ * @param type the type
+ * @return true iff type represents java.lang.Class
+ */
+ public static boolean isClass(TypeMirror type) {
+ return isDeclaredOfName(type, "java.lang.Class");
+ }
+
+ /**
+ * Checks if the type represents a java.lang.String declared type.
+ * TODO: it would be cleaner to use String.class.getCanonicalName(), but
+ * the two existing methods above don't do that, I guess for performance reasons.
+ *
+ * @param type the type
+ * @return true iff type represents java.lang.String
+ */
+ public static boolean isString(TypeMirror type) {
+ return isDeclaredOfName(type, "java.lang.String");
+ }
+
+ /**
+ * Checks if the type represents a boolean type, that is either boolean
+ * (primitive type) or java.lang.Boolean.
+ *
+ * @param type the type to test
+ * @return true iff type represents a boolean type
+ */
+ public static boolean isBooleanType(TypeMirror type) {
+ return isDeclaredOfName(type, "java.lang.Boolean")
+ || type.getKind().equals(TypeKind.BOOLEAN);
+ }
+
+ /**
+ * Check if the type represent a declared type of the given qualified name
+ *
+ * @param type the type
+ * @return type iff type represents a declared type of the qualified name
+ */
+ public static boolean isDeclaredOfName(TypeMirror type, CharSequence qualifiedName) {
+ return type.getKind() == TypeKind.DECLARED
+ && getQualifiedName((DeclaredType) type).contentEquals(qualifiedName);
+ }
+
+ public static boolean isBoxedPrimitive(TypeMirror type) {
+ if (type.getKind() != TypeKind.DECLARED) {
+ return false;
+ }
+
+ String qualifiedName = getQualifiedName((DeclaredType)type).toString();
+
+ return (qualifiedName.equals("java.lang.Boolean")
+ || qualifiedName.equals("java.lang.Byte")
+ || qualifiedName.equals("java.lang.Character")
+ || qualifiedName.equals("java.lang.Short")
+ || qualifiedName.equals("java.lang.Integer")
+ || qualifiedName.equals("java.lang.Long")
+ || qualifiedName.equals("java.lang.Double")
+ || qualifiedName.equals("java.lang.Float"));
+ }
+
+ /** @return type represents a Throwable type (e.g. Exception, Error) **/
+ public static boolean isThrowable(TypeMirror type) {
+ while (type != null && type.getKind() == TypeKind.DECLARED) {
+ DeclaredType dt = (DeclaredType) type;
+ TypeElement elem = (TypeElement) dt.asElement();
+ Name name = elem.getQualifiedName();
+ if ("java.lang.Throwable".contentEquals(name)) {
+ return true;
+ }
+ type = elem.getSuperclass();
+ }
+ return false;
+ }
+
+ /**
+ * Returns true iff the argument is an anonymous type.
+ *
+ * @return whether the argument is an anonymous type
+ */
+ public static boolean isAnonymous(TypeMirror type) {
+ return (type instanceof DeclaredType)
+ && (((TypeElement) ((DeclaredType) type).asElement()).getNestingKind()
+ .equals(NestingKind.ANONYMOUS));
+ }
+
+ /**
+ * Returns true iff the argument is a primitive type.
+ *
+ * @return whether the argument is a primitive type
+ */
+ public static boolean isPrimitive(TypeMirror type) {
+ switch (type.getKind()) {
+ case BOOLEAN:
+ case BYTE:
+ case CHAR:
+ case DOUBLE:
+ case FLOAT:
+ case INT:
+ case LONG:
+ case SHORT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Returns true iff the arguments are both the same primitive types.
+ *
+ * @return whether the arguments are the same primitive types
+ */
+ public static boolean areSamePrimitiveTypes(TypeMirror left, TypeMirror right) {
+ if (!isPrimitive(left) || !isPrimitive(right)) {
+ return false;
+ }
+
+ return (left.getKind() == right.getKind());
+ }
+
+ /**
+ * Returns true iff the argument is a primitive numeric type.
+ *
+ * @return whether the argument is a primitive numeric type
+ */
+ public static boolean isNumeric(TypeMirror type) {
+ switch (type.getKind()) {
+ case BYTE:
+ case CHAR:
+ case DOUBLE:
+ case FLOAT:
+ case INT:
+ case LONG:
+ case SHORT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Returns true iff the argument is an integral type.
+ *
+ * @return whether the argument is an integral type
+ */
+ public static boolean isIntegral(TypeMirror type) {
+ switch (type.getKind()) {
+ case BYTE:
+ case CHAR:
+ case INT:
+ case LONG:
+ case SHORT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Returns true iff the argument is a floating point type.
+ *
+ * @return whether the argument is a floating point type
+ */
+ public static boolean isFloating(TypeMirror type) {
+ switch (type.getKind()) {
+ case DOUBLE:
+ case FLOAT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Returns the widened numeric type for an arithmetic operation
+ * performed on a value of the left type and the right type.
+ * Defined in JLS 5.6.2. We return a {@link TypeKind} because
+ * creating a {@link TypeMirror} requires a {@link Types} object
+ * from the {@link javax.annotation.processing.ProcessingEnvironment}.
+ *
+ * @return the result of widening numeric conversion, or NONE when
+ * the conversion cannot be performed
+ */
+ public static TypeKind widenedNumericType(TypeMirror left, TypeMirror right) {
+ if (!isNumeric(left) || !isNumeric(right)) {
+ return TypeKind.NONE;
+ }
+
+ TypeKind leftKind = left.getKind();
+ TypeKind rightKind = right.getKind();
+
+ if (leftKind == TypeKind.DOUBLE || rightKind == TypeKind.DOUBLE) {
+ return TypeKind.DOUBLE;
+ }
+
+ if (leftKind == TypeKind.FLOAT || rightKind == TypeKind.FLOAT) {
+ return TypeKind.FLOAT;
+ }
+
+ if (leftKind == TypeKind.LONG || rightKind == TypeKind.LONG) {
+ return TypeKind.LONG;
+ }
+
+ return TypeKind.INT;
+ }
+
+ /**
+ * If the argument is a bounded TypeVariable or WildcardType,
+ * return its non-variable, non-wildcard upper bound. Otherwise,
+ * return the type itself.
+ *
+ * @param type a type
+ * @return the non-variable, non-wildcard upper bound of a type,
+ * if it has one, or itself if it has no bounds
+ */
+ public static TypeMirror upperBound(TypeMirror type) {
+ do {
+ if (type instanceof TypeVariable) {
+ TypeVariable tvar = (TypeVariable) type;
+ if (tvar.getUpperBound() != null) {
+ type = tvar.getUpperBound();
+ } else {
+ break;
+ }
+ } else if (type instanceof WildcardType) {
+ WildcardType wc = (WildcardType) type;
+ if (wc.getExtendsBound() != null) {
+ type = wc.getExtendsBound();
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } while (true);
+ return type;
+ }
+
+ /**
+ * Get the type parameter for this wildcard from the underlying type's bound field
+ * This field is sometimes null, in that case this method will return null
+ * @return the TypeParameterElement the wildcard is an argument to
+ */
+ public static TypeParameterElement wildcardToTypeParam(final Type.WildcardType wildcard) {
+
+ final Element typeParamElement;
+ if (wildcard.bound != null) {
+ typeParamElement = wildcard.bound.asElement();
+ } else {
+ typeParamElement = null;
+ }
+
+ return (TypeParameterElement) typeParamElement;
+ }
+
+ /**
+ * Version of com.sun.tools.javac.code.Types.wildUpperBound(Type)
+ * that works with both jdk8 (called upperBound there) and jdk8u.
+ */
+ // TODO: contrast to upperBound.
+ public static Type wildUpperBound(ProcessingEnvironment env, TypeMirror tm) {
+ Type t = (Type) tm;
+ if (t.hasTag(TypeTag.WILDCARD)) {
+ Context context = ((JavacProcessingEnvironment) env).getContext();
+ Type.WildcardType w = (Type.WildcardType) TypeAnnotationUtils.unannotatedType(t);
+ if (w.isSuperBound()) {
+ Symtab syms = Symtab.instance(context);
+ return w.bound == null ? syms.objectType : w.bound.bound;
+ } else {
+ return wildUpperBound(env, w.type);
+ }
+ } else {
+ return TypeAnnotationUtils.unannotatedType(t);
+ }
+ }
+
+ /**
+ * Version of com.sun.tools.javac.code.Types.wildLowerBound(Type)
+ * that works with both jdk8 (called upperBound there) and jdk8u.
+ */
+ public static Type wildLowerBound(ProcessingEnvironment env, TypeMirror tm) {
+ Type t = (Type) tm;
+ if (t.hasTag(WILDCARD)) {
+ Context context = ((JavacProcessingEnvironment) env).getContext();
+ Symtab syms = Symtab.instance(context);
+ Type.WildcardType w = (Type.WildcardType) TypeAnnotationUtils.unannotatedType(t);
+ return w.isExtendsBound() ? syms.botType : wildLowerBound(env, w.type);
+ } else {
+ return t.unannotatedType();
+ }
+ }
+ /**
+ * Returns the {@link TypeMirror} for a given {@link Class}.
+ */
+ public static TypeMirror typeFromClass(Types types, Elements elements, Class<?> clazz) {
+ if (clazz == void.class) {
+ return types.getNoType(TypeKind.VOID);
+ } else if (clazz.isPrimitive()) {
+ String primitiveName = clazz.getName().toUpperCase();
+ TypeKind primitiveKind = TypeKind.valueOf(primitiveName);
+ return types.getPrimitiveType(primitiveKind);
+ } else if (clazz.isArray()) {
+ TypeMirror componentType = typeFromClass(types, elements, clazz.getComponentType());
+ return types.getArrayType(componentType);
+ } else {
+ TypeElement element = elements.getTypeElement(clazz.getCanonicalName());
+ if (element == null) {
+ ErrorReporter.errorAbort("Unrecognized class: " + clazz);
+ return null; // dead code
+ }
+ return element.asType();
+ }
+ }
+
+ /**
+ * Returns an {@link ArrayType} with elements of type {@code componentType}.
+ */
+ public static ArrayType createArrayType(Types types, TypeMirror componentType) {
+ JavacTypes t = (JavacTypes) types;
+ return t.getArrayType(componentType);
+ }
+
+ /**
+ * Returns true if declaredType is a Class that is used to box primitive type
+ * (e.g. declaredType=java.lang.Double and primitiveType=22.5d )
+ */
+ public static boolean isBoxOf(TypeMirror declaredType, TypeMirror primitiveType) {
+ if (declaredType.getKind() != TypeKind.DECLARED) {
+ return false;
+ }
+
+ final String qualifiedName = getQualifiedName((DeclaredType) declaredType).toString();
+ switch (primitiveType.getKind()) {
+ case BOOLEAN: return qualifiedName.equals("java.lang.Boolean");
+ case BYTE: return qualifiedName.equals("java.lang.Byte");
+ case CHAR: return qualifiedName.equals("java.lang.Character");
+ case DOUBLE: return qualifiedName.equals("java.lang.Double");
+ case FLOAT: return qualifiedName.equals("java.lang.Float");
+ case INT: return qualifiedName.equals("java.lang.Integer");
+ case LONG: return qualifiedName.equals("java.lang.Long");
+ case SHORT: return qualifiedName.equals("java.lang.Short");
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Given a bounded type (wildcard or typevar) get the concrete type of its upper bound. If
+ * the bounded type extends other bounded types, this method will iterate through their bounds
+ * until a class, interface, or intersection is found.
+ * @return a type that is not a wildcard or typevar, or null if this type is an unbounded wildcard
+ */
+ public static TypeMirror findConcreteUpperBound(final TypeMirror boundedType) {
+ TypeMirror effectiveUpper = boundedType;
+ outerLoop : while (true) {
+ switch (effectiveUpper.getKind()) {
+ case WILDCARD:
+ effectiveUpper = ((javax.lang.model.type.WildcardType) effectiveUpper).getExtendsBound();
+ if (effectiveUpper == null) {
+ return null;
+ }
+ break;
+
+ case TYPEVAR:
+ effectiveUpper = ((TypeVariable) effectiveUpper).getUpperBound();
+ break;
+
+ default:
+ break outerLoop;
+ }
+ }
+ return effectiveUpper;
+ }
+
+ /**
+ * Returns true if the erased type of subtype is a subtype of the erased type of supertype.
+ *
+ * @param types Types
+ * @param subtype possible subtype
+ * @param supertype possible supertype
+ * @return true if the erased type of subtype is a subtype of the erased type of supertype
+ */
+ public static boolean isErasedSubtype(Types types, TypeMirror subtype, TypeMirror supertype) {
+ return types.isSubtype(types.erasure(subtype), types.erasure(supertype));
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/dist/ManualTaglet.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/dist/ManualTaglet.java
new file mode 100644
index 0000000000..e7df75ff54
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/dist/ManualTaglet.java
@@ -0,0 +1,129 @@
+package org.checkerframework.javacutil.dist;
+
+import java.util.Map;
+
+import com.sun.javadoc.Tag;
+import com.sun.tools.doclets.Taglet;
+
+/**
+ * A taglet for processing the {@code @checker_framework.manual} javadoc block tag, which inserts
+ * references to the Checker Framework manual into javadoc.
+ *
+ * <p>
+ *
+ * The {@code @checker_framework.manual} tag is used as follows:
+ *
+ * <ul>
+ * <li>{@code @checker_framework.manual #} expands to a top-level link to the Checker Framework manual
+ * <li>{@code @checker_framework.manual #anchor text} expands to a link with some text to a
+ * particular part of the manual
+ * </ul>
+ */
+public class ManualTaglet implements Taglet {
+
+ @Override
+ public String getName() {
+ return "checker_framework.manual";
+ }
+
+ @Override
+ public boolean inConstructor() {
+ return true;
+ }
+
+ @Override
+ public boolean inField() {
+ return true;
+ }
+
+ @Override
+ public boolean inMethod() {
+ return true;
+ }
+
+ @Override
+ public boolean inOverview() {
+ return true;
+ }
+
+ @Override
+ public boolean inPackage() {
+ return true;
+ }
+
+ @Override
+ public boolean inType() {
+ return true;
+ }
+
+ @Override
+ public boolean isInlineTag() {
+ return false;
+ }
+
+ /**
+ * Formats a link, given an array of tokens.
+ *
+ * @param parts the array of tokens
+ * @return a link to the manual top-level if the array size is one, or a
+ * link to a part of the manual if it's larger than one
+ */
+ private String formatLink(String[] parts) {
+ String anchor, text;
+ if (parts.length < 2) {
+ anchor = "";
+ text = "Checker Framework";
+ } else {
+ anchor = parts[0];
+ text = parts[1];
+ }
+ return String.format(
+ "<A HREF=\"http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html%s\">%s</A>",
+ anchor, text);
+ }
+
+ /**
+ * Formats the {@code @checker_framework.manual} tag, prepending the tag header to the
+ * tag content.
+ *
+ * @param text the tag content
+ * @return the formatted tag
+ */
+ private String formatHeader(String text) {
+ return String.format(
+ "<DT><B>See the Checker Framework Manual:</B><DD>%s<BR>",
+ text);
+ }
+
+ @Override
+ public String toString(Tag tag) {
+ String[] split = tag.text().split(" ", 2);
+ return formatHeader(formatLink(split));
+ }
+
+ @Override
+ public String toString(Tag[] tags) {
+ if (tags.length == 0) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (Tag t : tags) {
+ String[] split = t.text().split(" ", 2);
+ if (t != tags[0]) {
+ sb.append(", ");
+ }
+ sb.append(formatLink(split));
+ }
+ return formatHeader(sb.toString());
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static void register(Map tagletMap) {
+ ManualTaglet tag = new ManualTaglet();
+ Taglet t = (Taglet) tagletMap.get(tag.getName());
+ if (t != null) {
+ tagletMap.remove(tag.getName());
+ }
+ tagletMap.put(tag.getName(), tag);
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/DetachedVarSymbol.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/DetachedVarSymbol.java
new file mode 100644
index 0000000000..0edf3944e3
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/DetachedVarSymbol.java
@@ -0,0 +1,45 @@
+package org.checkerframework.javacutil.trees;
+
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.util.Name;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * A DetachedVarSymbol represents a variable that is not part of any
+ * AST Tree. DetachedVarSymbols are created when desugaring source
+ * code constructs and they carry important type information, but some
+ * methods such as TreeInfo.declarationFor do not work on them.
+ */
+
+public class DetachedVarSymbol extends Symbol.VarSymbol {
+
+ protected /*@Nullable*/ VariableTree decl;
+
+ /**
+ * Construct a detached variable symbol, given its flags, name,
+ * type and owner.
+ */
+ public DetachedVarSymbol(long flags, Name name, Type type, Symbol owner) {
+ super(flags, name, type, owner);
+ this.decl = null;
+ }
+
+ /**
+ * Set the declaration tree for the variable.
+ */
+ public void setDeclaration(VariableTree decl) {
+ this.decl = decl;
+ }
+
+ /**
+ * Get the declaration tree for the variable.
+ */
+ public /*@Nullable*/ VariableTree getDeclaration() {
+ return decl;
+ }
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeBuilder.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeBuilder.java
new file mode 100644
index 0000000000..2623f2f2e7
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeBuilder.java
@@ -0,0 +1,687 @@
+package org.checkerframework.javacutil.trees;
+
+import java.util.List;
+
+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.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+import org.checkerframework.javacutil.InternalUtils;
+import org.checkerframework.javacutil.TypesUtils;
+
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeInfo;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Names;
+
+/**
+ * The TreeBuilder permits the creation of new AST Trees using the
+ * non-public Java compiler API TreeMaker.
+ */
+
+public class TreeBuilder {
+ protected final Elements elements;
+ protected final Types modelTypes;
+ protected final com.sun.tools.javac.code.Types javacTypes;
+ protected final TreeMaker maker;
+ protected final Names names;
+ protected final Symtab symtab;
+ protected final ProcessingEnvironment env;
+
+ public TreeBuilder(ProcessingEnvironment env) {
+ this.env = env;
+ Context context = ((JavacProcessingEnvironment)env).getContext();
+ elements = env.getElementUtils();
+ modelTypes = env.getTypeUtils();
+ javacTypes = com.sun.tools.javac.code.Types.instance(context);
+ maker = TreeMaker.instance(context);
+ names = Names.instance(context);
+ symtab = Symtab.instance(context);
+ }
+
+ /**
+ * Builds an AST Tree to access the iterator() method of some iterable
+ * expression.
+ *
+ * @param iterableExpr an expression whose type is a subtype of Iterable
+ * @return a MemberSelectTree that accesses the iterator() method of
+ * the expression
+ */
+ public MemberSelectTree buildIteratorMethodAccess(ExpressionTree iterableExpr) {
+ DeclaredType exprType =
+ (DeclaredType)TypesUtils.upperBound(InternalUtils.typeOf(iterableExpr));
+ assert exprType != null : "expression must be of declared type Iterable<>";
+
+ TypeElement exprElement = (TypeElement)exprType.asElement();
+
+ // Find the iterator() method of the iterable type
+ Symbol.MethodSymbol iteratorMethod = null;
+
+ for (ExecutableElement method :
+ ElementFilter.methodsIn(elements.getAllMembers(exprElement))) {
+ Name methodName = method.getSimpleName();
+
+ if (method.getParameters().size() == 0) {
+ if (methodName.contentEquals("iterator")) {
+ iteratorMethod = (Symbol.MethodSymbol)method;
+ }
+ }
+ }
+
+ assert iteratorMethod != null : "no iterator method declared for expression type";
+
+ Type.MethodType methodType = (Type.MethodType)iteratorMethod.asType();
+ Symbol.TypeSymbol methodClass = methodType.asElement();
+ DeclaredType iteratorType = (DeclaredType)methodType.getReturnType();
+ TypeMirror elementType;
+
+ if (iteratorType.getTypeArguments().size() > 0) {
+ elementType = iteratorType.getTypeArguments().get(0);
+ // Remove captured type from a wildcard.
+ if (elementType instanceof Type.CapturedType) {
+ elementType = ((Type.CapturedType)elementType).wildcard;
+ }
+
+ iteratorType =
+ modelTypes.getDeclaredType((TypeElement)modelTypes.asElement(iteratorType),
+ elementType);
+ }
+
+
+ // Replace the iterator method's generic return type with
+ // the actual element type of the expression.
+ Type.MethodType updatedMethodType =
+ new Type.MethodType(com.sun.tools.javac.util.List.<Type>nil(),
+ (Type)iteratorType,
+ com.sun.tools.javac.util.List.<Type>nil(),
+ methodClass);
+
+ JCTree.JCFieldAccess iteratorAccess =
+ (JCTree.JCFieldAccess)
+ maker.Select((JCTree.JCExpression)iterableExpr,
+ iteratorMethod);
+ iteratorAccess.setType(updatedMethodType);
+
+ return iteratorAccess;
+ }
+
+ /**
+ * Builds an AST Tree to access the hasNext() method of an iterator.
+ *
+ * @param iteratorExpr an expression whose type is a subtype of Iterator
+ * @return a MemberSelectTree that accesses the hasNext() method of
+ * the expression
+ */
+ public MemberSelectTree buildHasNextMethodAccess(ExpressionTree iteratorExpr) {
+ DeclaredType exprType = (DeclaredType)InternalUtils.typeOf(iteratorExpr);
+ assert exprType != null : "expression must be of declared type Iterator<>";
+
+ TypeElement exprElement = (TypeElement)exprType.asElement();
+
+ // Find the hasNext() method of the iterator type
+ Symbol.MethodSymbol hasNextMethod = null;
+
+ for (ExecutableElement method :
+ ElementFilter.methodsIn(elements.getAllMembers(exprElement))) {
+ Name methodName = method.getSimpleName();
+
+ if (method.getParameters().size() == 0) {
+ if (methodName.contentEquals("hasNext")) {
+ hasNextMethod = (Symbol.MethodSymbol)method;
+ }
+ }
+ }
+
+ assert hasNextMethod != null : "no hasNext method declared for expression type";
+
+ JCTree.JCFieldAccess hasNextAccess =
+ (JCTree.JCFieldAccess)
+ maker.Select((JCTree.JCExpression)iteratorExpr,
+ hasNextMethod);
+ hasNextAccess.setType(hasNextMethod.asType());
+
+ return hasNextAccess;
+ }
+
+ /**
+ * Builds an AST Tree to access the next() method of an iterator.
+ *
+ * @param iteratorExpr an expression whose type is a subtype of Iterator
+ * @return a MemberSelectTree that accesses the next() method of
+ * the expression
+ */
+ public MemberSelectTree buildNextMethodAccess(ExpressionTree iteratorExpr) {
+ DeclaredType exprType = (DeclaredType)InternalUtils.typeOf(iteratorExpr);
+ assert exprType != null : "expression must be of declared type Iterator<>";
+
+ TypeElement exprElement = (TypeElement)exprType.asElement();
+
+ // Find the next() method of the iterator type
+ Symbol.MethodSymbol nextMethod = null;
+
+ for (ExecutableElement method :
+ ElementFilter.methodsIn(elements.getAllMembers(exprElement))) {
+ Name methodName = method.getSimpleName();
+
+ if (method.getParameters().size() == 0) {
+ if (methodName.contentEquals("next")) {
+ nextMethod = (Symbol.MethodSymbol)method;
+ }
+ }
+ }
+
+ assert nextMethod != null : "no next method declared for expression type";
+
+ Type.MethodType methodType = (Type.MethodType)nextMethod.asType();
+ Symbol.TypeSymbol methodClass = methodType.asElement();
+ Type elementType;
+
+ if (exprType.getTypeArguments().size() > 0) {
+ elementType = (Type)exprType.getTypeArguments().get(0);
+ } else {
+ elementType = symtab.objectType;
+ }
+
+ // Replace the next method's generic return type with
+ // the actual element type of the expression.
+ Type.MethodType updatedMethodType =
+ new Type.MethodType(com.sun.tools.javac.util.List.<Type>nil(),
+ elementType,
+ com.sun.tools.javac.util.List.<Type>nil(),
+ methodClass);
+
+ JCTree.JCFieldAccess nextAccess =
+ (JCTree.JCFieldAccess)
+ maker.Select((JCTree.JCExpression)iteratorExpr,
+ nextMethod);
+ nextAccess.setType(updatedMethodType);
+
+ return nextAccess;
+ }
+
+ /**
+ * Builds an AST Tree to dereference the length field of an array
+ *
+ * @param expression the array expression whose length is being accessed
+ * @return a MemberSelectTree to dereference the length of the array
+ */
+ public MemberSelectTree buildArrayLengthAccess(ExpressionTree expression) {
+
+ return (JCTree.JCFieldAccess)
+ maker.Select((JCTree.JCExpression)expression, symtab.lengthVar);
+ }
+
+ /**
+ * Builds an AST Tree to call a method designated by the argument expression.
+ *
+ * @param methodExpr an expression denoting a method with no arguments
+ * @return a MethodInvocationTree to call the argument method
+ */
+ public MethodInvocationTree buildMethodInvocation(ExpressionTree methodExpr) {
+ return maker.App((JCTree.JCExpression)methodExpr);
+ }
+
+ /**
+ * Builds an AST Tree to call a method designated by methodExpr,
+ * with one argument designated by argExpr.
+ *
+ * @param methodExpr an expression denoting a method with one argument
+ * @param argExpr an expression denoting an argument to the method
+ * @return a MethodInvocationTree to call the argument method
+ */
+ public MethodInvocationTree buildMethodInvocation(ExpressionTree methodExpr,
+ ExpressionTree argExpr) {
+ return maker.App((JCTree.JCExpression)methodExpr,
+ com.sun.tools.javac.util.List.of((JCTree.JCExpression)argExpr));
+ }
+
+ /**
+ * Builds an AST Tree to declare and initialize a variable, with no modifiers.
+ *
+ * @param type the type of the variable
+ * @param name the name of the variable
+ * @param owner the element containing the new symbol
+ * @param initializer the initializer expression
+ * @return a VariableDeclTree declaring the new variable
+ */
+ public VariableTree buildVariableDecl(TypeMirror type,
+ String name,
+ Element owner,
+ ExpressionTree initializer) {
+ DetachedVarSymbol sym =
+ new DetachedVarSymbol(0, names.fromString(name),
+ (Type)type, (Symbol)owner);
+ VariableTree tree = maker.VarDef(sym, (JCTree.JCExpression)initializer);
+ sym.setDeclaration(tree);
+ return tree;
+ }
+
+ /**
+ * Builds an AST Tree to declare and initialize a variable. The
+ * type of the variable is specified by a Tree.
+ *
+ * @param type the type of the variable, as a Tree
+ * @param name the name of the variable
+ * @param owner the element containing the new symbol
+ * @param initializer the initializer expression
+ * @return a VariableDeclTree declaring the new variable
+ */
+ public VariableTree buildVariableDecl(Tree type,
+ String name,
+ Element owner,
+ ExpressionTree initializer) {
+ Type typeMirror = (Type)InternalUtils.typeOf(type);
+ DetachedVarSymbol sym =
+ new DetachedVarSymbol(0, names.fromString(name),
+ typeMirror, (Symbol)owner);
+ JCTree.JCModifiers mods = maker.Modifiers(0);
+ JCTree.JCVariableDecl decl = maker.VarDef(mods, sym.name,
+ (JCTree.JCExpression)type,
+ (JCTree.JCExpression)initializer);
+ decl.setType(typeMirror);
+ decl.sym = sym;
+ sym.setDeclaration(decl);
+ return decl;
+ }
+
+ /**
+ * Builds an AST Tree to refer to a variable.
+ *
+ * @param decl the declaration of the variable
+ * @return an IdentifierTree to refer to the variable
+ */
+ public IdentifierTree buildVariableUse(VariableTree decl) {
+ return (IdentifierTree)maker.Ident((JCTree.JCVariableDecl)decl);
+ }
+
+ /**
+ * Builds an AST Tree to cast the type of an expression.
+ *
+ * @param type the type to cast to
+ * @param expr the expression to be cast
+ * @return a cast of the expression to the type
+ */
+ public TypeCastTree buildTypeCast(TypeMirror type,
+ ExpressionTree expr) {
+ return maker.TypeCast((Type)type, (JCTree.JCExpression)expr);
+ }
+
+ /**
+ * Builds an AST Tree to assign an expression to a variable.
+ *
+ * @param variable the declaration of the variable to assign to
+ * @param expr the expression to be assigned
+ * @return a statement assigning the expression to the variable
+ */
+ public StatementTree buildAssignment(VariableTree variable,
+ ExpressionTree expr) {
+ return maker.Assignment(TreeInfo.symbolFor((JCTree)variable),
+ (JCTree.JCExpression)expr);
+ }
+
+ /**
+ * Builds an AST Tree to assign an RHS expression to an LHS expression.
+ *
+ * @param lhs the expression to be assigned to
+ * @param rhs the expression to be assigned
+ * @return a statement assigning the expression to the variable
+ */
+ public AssignmentTree buildAssignment(ExpressionTree lhs,
+ ExpressionTree rhs) {
+ JCTree.JCAssign assign =
+ maker.Assign((JCTree.JCExpression)lhs, (JCTree.JCExpression)rhs);
+ assign.setType((Type)InternalUtils.typeOf(lhs));
+ return assign;
+ }
+
+ /**
+ * Builds an AST Tree representing a literal value of primitive
+ * or String type.
+ */
+ public LiteralTree buildLiteral(Object value) {
+ return maker.Literal(value);
+ }
+
+ /**
+ * Builds an AST Tree to compare two operands with less than.
+ *
+ * @param left the left operand tree
+ * @param right the right operand tree
+ * @return a Tree representing "left &lt; right"
+ */
+ public BinaryTree buildLessThan(ExpressionTree left, ExpressionTree right) {
+ JCTree.JCBinary binary =
+ maker.Binary(JCTree.Tag.LT, (JCTree.JCExpression)left,
+ (JCTree.JCExpression)right);
+ binary.setType((Type)modelTypes.getPrimitiveType(TypeKind.BOOLEAN));
+ return binary;
+ }
+
+ /**
+ * Builds an AST Tree to dereference an array.
+ *
+ * @param array the array to dereference
+ * @param index the index at which to dereference
+ * @return a Tree representing the dereference
+ */
+ public ArrayAccessTree buildArrayAccess(ExpressionTree array,
+ ExpressionTree index) {
+ ArrayType arrayType = (ArrayType)InternalUtils.typeOf(array);
+ JCTree.JCArrayAccess access =
+ maker.Indexed((JCTree.JCExpression)array, (JCTree.JCExpression)index);
+ access.setType((Type)arrayType.getComponentType());
+ return access;
+ }
+
+ /**
+ * Builds an AST Tree to refer to a class name.
+ *
+ * @param elt an element representing the class
+ * @return an IdentifierTree referring to the class
+ */
+ public IdentifierTree buildClassUse(Element elt) {
+ return maker.Ident((Symbol)elt);
+ }
+
+ /**
+ * Builds an AST Tree to access the valueOf() method of boxed type
+ * such as Short or Float.
+ *
+ * @param expr an expression whose type is a boxed type
+ * @return a MemberSelectTree that accesses the valueOf() method of
+ * the expression
+ */
+ public MemberSelectTree buildValueOfMethodAccess(Tree expr) {
+ TypeMirror boxedType = InternalUtils.typeOf(expr);
+
+ assert TypesUtils.isBoxedPrimitive(boxedType);
+
+ // Find the valueOf(unboxedType) method of the boxed type
+ Symbol.MethodSymbol valueOfMethod = getValueOfMethod(env, boxedType);
+
+ Type.MethodType methodType = (Type.MethodType)valueOfMethod.asType();
+
+ JCTree.JCFieldAccess valueOfAccess =
+ (JCTree.JCFieldAccess)
+ maker.Select((JCTree.JCExpression)expr, valueOfMethod);
+ valueOfAccess.setType(methodType);
+
+ return valueOfAccess;
+ }
+
+ /**
+ * Returns the valueOf method of a boxed type such as Short or Float.
+ */
+ public static Symbol.MethodSymbol getValueOfMethod(ProcessingEnvironment env, TypeMirror boxedType) {
+ Symbol.MethodSymbol valueOfMethod = null;
+
+ TypeMirror unboxedType = env.getTypeUtils().unboxedType(boxedType);
+ TypeElement boxedElement = (TypeElement)((DeclaredType)boxedType).asElement();
+ for (ExecutableElement method :
+ ElementFilter.methodsIn(env.getElementUtils().getAllMembers(boxedElement))) {
+ Name methodName = method.getSimpleName();
+
+ if (methodName.contentEquals("valueOf")) {
+ List<? extends VariableElement> params = method.getParameters();
+ if (params.size() == 1 && env.getTypeUtils().isSameType(params.get(0).asType(), unboxedType)) {
+ valueOfMethod = (Symbol.MethodSymbol)method;
+ }
+ }
+ }
+
+ assert valueOfMethod != null : "no valueOf method declared for boxed type";
+ return valueOfMethod;
+ }
+
+ /**
+ * Builds an AST Tree to access the *Value() method of a
+ * boxed type such as Short or Float, where * is the corresponding
+ * primitive type (i.e. shortValue or floatValue).
+ *
+ * @param expr an expression whose type is a boxed type
+ * @return a MemberSelectTree that accesses the *Value() method of
+ * the expression
+ */
+ public MemberSelectTree buildPrimValueMethodAccess(Tree expr) {
+ TypeMirror boxedType = InternalUtils.typeOf(expr);
+ TypeElement boxedElement = (TypeElement)((DeclaredType)boxedType).asElement();
+
+ assert TypesUtils.isBoxedPrimitive(boxedType);
+ TypeMirror unboxedType = modelTypes.unboxedType(boxedType);
+
+ // Find the *Value() method of the boxed type
+ String primValueName = unboxedType.toString() + "Value";
+ Symbol.MethodSymbol primValueMethod = null;
+
+ for (ExecutableElement method :
+ ElementFilter.methodsIn(elements.getAllMembers(boxedElement))) {
+ Name methodName = method.getSimpleName();
+
+ if (methodName.contentEquals(primValueName) &&
+ method.getParameters().size() == 0) {
+ primValueMethod = (Symbol.MethodSymbol)method;
+ }
+ }
+
+ assert primValueMethod != null : "no *Value method declared for boxed type";
+
+ Type.MethodType methodType = (Type.MethodType)primValueMethod.asType();
+
+ JCTree.JCFieldAccess primValueAccess =
+ (JCTree.JCFieldAccess)
+ maker.Select((JCTree.JCExpression)expr, primValueMethod);
+ primValueAccess.setType(methodType);
+
+ return primValueAccess;
+ }
+
+ /**
+ * Map public AST Tree.Kinds to internal javac JCTree.Tags.
+ */
+ public JCTree.Tag kindToTag(Tree.Kind kind) {
+ switch (kind) {
+ case AND:
+ return JCTree.Tag.BITAND;
+ case AND_ASSIGNMENT:
+ return JCTree.Tag.BITAND_ASG;
+ case ANNOTATION:
+ return JCTree.Tag.ANNOTATION;
+ case ANNOTATION_TYPE:
+ return JCTree.Tag.TYPE_ANNOTATION;
+ case ARRAY_ACCESS:
+ return JCTree.Tag.INDEXED;
+ case ARRAY_TYPE:
+ return JCTree.Tag.TYPEARRAY;
+ case ASSERT:
+ return JCTree.Tag.ASSERT;
+ case ASSIGNMENT:
+ return JCTree.Tag.ASSIGN;
+ case BITWISE_COMPLEMENT:
+ return JCTree.Tag.COMPL;
+ case BLOCK:
+ return JCTree.Tag.BLOCK;
+ case BREAK:
+ return JCTree.Tag.BREAK;
+ case CASE:
+ return JCTree.Tag.CASE;
+ case CATCH:
+ return JCTree.Tag.CATCH;
+ case CLASS:
+ return JCTree.Tag.CLASSDEF;
+ case CONDITIONAL_AND:
+ return JCTree.Tag.AND;
+ case CONDITIONAL_EXPRESSION:
+ return JCTree.Tag.CONDEXPR;
+ case CONDITIONAL_OR:
+ return JCTree.Tag.OR;
+ case CONTINUE:
+ return JCTree.Tag.CONTINUE;
+ case DIVIDE:
+ return JCTree.Tag.DIV;
+ case DIVIDE_ASSIGNMENT:
+ return JCTree.Tag.DIV_ASG;
+ case DO_WHILE_LOOP:
+ return JCTree.Tag.DOLOOP;
+ case ENHANCED_FOR_LOOP:
+ return JCTree.Tag.FOREACHLOOP;
+ case EQUAL_TO:
+ return JCTree.Tag.EQ;
+ case EXPRESSION_STATEMENT:
+ return JCTree.Tag.EXEC;
+ case FOR_LOOP:
+ return JCTree.Tag.FORLOOP;
+ case GREATER_THAN:
+ return JCTree.Tag.GT;
+ case GREATER_THAN_EQUAL:
+ return JCTree.Tag.GE;
+ case IDENTIFIER:
+ return JCTree.Tag.IDENT;
+ case IF:
+ return JCTree.Tag.IF;
+ case IMPORT:
+ return JCTree.Tag.IMPORT;
+ case INSTANCE_OF:
+ return JCTree.Tag.TYPETEST;
+ case LABELED_STATEMENT:
+ return JCTree.Tag.LABELLED;
+ case LEFT_SHIFT:
+ return JCTree.Tag.SL;
+ case LEFT_SHIFT_ASSIGNMENT:
+ return JCTree.Tag.SL_ASG;
+ case LESS_THAN:
+ return JCTree.Tag.LT;
+ case LESS_THAN_EQUAL:
+ return JCTree.Tag.LE;
+ case LOGICAL_COMPLEMENT:
+ return JCTree.Tag.NOT;
+ case MEMBER_SELECT:
+ return JCTree.Tag.SELECT;
+ case METHOD:
+ return JCTree.Tag.METHODDEF;
+ case METHOD_INVOCATION:
+ return JCTree.Tag.APPLY;
+ case MINUS:
+ return JCTree.Tag.MINUS;
+ case MINUS_ASSIGNMENT:
+ return JCTree.Tag.MINUS_ASG;
+ case MODIFIERS:
+ return JCTree.Tag.MODIFIERS;
+ case MULTIPLY:
+ return JCTree.Tag.MUL;
+ case MULTIPLY_ASSIGNMENT:
+ return JCTree.Tag.MUL_ASG;
+ case NEW_ARRAY:
+ return JCTree.Tag.NEWARRAY;
+ case NEW_CLASS:
+ return JCTree.Tag.NEWCLASS;
+ case NOT_EQUAL_TO:
+ return JCTree.Tag.NE;
+ case OR:
+ return JCTree.Tag.BITOR;
+ case OR_ASSIGNMENT:
+ return JCTree.Tag.BITOR_ASG;
+ case PARENTHESIZED:
+ return JCTree.Tag.PARENS;
+ case PLUS:
+ return JCTree.Tag.PLUS;
+ case PLUS_ASSIGNMENT:
+ return JCTree.Tag.PLUS_ASG;
+ case POSTFIX_DECREMENT:
+ return JCTree.Tag.POSTDEC;
+ case POSTFIX_INCREMENT:
+ return JCTree.Tag.POSTINC;
+ case PREFIX_DECREMENT:
+ return JCTree.Tag.PREDEC;
+ case PREFIX_INCREMENT:
+ return JCTree.Tag.PREINC;
+ case REMAINDER:
+ return JCTree.Tag.MOD;
+ case REMAINDER_ASSIGNMENT:
+ return JCTree.Tag.MOD_ASG;
+ case RETURN:
+ return JCTree.Tag.RETURN;
+ case RIGHT_SHIFT:
+ return JCTree.Tag.SR;
+ case RIGHT_SHIFT_ASSIGNMENT:
+ return JCTree.Tag.SR_ASG;
+ case SWITCH:
+ return JCTree.Tag.SWITCH;
+ case SYNCHRONIZED:
+ return JCTree.Tag.SYNCHRONIZED;
+ case THROW:
+ return JCTree.Tag.THROW;
+ case TRY:
+ return JCTree.Tag.TRY;
+ case TYPE_CAST:
+ return JCTree.Tag.TYPECAST;
+ case TYPE_PARAMETER:
+ return JCTree.Tag.TYPEPARAMETER;
+ case UNARY_MINUS:
+ return JCTree.Tag.NEG;
+ case UNARY_PLUS:
+ return JCTree.Tag.POS;
+ case UNION_TYPE:
+ return JCTree.Tag.TYPEUNION;
+ case UNSIGNED_RIGHT_SHIFT:
+ return JCTree.Tag.USR;
+ case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
+ return JCTree.Tag.USR_ASG;
+ case VARIABLE:
+ return JCTree.Tag.VARDEF;
+ case WHILE_LOOP:
+ return JCTree.Tag.WHILELOOP;
+ case XOR:
+ return JCTree.Tag.BITXOR;
+ case XOR_ASSIGNMENT:
+ return JCTree.Tag.BITXOR_ASG;
+ default:
+ return JCTree.Tag.NO_TAG;
+ }
+ }
+
+ /**
+ * Builds an AST Tree to perform a binary operation.
+ *
+ * @param type result type of the operation
+ * @param op AST Tree operator
+ * @param left the left operand tree
+ * @param right the right operand tree
+ * @return a Tree representing "left &lt; right"
+ */
+ public BinaryTree buildBinary(TypeMirror type, Tree.Kind op, ExpressionTree left, ExpressionTree right) {
+ JCTree.Tag jcOp = kindToTag(op);
+ JCTree.JCBinary binary =
+ maker.Binary(jcOp, (JCTree.JCExpression)left,
+ (JCTree.JCExpression)right);
+ binary.setType((Type)type);
+ return binary;
+ }
+
+}
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java
new file mode 100644
index 0000000000..1ac9d09309
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java
@@ -0,0 +1,144 @@
+package org.checkerframework.javacutil.trees;
+
+import java.util.StringTokenizer;
+
+import javax.annotation.processing.ProcessingEnvironment;
+
+import com.sun.source.tree.ExpressionTree;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Names;
+
+/**
+ * A Utility class for parsing Java expression snippets, and converting them
+ * to proper Javac AST nodes.
+ *
+ * This is useful for parsing {@code EnsuresNonNull*},
+ * and {@code KeyFor} values.
+ *
+ * Currently, it handles four tree types only:
+ * <ul>
+ * <li>Identifier tree (e.g. {@code id})</li>
+ * <li>Literal tree (e.g. 2, 3)</li>
+ * <li>Method invocation tree (e.g. {@code method(2, 3)})</li>
+ * <li>Member select tree (e.g. {@code Class.field}, {@code instance.method()})
+ * <li>Array access tree (e.g. {@code array[id]})</li>
+ * </ul>
+ *
+ * Notable limitation: Doesn't handle spaces, or non-method-argument
+ * parenthesis.
+ *
+ * It's implemented via a Recursive-Descend parser.
+ */
+public class TreeParser {
+ private static final String DELIMS = ".[](),";
+ private static final String SENTINAL = "";
+
+ private final TreeMaker maker;
+ private final Names names;
+
+ public TreeParser(ProcessingEnvironment env) {
+ Context context = ((JavacProcessingEnvironment)env).getContext();
+ maker = TreeMaker.instance(context);
+ names = Names.instance(context);
+ }
+
+ /**
+ * Parses the snippet in the string as an internal Javac AST expression
+ * node
+ *
+ * @param s the java snippet
+ * @return the AST corresponding to the snippet
+ */
+ public ExpressionTree parseTree(String s) {
+ tokenizer = new StringTokenizer(s, DELIMS, true);
+ token = tokenizer.nextToken();
+
+ try {
+ return parseExpression();
+ } catch (Exception e) {
+ throw new ParseError(e);
+ } finally {
+ tokenizer = null;
+ token = null;
+ }
+ }
+
+ StringTokenizer tokenizer = null;
+ String token = null;
+
+ private String nextToken() {
+ token = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : SENTINAL;
+ return token;
+ }
+
+ JCExpression fromToken(String token) {
+ // Optimization
+ if ("true".equals(token)) {
+ return maker.Literal(true);
+ } else if ("false".equals(token)) {
+ return maker.Literal(false);
+ }
+
+ if (Character.isLetter(token.charAt(0))) {
+ return maker.Ident(names.fromString(token));
+ }
+
+ Object value = null;
+ try {
+ value = Integer.valueOf(token);
+ } catch (Exception e2) { try {
+ value = Double.valueOf(token);
+ } catch (Exception ef) {}}
+ assert value != null;
+ return maker.Literal(value);
+ }
+
+ JCExpression parseExpression() {
+ JCExpression tree = fromToken(token);
+
+ while (tokenizer.hasMoreTokens()) {
+ String delim = nextToken();
+ if (".".equals(delim)) {
+ nextToken();
+ tree = maker.Select(tree,
+ names.fromString(token));
+ } else if ("(".equals(delim)) {
+ nextToken();
+ ListBuffer<JCExpression> args = new ListBuffer<>();
+ while (!")".equals(token)) {
+ JCExpression arg = parseExpression();
+ args.append(arg);
+ if (",".equals(token)) {
+ nextToken();
+ }
+ }
+ // For now, handle empty args only
+ assert ")".equals(token);
+ tree = maker.Apply(List.<JCExpression>nil(),
+ tree, args.toList());
+ } else if ("[".equals(token)) {
+ nextToken();
+ JCExpression index = parseExpression();
+ assert "]".equals(token);
+ tree = maker.Indexed(tree, index);
+ } else {
+ return tree;
+ }
+ }
+
+ return tree;
+ }
+
+ class ParseError extends RuntimeException {
+ private static final long serialVersionUID = 1887754619522101929L;
+
+ ParseError(Throwable cause) {
+ super(cause);
+ }
+ }
+}