aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Damien Martin-Guillerez <dmarting@google.com>2016-06-29 14:24:16 +0200
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-06-30 15:11:05 +0200
commita18add1613574a15c81f60bde847c5d7b2bedcb5 (patch)
tree103fa812c66403f5e8caefb898ea3f18107865dc
parent14c7964b8bc0da2bab65b1c28aa8c302846d7720 (diff)
Adds the source of the checker framework
This needs to predate the rest of the changes to the checker framework to keep the build green. Also add the source of javacutil part of the checker framework, that will be included in the next change. Change-Id: Ie18d0e8e21035ce5141416e552a83d893f71b88b
-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);
+ }
+ }
+}