mirror of
https://github.com/github/codeql.git
synced 2026-04-17 21:14:02 +02:00
Java: IPA the CFG
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* The only API exported by this library are the toplevel classes `ControlFlowNode`
|
||||
* and its subclass `ConditionNode`, which wrap the successor relation and the
|
||||
* concept of true- and false-successors of conditions. A cfg node may either be a
|
||||
* statement, an expression, or the enclosing callable, indicating that
|
||||
* statement, an expression, or an exit node for a callable, indicating that
|
||||
* execution of the callable terminates.
|
||||
*/
|
||||
|
||||
@@ -84,45 +84,122 @@ private import Completion
|
||||
private import controlflow.internal.Preconditions
|
||||
private import controlflow.internal.SwitchCases
|
||||
|
||||
/** A node in the expression-level control-flow graph. */
|
||||
class ControlFlowNode extends Top, @exprparent {
|
||||
/** Gets the statement containing this node, if any. */
|
||||
Stmt getEnclosingStmt() {
|
||||
result = this or
|
||||
result = this.(Expr).getEnclosingStmt()
|
||||
/** Provides the definition of control flow nodes. */
|
||||
module ControlFlow {
|
||||
private predicate hasControlFlow(Expr e) {
|
||||
not exists(ConstCase cc | e = cc.getValue(_)) and
|
||||
not e.getParent*() instanceof Annotation and
|
||||
not e instanceof TypeAccess and
|
||||
not e instanceof ArrayTypeAccess and
|
||||
not e instanceof UnionTypeAccess and
|
||||
not e instanceof IntersectionTypeAccess and
|
||||
not e instanceof WildcardTypeAccess and
|
||||
not exists(AssignExpr ae | ae.getDest() = e)
|
||||
}
|
||||
|
||||
/** Gets the immediately enclosing callable whose body contains this node. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = this or
|
||||
result = this.(Stmt).getEnclosingCallable() or
|
||||
result = this.(Expr).getEnclosingCallable()
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) { hasControlFlow(e) } or
|
||||
TStmtNode(Stmt s) or
|
||||
TExitNode(Callable c) { exists(c.getBody()) }
|
||||
|
||||
/** A node in the expression-level control-flow graph. */
|
||||
class Node extends TNode {
|
||||
/** Gets the statement containing this node, if any. */
|
||||
Stmt getEnclosingStmt() {
|
||||
result = this.asStmt() or
|
||||
result = this.asExpr().getEnclosingStmt()
|
||||
}
|
||||
|
||||
/** Gets the immediately enclosing callable whose body contains this node. */
|
||||
Callable getEnclosingCallable() {
|
||||
this = TExitNode(result) or
|
||||
result = this.asStmt().getEnclosingCallable() or
|
||||
result = this.asExpr().getEnclosingCallable()
|
||||
}
|
||||
|
||||
/** Gets the statement this `Node` corresponds to, if any. */
|
||||
Stmt asStmt() { this = TStmtNode(result) }
|
||||
|
||||
/** Gets the expression this `Node` corresponds to, if any. */
|
||||
Expr asExpr() { this = TExprNode(result) }
|
||||
|
||||
/** Gets the call this `Node` corresponds to, if any. */
|
||||
Call asCall() {
|
||||
result = this.asExpr() or
|
||||
result = this.asStmt()
|
||||
}
|
||||
|
||||
/** Gets an immediate successor of this node. */
|
||||
Node getASuccessor() { result = succ(this) }
|
||||
|
||||
/** Gets an immediate predecessor of this node. */
|
||||
Node getAPredecessor() { this = succ(result) }
|
||||
|
||||
/** Gets an exception successor of this node. */
|
||||
Node getAnExceptionSuccessor() { result = succ(this, ThrowCompletion(_)) }
|
||||
|
||||
/** Gets a successor of this node that is neither an exception successor nor a jump (break, continue, return). */
|
||||
Node getANormalSuccessor() {
|
||||
result = succ(this, BooleanCompletion(_, _)) or
|
||||
result = succ(this, NormalCompletion())
|
||||
}
|
||||
|
||||
/** Gets the basic block that contains this node. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
result = this.asExpr().toString()
|
||||
or
|
||||
result = this.asStmt().toString()
|
||||
or
|
||||
result = "Exit" and this instanceof ExitNode
|
||||
}
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() {
|
||||
result = this.asExpr().getLocation() or
|
||||
result = this.asStmt().getLocation() or
|
||||
result = this.(ExitNode).getEnclosingCallable().getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most appropriate AST node for this control flow node, if any.
|
||||
*
|
||||
* This is needed for the equivalence relation on basic blocks in range
|
||||
* analysis.
|
||||
*/
|
||||
ExprParent getAstNode() {
|
||||
result = this.asExpr() or
|
||||
result = this.asStmt() or
|
||||
this = TExitNode(result)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets an immediate successor of this node. */
|
||||
ControlFlowNode getASuccessor() { result = succ(this) }
|
||||
|
||||
/** Gets an immediate predecessor of this node. */
|
||||
ControlFlowNode getAPredecessor() { this = succ(result) }
|
||||
|
||||
/** Gets an exception successor of this node. */
|
||||
ControlFlowNode getAnExceptionSuccessor() { result = succ(this, ThrowCompletion(_)) }
|
||||
|
||||
/** Gets a successor of this node that is neither an exception successor nor a jump (break, continue, return). */
|
||||
ControlFlowNode getANormalSuccessor() {
|
||||
result = succ(this, BooleanCompletion(_, _)) or
|
||||
result = succ(this, NormalCompletion())
|
||||
}
|
||||
|
||||
/** Gets the basic block that contains this node. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
/** A synthetic node for the exit of a callable. */
|
||||
class ExitNode extends Node, TExitNode { }
|
||||
}
|
||||
|
||||
class ControlFlowNode = ControlFlow::Node;
|
||||
|
||||
/** Gets the intra-procedural successor of `n`. */
|
||||
private ControlFlowNode succ(ControlFlowNode n) { result = succ(n, _) }
|
||||
|
||||
cached
|
||||
private module ControlFlowGraphImpl {
|
||||
private import ControlFlow
|
||||
|
||||
private class AstNode extends ExprParent {
|
||||
AstNode() { this instanceof Expr or this instanceof Stmt }
|
||||
|
||||
Stmt getEnclosingStmt() {
|
||||
result = this or
|
||||
result = this.(Expr).getEnclosingStmt()
|
||||
}
|
||||
|
||||
Node getCFGNode() { result.asExpr() = this or result.asStmt() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a label that applies to this statement.
|
||||
*/
|
||||
@@ -167,7 +244,7 @@ private module ControlFlowGraphImpl {
|
||||
* `ClassCastException` is expected, or because it is a Kotlin not-null check
|
||||
* and a `NullPointerException` is expected.
|
||||
*/
|
||||
private predicate mayThrow(ControlFlowNode n, ThrowableType t) {
|
||||
private predicate mayThrow(AstNode n, ThrowableType t) {
|
||||
t = n.(ThrowStmt).getThrownExceptionType()
|
||||
or
|
||||
exists(Call c | c = n |
|
||||
@@ -200,7 +277,7 @@ private module ControlFlowGraphImpl {
|
||||
* Bind `t` to an unchecked exception that may transfer control to a finally
|
||||
* block inside which `n` is nested.
|
||||
*/
|
||||
private predicate uncheckedExceptionFromFinally(ControlFlowNode n, ThrowableType t) {
|
||||
private predicate uncheckedExceptionFromFinally(AstNode n, ThrowableType t) {
|
||||
exists(TryStmt try |
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
@@ -214,7 +291,7 @@ private module ControlFlowGraphImpl {
|
||||
* Bind `t` to all unchecked exceptions that may be caught by some
|
||||
* `try-catch` inside which `n` is nested.
|
||||
*/
|
||||
private predicate uncheckedExceptionFromCatch(ControlFlowNode n, ThrowableType t) {
|
||||
private predicate uncheckedExceptionFromCatch(AstNode n, ThrowableType t) {
|
||||
exists(TryStmt try, UncheckedThrowableSuperType caught |
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
@@ -229,7 +306,7 @@ private module ControlFlowGraphImpl {
|
||||
* body or the resources (if any) of `try`.
|
||||
*/
|
||||
private ThrowableType thrownInBody(TryStmt try) {
|
||||
exists(ControlFlowNode n | mayThrow(n, result) |
|
||||
exists(AstNode n | mayThrow(n, result) |
|
||||
n.getEnclosingStmt().getEnclosingStmt+() = try.getBlock() or
|
||||
n.(Expr).getParent*() = try.getAResource()
|
||||
)
|
||||
@@ -287,7 +364,7 @@ private module ControlFlowGraphImpl {
|
||||
* That is, contexts where the control-flow edges depend on `value` given that `b` ends
|
||||
* with a `booleanCompletion(value, _)`.
|
||||
*/
|
||||
private predicate inBooleanContext(ControlFlowNode b) {
|
||||
private predicate inBooleanContext(AstNode b) {
|
||||
exists(LogicExpr logexpr |
|
||||
logexpr.(BinaryExpr).getLeftOperand() = b
|
||||
or
|
||||
@@ -493,9 +570,7 @@ private module ControlFlowGraphImpl {
|
||||
* immediately before either falling through to execute successor statements or execute a rule body
|
||||
* if present. `completion` is the completion kind of the last operation.
|
||||
*/
|
||||
private predicate lastPatternCaseMatchingOp(
|
||||
PatternCase pc, ControlFlowNode last, Completion completion
|
||||
) {
|
||||
private predicate lastPatternCaseMatchingOp(PatternCase pc, Node last, Completion completion) {
|
||||
last(pc.getAPattern(), last, completion) and
|
||||
completion = NormalCompletion() and
|
||||
not exists(pc.getGuard())
|
||||
@@ -514,7 +589,7 @@ private module ControlFlowGraphImpl {
|
||||
* and `ThrowStmt`. CFG nodes without child nodes in the CFG that may complete
|
||||
* normally are also included.
|
||||
*/
|
||||
private class PostOrderNode extends ControlFlowNode {
|
||||
private class PostOrderNode extends AstNode {
|
||||
PostOrderNode() {
|
||||
// For VarAccess and ArrayAccess only read accesses (r-values) are included,
|
||||
// as write accesses aren't included in the CFG.
|
||||
@@ -576,7 +651,7 @@ private module ControlFlowGraphImpl {
|
||||
}
|
||||
|
||||
/** Gets child nodes in their order of execution. Indexing starts at either -1 or 0. */
|
||||
ControlFlowNode getChildNode(int index) {
|
||||
AstNode getChildNode(int index) {
|
||||
exists(ArrayAccess e | e = this |
|
||||
index = 0 and result = e.getArray()
|
||||
or
|
||||
@@ -649,7 +724,7 @@ private module ControlFlowGraphImpl {
|
||||
}
|
||||
|
||||
/** Gets the first child node, if any. */
|
||||
ControlFlowNode firstChild() {
|
||||
AstNode firstChild() {
|
||||
result = this.getChildNode(-1)
|
||||
or
|
||||
result = this.getChildNode(0) and not exists(this.getChildNode(-1))
|
||||
@@ -687,18 +762,18 @@ private module ControlFlowGraphImpl {
|
||||
/**
|
||||
* Determine the part of the AST node `n` that will be executed first.
|
||||
*/
|
||||
private ControlFlowNode first(ControlFlowNode n) {
|
||||
result = n and n instanceof LogicExpr
|
||||
private Node first(AstNode n) {
|
||||
result.asExpr() = n and n instanceof LogicExpr
|
||||
or
|
||||
result = n and n instanceof ConditionalExpr
|
||||
result.asExpr() = n and n instanceof ConditionalExpr
|
||||
or
|
||||
result = n and n instanceof WhenExpr
|
||||
result.asExpr() = n and n instanceof WhenExpr
|
||||
or
|
||||
result = n and n instanceof WhenBranch
|
||||
result.asStmt() = n and n instanceof WhenBranch
|
||||
or
|
||||
result = n and n instanceof StmtExpr
|
||||
result.asExpr() = n and n instanceof StmtExpr
|
||||
or
|
||||
result = n and n.(PostOrderNode).isLeafNode()
|
||||
result = n.getCFGNode() and n.(PostOrderNode).isLeafNode()
|
||||
or
|
||||
result = first(n.(PostOrderNode).firstChild())
|
||||
or
|
||||
@@ -706,12 +781,11 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
result = first(n.(SynchronizedStmt).getExpr())
|
||||
or
|
||||
result = n and
|
||||
n instanceof Stmt and
|
||||
result.asStmt() = n and
|
||||
not n instanceof PostOrderNode and
|
||||
not n instanceof SynchronizedStmt
|
||||
or
|
||||
result = n and n instanceof SwitchExpr
|
||||
result.asExpr() = n and n instanceof SwitchExpr
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -722,9 +796,7 @@ private module ControlFlowGraphImpl {
|
||||
* node in the `try` block that may not complete normally, or a node in
|
||||
* the `try` block that has no control flow successors inside the block.
|
||||
*/
|
||||
private predicate catchOrFinallyCompletion(
|
||||
TryStmt try, ControlFlowNode last, Completion completion
|
||||
) {
|
||||
private predicate catchOrFinallyCompletion(TryStmt try, Node last, Completion completion) {
|
||||
last(try.getBlock(), last, completion)
|
||||
or
|
||||
last(try.getAResource(), last, completion) and completion = ThrowCompletion(_)
|
||||
@@ -737,7 +809,7 @@ private module ControlFlowGraphImpl {
|
||||
* In other words, if `last` throws an exception it is possibly not caught by any
|
||||
* of the catch clauses.
|
||||
*/
|
||||
private predicate uncaught(TryStmt try, ControlFlowNode last, Completion completion) {
|
||||
private predicate uncaught(TryStmt try, Node last, Completion completion) {
|
||||
catchOrFinallyCompletion(try, last, completion) and
|
||||
(
|
||||
exists(ThrowableType thrown |
|
||||
@@ -767,12 +839,12 @@ private module ControlFlowGraphImpl {
|
||||
* This is similar to `uncaught`, but also includes final statements of `catch`
|
||||
* clauses.
|
||||
*/
|
||||
private predicate finallyPred(TryStmt try, ControlFlowNode last, Completion completion) {
|
||||
private predicate finallyPred(TryStmt try, Node last, Completion completion) {
|
||||
uncaught(try, last, completion) or
|
||||
last(try.getACatchClause(), last, completion)
|
||||
}
|
||||
|
||||
private predicate lastInFinally(TryStmt try, ControlFlowNode last) {
|
||||
private predicate lastInFinally(TryStmt try, Node last) {
|
||||
last(try.getFinally(), last, NormalCompletion())
|
||||
}
|
||||
|
||||
@@ -796,7 +868,7 @@ private module ControlFlowGraphImpl {
|
||||
* A `booleanCompletion` implies that `n` is an `Expr`. Any abnormal
|
||||
* completion besides `throwCompletion` implies that `n` is a `Stmt`.
|
||||
*/
|
||||
private predicate last(ControlFlowNode n, ControlFlowNode last, Completion completion) {
|
||||
private predicate last(AstNode n, Node last, Completion completion) {
|
||||
// Exceptions are propagated from any sub-expression.
|
||||
// As are any break, yield, continue, or return completions.
|
||||
exists(Expr e | e.getParent() = n |
|
||||
@@ -853,15 +925,18 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe | ioe.isPattern() and ioe = n |
|
||||
last = n and completion = basicBooleanCompletion(false)
|
||||
last.asExpr() = n and completion = basicBooleanCompletion(false)
|
||||
or
|
||||
last(ioe.getPattern(), last, NormalCompletion()) and completion = basicBooleanCompletion(true)
|
||||
)
|
||||
or
|
||||
// The last node of a node executed in post-order is the node itself.
|
||||
n.(PostOrderNode).mayCompleteNormally() and last = n and completion = NormalCompletion()
|
||||
// n.(PostOrderNode).mayCompleteNormally() and last = n and completion = NormalCompletion()
|
||||
exists(PostOrderNode p | p = n |
|
||||
p.mayCompleteNormally() and last = p.getCFGNode() and completion = NormalCompletion()
|
||||
)
|
||||
or
|
||||
last = n and completion = basicBooleanCompletion(n.(BooleanLiteral).getBooleanValue())
|
||||
last.asExpr() = n and completion = basicBooleanCompletion(n.(BooleanLiteral).getBooleanValue())
|
||||
or
|
||||
// The last statement in a block is any statement that does not complete normally,
|
||||
// or the last statement.
|
||||
@@ -997,7 +1072,7 @@ private module ControlFlowGraphImpl {
|
||||
// * On success of its guard test, if it is not a rule (boolean true)
|
||||
// (the latter two cases are accounted for by lastPatternCaseMatchingOp)
|
||||
exists(PatternCase pc | n = pc |
|
||||
last = pc and completion = basicBooleanCompletion(false)
|
||||
last.asStmt() = pc and completion = basicBooleanCompletion(false)
|
||||
or
|
||||
last(pc.getGuard(), last, completion) and
|
||||
completion = BooleanCompletion(false, _)
|
||||
@@ -1010,13 +1085,15 @@ private module ControlFlowGraphImpl {
|
||||
last(n.(SynchronizedStmt).getBlock(), last, completion)
|
||||
or
|
||||
// `return` statements give rise to a `Return` completion
|
||||
last = n.(ReturnStmt) and completion = ReturnCompletion()
|
||||
last.asStmt() = n.(ReturnStmt) and completion = ReturnCompletion()
|
||||
or
|
||||
// `throw` statements or throwing calls give rise to ` Throw` completion
|
||||
exists(ThrowableType tt | mayThrow(n, tt) | last = n and completion = ThrowCompletion(tt))
|
||||
exists(ThrowableType tt | mayThrow(n, tt) |
|
||||
last = n.getCFGNode() and completion = ThrowCompletion(tt)
|
||||
)
|
||||
or
|
||||
// `break` statements give rise to a `Break` completion
|
||||
exists(BreakStmt break | break = n and last = n |
|
||||
exists(BreakStmt break | break = n and last.asStmt() = n |
|
||||
completion = labelledBreakCompletion(MkLabel(break.getLabel()))
|
||||
or
|
||||
not exists(break.getLabel()) and completion = anonymousBreakCompletion()
|
||||
@@ -1031,7 +1108,7 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
// `continue` statements give rise to a `Continue` completion
|
||||
exists(ContinueStmt cont | cont = n and last = n |
|
||||
exists(ContinueStmt cont | cont = n and last.asStmt() = n |
|
||||
completion = labelledContinueCompletion(MkLabel(cont.getLabel()))
|
||||
or
|
||||
not exists(cont.getLabel()) and completion = anonymousContinueCompletion()
|
||||
@@ -1067,7 +1144,7 @@ private module ControlFlowGraphImpl {
|
||||
// the last node of the condition of the last branch in the absence of an else-branch.
|
||||
exists(WhenExpr whenexpr | whenexpr = n |
|
||||
// If we have no branches then we are the last node
|
||||
last = n and
|
||||
last.asExpr() = n and
|
||||
completion = NormalCompletion() and
|
||||
not exists(whenexpr.getBranch(_))
|
||||
or
|
||||
@@ -1117,17 +1194,19 @@ private module ControlFlowGraphImpl {
|
||||
* execution finishes with the given completion.
|
||||
*/
|
||||
cached
|
||||
ControlFlowNode succ(ControlFlowNode n, Completion completion) {
|
||||
// Callables serve as their own exit nodes.
|
||||
exists(Callable c | last(c.getBody(), n, completion) | result = c)
|
||||
Node succ(Node n, Completion completion) {
|
||||
// The successor of a callable is its exit node.
|
||||
exists(Callable c | last(c.getBody(), n, completion) |
|
||||
result.(ExitNode).getEnclosingCallable() = c
|
||||
)
|
||||
or
|
||||
// Logic expressions and conditional expressions execute in AST pre-order.
|
||||
completion = NormalCompletion() and
|
||||
(
|
||||
result = first(n.(AndLogicalExpr).getLeftOperand()) or
|
||||
result = first(n.(OrLogicalExpr).getLeftOperand()) or
|
||||
result = first(n.(LogNotExpr).getExpr()) or
|
||||
result = first(n.(ConditionalExpr).getCondition())
|
||||
result = first(n.asExpr().(AndLogicalExpr).getLeftOperand()) or
|
||||
result = first(n.asExpr().(OrLogicalExpr).getLeftOperand()) or
|
||||
result = first(n.asExpr().(LogNotExpr).getExpr()) or
|
||||
result = first(n.asExpr().(ConditionalExpr).getCondition())
|
||||
)
|
||||
or
|
||||
// If a logic expression doesn't short-circuit then control flows from its left operand to its right.
|
||||
@@ -1151,9 +1230,11 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe | ioe.isPattern() |
|
||||
last(ioe.getExpr(), n, completion) and completion = NormalCompletion() and result = ioe
|
||||
last(ioe.getExpr(), n, completion) and
|
||||
completion = NormalCompletion() and
|
||||
result.asExpr() = ioe
|
||||
or
|
||||
n = ioe and
|
||||
n.asExpr() = ioe and
|
||||
result = first(ioe.getPattern()) and
|
||||
completion = basicBooleanCompletion(true)
|
||||
)
|
||||
@@ -1164,11 +1245,11 @@ private module ControlFlowGraphImpl {
|
||||
|
|
||||
result = first(p.getChildNode(i + 1))
|
||||
or
|
||||
not exists(p.getChildNode(i + 1)) and result = p
|
||||
not exists(p.getChildNode(i + 1)) and result = p.getCFGNode()
|
||||
)
|
||||
or
|
||||
// Statements within a block execute sequentially.
|
||||
result = first(n.(BlockStmt).getStmt(0)) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(BlockStmt).getStmt(0)) and completion = NormalCompletion()
|
||||
or
|
||||
exists(BlockStmt blk, int i |
|
||||
last(blk.getStmt(i), n, completion) and
|
||||
@@ -1178,7 +1259,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// Control flows to the corresponding branch depending on the boolean completion of the condition.
|
||||
exists(IfStmt s |
|
||||
n = s and result = first(s.getCondition()) and completion = NormalCompletion()
|
||||
n.asStmt() = s and result = first(s.getCondition()) and completion = NormalCompletion()
|
||||
or
|
||||
last(s.getCondition(), n, completion) and
|
||||
completion = BooleanCompletion(true, _) and
|
||||
@@ -1190,7 +1271,7 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
// For statements:
|
||||
exists(ForStmt for, ControlFlowNode condentry |
|
||||
exists(ForStmt for, Node condentry |
|
||||
// Any part of the control flow that aims for the condition needs to hit either the condition...
|
||||
condentry = first(for.getCondition())
|
||||
or
|
||||
@@ -1198,10 +1279,10 @@ private module ControlFlowGraphImpl {
|
||||
not exists(for.getCondition()) and condentry = first(for.getStmt())
|
||||
|
|
||||
// From the entry point, which is the for statement itself, control goes to either the first init expression...
|
||||
n = for and result = first(for.getInit(0)) and completion = NormalCompletion()
|
||||
n.asStmt() = for and result = first(for.getInit(0)) and completion = NormalCompletion()
|
||||
or
|
||||
// ...or the condition if the for doesn't include init expressions.
|
||||
n = for and
|
||||
n.asStmt() = for and
|
||||
not exists(for.getAnInit()) and
|
||||
result = condentry and
|
||||
completion = NormalCompletion()
|
||||
@@ -1238,27 +1319,29 @@ private module ControlFlowGraphImpl {
|
||||
// Enhanced for statements:
|
||||
exists(EnhancedForStmt for |
|
||||
// First the expression gets evaluated...
|
||||
n = for and result = first(for.getExpr()) and completion = NormalCompletion()
|
||||
n.asStmt() = for and result = first(for.getExpr()) and completion = NormalCompletion()
|
||||
or
|
||||
// ...then the variable gets assigned...
|
||||
last(for.getExpr(), n, completion) and
|
||||
completion = NormalCompletion() and
|
||||
result = for.getVariable()
|
||||
result.asExpr() = for.getVariable()
|
||||
or
|
||||
// ...and then control goes to the body of the loop.
|
||||
n = for.getVariable() and result = first(for.getStmt()) and completion = NormalCompletion()
|
||||
n.asExpr() = for.getVariable() and
|
||||
result = first(for.getStmt()) and
|
||||
completion = NormalCompletion()
|
||||
or
|
||||
// Finally, the back edge of the loop goes to reassign the variable.
|
||||
last(for.getStmt(), n, completion) and
|
||||
continues(completion, for) and
|
||||
result = for.getVariable()
|
||||
result.asExpr() = for.getVariable()
|
||||
)
|
||||
or
|
||||
// While loops start at the condition...
|
||||
result = first(n.(WhileStmt).getCondition()) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(WhileStmt).getCondition()) and completion = NormalCompletion()
|
||||
or
|
||||
// ...and do-while loops start at the body.
|
||||
result = first(n.(DoStmt).getStmt()) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(DoStmt).getStmt()) and completion = NormalCompletion()
|
||||
or
|
||||
exists(LoopStmt loop | loop instanceof WhileStmt or loop instanceof DoStmt |
|
||||
// Control goes from the condition via a true-completion to the body...
|
||||
@@ -1282,7 +1365,7 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
// After the last resource declaration, control transfers to the body.
|
||||
exists(TryStmt try | n = try and completion = NormalCompletion() |
|
||||
exists(TryStmt try | n.asStmt() = try and completion = NormalCompletion() |
|
||||
result = first(try.getResource(0))
|
||||
or
|
||||
not exists(try.getAResource()) and result = first(try.getBlock())
|
||||
@@ -1310,7 +1393,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// Catch clauses first assign their variable and then execute their block
|
||||
exists(CatchClause cc | completion = NormalCompletion() |
|
||||
n = cc and result = first(cc.getVariable())
|
||||
n.asStmt() = cc and result = first(cc.getVariable())
|
||||
or
|
||||
last(cc.getVariable(), n, completion) and result = first(cc.getBlock())
|
||||
)
|
||||
@@ -1321,7 +1404,9 @@ private module ControlFlowGraphImpl {
|
||||
switchExpr = switch.(SwitchStmt).getExpr() or switchExpr = switch.(SwitchExpr).getExpr()
|
||||
|
|
||||
// From the entry point control is transferred first to the expression...
|
||||
n = switch and result = first(switchExpr) and completion = NormalCompletion()
|
||||
(n.asStmt() = switch or n.asExpr() = switch) and
|
||||
result = first(switchExpr) and
|
||||
completion = NormalCompletion()
|
||||
or
|
||||
// ...and then to any case up to and including the first pattern case, if any.
|
||||
last(switchExpr, n, completion) and
|
||||
@@ -1345,7 +1430,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// A pattern case that completes boolean false (type test or guard failure) continues to consider other cases:
|
||||
exists(PatternCase case | completion = BooleanCompletion(false, _) |
|
||||
last(case, n, completion) and result = getASuccessorSwitchCase(case, switch)
|
||||
last(case, n, completion) and result.asStmt() = getASuccessorSwitchCase(case, switch)
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -1358,7 +1443,7 @@ private module ControlFlowGraphImpl {
|
||||
// * Variable declarations -normal-> rule execution (when there is no guard)
|
||||
// * Guard success -true-> rule execution
|
||||
exists(PatternCase pc |
|
||||
n = pc and
|
||||
n.asStmt() = pc and
|
||||
completion = basicBooleanCompletion(true) and
|
||||
result = first(pc.getAPattern())
|
||||
or
|
||||
@@ -1375,7 +1460,7 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
or
|
||||
// Non-pattern cases have an internal edge leading to their rule body if any when the case matches.
|
||||
exists(SwitchCase case | n = case |
|
||||
exists(SwitchCase case | n.asStmt() = case |
|
||||
not case instanceof PatternCase and
|
||||
completion = NormalCompletion() and
|
||||
(
|
||||
@@ -1387,32 +1472,32 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// Yield
|
||||
exists(YieldStmt yield | completion = NormalCompletion() |
|
||||
n = yield and result = first(yield.getValue())
|
||||
n.asStmt() = yield and result = first(yield.getValue())
|
||||
)
|
||||
or
|
||||
// Synchronized statements execute their expression _before_ synchronization, so the CFG reflects that.
|
||||
exists(SynchronizedStmt synch | completion = NormalCompletion() |
|
||||
last(synch.getExpr(), n, completion) and result = synch
|
||||
last(synch.getExpr(), n, completion) and result.asStmt() = synch
|
||||
or
|
||||
n = synch and result = first(synch.getBlock())
|
||||
n.asStmt() = synch and result = first(synch.getBlock())
|
||||
)
|
||||
or
|
||||
result = first(n.(ExprStmt).getExpr()) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(ExprStmt).getExpr()) and completion = NormalCompletion()
|
||||
or
|
||||
result = first(n.(StmtExpr).getStmt()) and completion = NormalCompletion()
|
||||
result = first(n.asExpr().(StmtExpr).getStmt()) and completion = NormalCompletion()
|
||||
or
|
||||
result = first(n.(LabeledStmt).getStmt()) and completion = NormalCompletion()
|
||||
result = first(n.asStmt().(LabeledStmt).getStmt()) and completion = NormalCompletion()
|
||||
or
|
||||
// Variable declarations in a variable declaration statement are executed sequentially.
|
||||
exists(LocalVariableDeclStmt s | completion = NormalCompletion() |
|
||||
n = s and result = first(s.getVariable(1))
|
||||
n.asStmt() = s and result = first(s.getVariable(1))
|
||||
or
|
||||
exists(int i | last(s.getVariable(i), n, completion) and result = first(s.getVariable(i + 1)))
|
||||
)
|
||||
or
|
||||
// When expressions:
|
||||
exists(WhenExpr whenexpr |
|
||||
n = whenexpr and
|
||||
n.asExpr() = whenexpr and
|
||||
result = first(whenexpr.getBranch(0)) and
|
||||
completion = NormalCompletion()
|
||||
or
|
||||
@@ -1425,7 +1510,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
// When branches:
|
||||
exists(WhenBranch whenbranch |
|
||||
n = whenbranch and
|
||||
n.asStmt() = whenbranch and
|
||||
completion = NormalCompletion() and
|
||||
result = first(whenbranch.getCondition())
|
||||
or
|
||||
@@ -1463,7 +1548,7 @@ private module ControlFlowGraphImpl {
|
||||
* predicate `finallyPred`, since their completion is resumed after normal
|
||||
* completion of the `finally`.
|
||||
*/
|
||||
private Completion resumption(ControlFlowNode n) {
|
||||
private Completion resumption(Node n) {
|
||||
exists(TryStmt try | lastInFinally(try, n) and finallyPred(try, _, result))
|
||||
or
|
||||
not lastInFinally(_, n) and result = NormalCompletion()
|
||||
@@ -1474,9 +1559,7 @@ private module ControlFlowGraphImpl {
|
||||
*
|
||||
* That is, the `booleanCompletion` is the label of the edge in the CFG.
|
||||
*/
|
||||
private ControlFlowNode mainBranchSucc(ControlFlowNode n, boolean b) {
|
||||
result = succ(n, BooleanCompletion(_, b))
|
||||
}
|
||||
private Node mainBranchSucc(Node n, boolean b) { result = succ(n, BooleanCompletion(_, b)) }
|
||||
|
||||
/**
|
||||
* A true- or false-successor that is not tagged with a `booleanCompletion`.
|
||||
@@ -1487,8 +1570,8 @@ private module ControlFlowGraphImpl {
|
||||
* In the latter case, when `n` occurs as the last node in a finally block, there might be
|
||||
* multiple different such successors.
|
||||
*/
|
||||
private ControlFlowNode otherBranchSucc(ControlFlowNode n, boolean b) {
|
||||
exists(ControlFlowNode main | main = mainBranchSucc(n, b.booleanNot()) |
|
||||
private Node otherBranchSucc(Node n, boolean b) {
|
||||
exists(Node main | main = mainBranchSucc(n, b.booleanNot()) |
|
||||
result = succ(n, resumption(n)) and
|
||||
not result = main and
|
||||
(b = true or b = false)
|
||||
@@ -1497,7 +1580,7 @@ private module ControlFlowGraphImpl {
|
||||
|
||||
/** Gets a true- or false-successor of `n`. */
|
||||
cached
|
||||
ControlFlowNode branchSuccessor(ControlFlowNode n, boolean branch) {
|
||||
Node branchSuccessor(Node n, boolean branch) {
|
||||
result = mainBranchSucc(n, branch) or
|
||||
result = otherBranchSucc(n, branch)
|
||||
}
|
||||
@@ -1506,18 +1589,18 @@ private module ControlFlowGraphImpl {
|
||||
private import ControlFlowGraphImpl
|
||||
|
||||
/** A control-flow node that branches based on a condition. */
|
||||
class ConditionNode extends ControlFlowNode {
|
||||
class ConditionNode extends ControlFlow::Node {
|
||||
ConditionNode() { exists(branchSuccessor(this, _)) }
|
||||
|
||||
/** Gets a true- or false-successor of the `ConditionNode`. */
|
||||
ControlFlowNode getABranchSuccessor(boolean branch) { result = branchSuccessor(this, branch) }
|
||||
ControlFlow::Node getABranchSuccessor(boolean branch) { result = branchSuccessor(this, branch) }
|
||||
|
||||
/** Gets a true-successor of the `ConditionNode`. */
|
||||
ControlFlowNode getATrueSuccessor() { result = this.getABranchSuccessor(true) }
|
||||
ControlFlow::Node getATrueSuccessor() { result = this.getABranchSuccessor(true) }
|
||||
|
||||
/** Gets a false-successor of the `ConditionNode`. */
|
||||
ControlFlowNode getAFalseSuccessor() { result = this.getABranchSuccessor(false) }
|
||||
ControlFlow::Node getAFalseSuccessor() { result = this.getABranchSuccessor(false) }
|
||||
|
||||
/** Gets the condition of this `ConditionNode`. This is equal to the node itself. */
|
||||
ExprParent getCondition() { result = this }
|
||||
ExprParent getCondition() { result = this.asExpr() or result = this.asStmt() }
|
||||
}
|
||||
|
||||
@@ -61,10 +61,10 @@ class Expr extends ExprParent, @expr {
|
||||
Expr getAChildExpr() { exprs(result, _, _, this, _) }
|
||||
|
||||
/** Gets the basic block in which this expression occurs, if any. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
BasicBlock getBasicBlock() { result.getANode().asExpr() = this }
|
||||
|
||||
/** Gets the `ControlFlowNode` corresponding to this expression. */
|
||||
ControlFlowNode getControlFlowNode() { result = this }
|
||||
ControlFlowNode getControlFlowNode() { result.asExpr() = this }
|
||||
|
||||
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
||||
string getHalsteadID() { result = this.toString() }
|
||||
|
||||
@@ -45,10 +45,10 @@ class Stmt extends StmtParent, ExprParent, @stmt {
|
||||
Stmt getAChild() { result.getParent() = this }
|
||||
|
||||
/** Gets the basic block in which this statement occurs. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
BasicBlock getBasicBlock() { result.getANode().asStmt() = this }
|
||||
|
||||
/** Gets the `ControlFlowNode` corresponding to this statement. */
|
||||
ControlFlowNode getControlFlowNode() { result = this }
|
||||
ControlFlowNode getControlFlowNode() { result.asStmt() = this }
|
||||
|
||||
/** Cast this statement to a class that provides access to metrics information. */
|
||||
MetricStmt getMetrics() { result = this }
|
||||
|
||||
@@ -66,3 +66,8 @@ class BasicBlock extends ControlFlowNode {
|
||||
/** Holds if this basic block post-dominates `node`. (This is reflexive.) */
|
||||
predicate bbPostDominates(BasicBlock node) { bbPostDominates(this, node) }
|
||||
}
|
||||
|
||||
/** A basic block that ends in an exit node. */
|
||||
class ExitBlock extends BasicBlock {
|
||||
ExitBlock() { this.getLastNode() instanceof ControlFlow::ExitNode }
|
||||
}
|
||||
|
||||
@@ -9,13 +9,15 @@ import java
|
||||
*/
|
||||
|
||||
/** Entry points for control-flow. */
|
||||
private predicate flowEntry(Stmt entry) {
|
||||
exists(Callable c | entry = c.getBody())
|
||||
or
|
||||
// This disjunct is technically superfluous, but safeguards against extractor problems.
|
||||
entry instanceof BlockStmt and
|
||||
not exists(entry.getEnclosingCallable()) and
|
||||
not entry.getParent() instanceof Stmt
|
||||
private predicate flowEntry(BasicBlock entry) {
|
||||
exists(Stmt entrystmt | entrystmt = entry.getFirstNode().asStmt() |
|
||||
exists(Callable c | entrystmt = c.getBody())
|
||||
or
|
||||
// This disjunct is technically superfluous, but safeguards against extractor problems.
|
||||
entrystmt instanceof BlockStmt and
|
||||
not exists(entry.getEnclosingCallable()) and
|
||||
not entrystmt.getParent() instanceof Stmt
|
||||
)
|
||||
}
|
||||
|
||||
/** The successor relation for basic blocks. */
|
||||
@@ -31,11 +33,8 @@ predicate hasDominanceInformation(BasicBlock bb) {
|
||||
exists(BasicBlock entry | flowEntry(entry) and bbSucc*(entry, bb))
|
||||
}
|
||||
|
||||
/** Exit points for control-flow. */
|
||||
private predicate flowExit(Callable exit) { exists(ControlFlowNode s | s.getASuccessor() = exit) }
|
||||
|
||||
/** Exit points for basic-block control-flow. */
|
||||
private predicate bbSink(BasicBlock exit) { flowExit(exit.getLastNode()) }
|
||||
private predicate bbSink(BasicBlock exit) { exit.getLastNode() instanceof ControlFlow::ExitNode }
|
||||
|
||||
/** Reversed `bbSucc`. */
|
||||
private predicate bbPred(BasicBlock post, BasicBlock pre) { post = pre.getABBSuccessor() }
|
||||
|
||||
@@ -113,7 +113,7 @@ private PatternCase getClosestPrecedingPatternCase(SwitchCase case) {
|
||||
private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pred) {
|
||||
pred = sc.getControlFlowNode().getAPredecessor() and
|
||||
(
|
||||
pred.(Expr).getParent*() = sc.getSelectorExpr()
|
||||
pred.asExpr().getParent*() = sc.getSelectorExpr()
|
||||
or
|
||||
// Ambiguous: in the case of `case String _ when x: case "SomeConstant":`, the guard `x`
|
||||
// passing edge will fall through into the constant case, and the guard failing edge
|
||||
@@ -122,7 +122,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre
|
||||
exists(PatternCase previousPatternCase |
|
||||
previousPatternCase = getClosestPrecedingPatternCase(sc)
|
||||
|
|
||||
pred.(Expr).getParent*() = previousPatternCase.getGuard() and
|
||||
pred.asExpr().getParent*() = previousPatternCase.getGuard() and
|
||||
// Check there is any statement in between the previous pattern case and this one,
|
||||
// or the case is a rule, so there is no chance of a fall-through.
|
||||
(
|
||||
@@ -133,7 +133,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre
|
||||
or
|
||||
// Unambigious: on the test-passing edge there must be at least one intervening
|
||||
// declaration node, including anonymous `_` declarations.
|
||||
pred = getClosestPrecedingPatternCase(sc)
|
||||
pred.asStmt() = getClosestPrecedingPatternCase(sc)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ abstract class ActionConfiguration extends string {
|
||||
private BasicBlock actionBlock(ActionConfiguration conf) {
|
||||
exists(ControlFlowNode node | result = node.getBasicBlock() |
|
||||
conf.isAction(node) or
|
||||
callAlwaysPerformsAction(node, conf)
|
||||
callAlwaysPerformsAction(node.asCall(), conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -45,17 +45,17 @@ private predicate callAlwaysPerformsAction(Call call, ActionConfiguration conf)
|
||||
|
||||
/** Holds if an action dominates the exit of the callable. */
|
||||
private predicate actionDominatesExit(Callable callable, ActionConfiguration conf) {
|
||||
exists(BasicBlock exit |
|
||||
exit.getLastNode() = callable and
|
||||
exists(ExitBlock exit |
|
||||
exit.getEnclosingCallable() = callable and
|
||||
actionBlock(conf).bbDominates(exit)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a `BasicBlock` that contains an action that does not dominate the exit. */
|
||||
private BasicBlock nonDominatingActionBlock(ActionConfiguration conf) {
|
||||
exists(BasicBlock exit |
|
||||
exists(ExitBlock exit |
|
||||
result = actionBlock(conf) and
|
||||
exit.getLastNode() = result.getEnclosingCallable() and
|
||||
exit.getEnclosingCallable() = result.getEnclosingCallable() and
|
||||
not result.bbDominates(exit)
|
||||
)
|
||||
}
|
||||
@@ -80,8 +80,8 @@ private predicate postActionBlock(BasicBlock bb, ActionConfiguration conf) {
|
||||
private predicate callableAlwaysPerformsAction(Callable callable, ActionConfiguration conf) {
|
||||
actionDominatesExit(callable, conf)
|
||||
or
|
||||
exists(BasicBlock exit |
|
||||
exit.getLastNode() = callable and
|
||||
exists(ExitBlock exit |
|
||||
exit.getEnclosingCallable() = callable and
|
||||
postActionBlock(exit, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -207,14 +207,12 @@ class UnreachableBasicBlock extends BasicBlock {
|
||||
conditionBlock.controls(this, constant.booleanNot())
|
||||
)
|
||||
or
|
||||
// This block is not reachable in the CFG, and is not a callable, a body of a callable, an
|
||||
// expression in an annotation, an expression in an assert statement, or a catch clause.
|
||||
// This block is not reachable in the CFG, and is not the entrypoint in a callable, an
|
||||
// expression in an assert statement, or a catch clause.
|
||||
forall(BasicBlock bb | bb = this.getABBPredecessor() | bb instanceof UnreachableBasicBlock) and
|
||||
not exists(Callable c | c.getBody() = this) and
|
||||
not this instanceof Callable and
|
||||
not exists(Annotation a | a.getAChildExpr*() = this) and
|
||||
not this.(Expr).getEnclosingStmt() instanceof AssertStmt and
|
||||
not this instanceof CatchClause
|
||||
not exists(Callable c | c.getBody().getControlFlowNode() = this.getFirstNode()) and
|
||||
not this.getFirstNode().asExpr().getEnclosingStmt() instanceof AssertStmt and
|
||||
not this.getFirstNode().asStmt() instanceof CatchClause
|
||||
or
|
||||
// Switch statements with a constant comparison expression may have unreachable cases.
|
||||
exists(ConstSwitchStmt constSwitchStmt, BasicBlock unreachableCaseBlock |
|
||||
|
||||
@@ -227,12 +227,14 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
/** Gets the control flow node associated with this instance access. */
|
||||
ControlFlowNode getCfgNode() {
|
||||
exists(ExprParent e | e = this.getAssociatedExprOrStmt() |
|
||||
e instanceof Call and result = e
|
||||
result.asCall() = e
|
||||
or
|
||||
e instanceof InstanceAccess and result = e
|
||||
e.(InstanceAccess).getControlFlowNode() = result
|
||||
or
|
||||
exists(FieldAccess fa | fa = e |
|
||||
if fa instanceof VarRead then fa = result else result.(AssignExpr).getDest() = fa
|
||||
if fa instanceof VarRead
|
||||
then fa.getControlFlowNode() = result
|
||||
else result.asExpr().(AssignExpr).getDest() = fa
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -130,8 +130,8 @@ predicate dereference(Expr e) {
|
||||
* The `VarAccess` is included for nicer error reporting.
|
||||
*/
|
||||
private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
dereference(result) and
|
||||
result = sameValue(v, va)
|
||||
dereference(result.asExpr()) and
|
||||
result.asExpr() = sameValue(v, va)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,16 +141,16 @@ private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
private ControlFlowNode ensureNotNull(SsaVariable v) {
|
||||
result = varDereference(v, _)
|
||||
or
|
||||
result.(AssertStmt).getExpr() = nullGuard(v, true, false)
|
||||
result.asStmt().(AssertStmt).getExpr() = nullGuard(v, true, false)
|
||||
or
|
||||
exists(AssertTrueMethod m | result = m.getACheck(nullGuard(v, true, false)))
|
||||
exists(AssertTrueMethod m | result.asCall() = m.getACheck(nullGuard(v, true, false)))
|
||||
or
|
||||
exists(AssertFalseMethod m | result = m.getACheck(nullGuard(v, false, false)))
|
||||
exists(AssertFalseMethod m | result.asCall() = m.getACheck(nullGuard(v, false, false)))
|
||||
or
|
||||
exists(AssertNotNullMethod m | result = m.getACheck(v.getAUse()))
|
||||
exists(AssertNotNullMethod m | result.asCall() = m.getACheck(v.getAUse()))
|
||||
or
|
||||
exists(AssertThatMethod m, MethodCall ma |
|
||||
result = m.getACheck(v.getAUse()) and ma.getControlFlowNode() = result
|
||||
result.asCall() = m.getACheck(v.getAUse()) and ma.getControlFlowNode() = result
|
||||
|
|
||||
ma.getAnArgument().(MethodCall).getMethod().getName() = "notNullValue"
|
||||
)
|
||||
@@ -279,10 +279,10 @@ private predicate enhancedForEarlyExit(EnhancedForStmt for, ControlFlowNode n1,
|
||||
exists(Expr forExpr |
|
||||
n1.getANormalSuccessor() = n2 and
|
||||
for.getExpr() = forExpr and
|
||||
forExpr.getAChildExpr*() = n1 and
|
||||
not forExpr.getAChildExpr*() = n2 and
|
||||
n1.getANormalSuccessor() = for.getVariable() and
|
||||
not n2 = for.getVariable()
|
||||
forExpr.getAChildExpr*() = n1.asExpr() and
|
||||
not forExpr.getAChildExpr*() = n2.asExpr() and
|
||||
n1.getANormalSuccessor().asExpr() = for.getVariable() and
|
||||
not n2.asExpr() = for.getVariable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ private predicate nullVarStep(
|
||||
not impossibleEdge(mid, bb) and
|
||||
not exists(boolean branch | nullGuard(midssa, branch, false).hasBranchEdge(mid, bb, branch)) and
|
||||
not (leavingFinally(mid, bb, true) and midstoredcompletion = true) and
|
||||
if bb.getFirstNode() = any(TryStmt try | | try.getFinally())
|
||||
if bb.getFirstNode().asStmt() = any(TryStmt try | | try.getFinally())
|
||||
then
|
||||
if bb.getFirstNode() = mid.getLastNode().getANormalSuccessor()
|
||||
then storedcompletion = false
|
||||
|
||||
@@ -211,9 +211,11 @@ module Sem implements Semantic {
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getABBSuccessor() }
|
||||
|
||||
private predicate id(BasicBlock x, BasicBlock y) { x = y }
|
||||
private predicate id(ExprParent x, ExprParent y) { x = y }
|
||||
|
||||
private predicate idOf(BasicBlock x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
private predicate idOfAst(ExprParent x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
|
||||
private predicate idOf(BasicBlock x, int y) { idOfAst(x.getAstNode(), y) }
|
||||
|
||||
int getBlockId1(BasicBlock bb) { idOf(bb, result) }
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ private module SsaImpl {
|
||||
/** Holds if `n` must update the locally tracked variable `v`. */
|
||||
cached
|
||||
predicate certainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(VariableUpdate a | a = n | getDestVar(a) = v) and
|
||||
exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and
|
||||
b.getNode(i) = n and
|
||||
hasDominanceInformation(b)
|
||||
or
|
||||
@@ -237,8 +237,8 @@ private module SsaImpl {
|
||||
|
||||
/** Gets the definition point of a nested class in the parent scope. */
|
||||
private ControlFlowNode parentDef(NestedClass nc) {
|
||||
nc.(AnonymousClass).getClassInstanceExpr() = result or
|
||||
nc.(LocalClass).getLocalTypeDeclStmt() = result
|
||||
nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or
|
||||
nc.(LocalClass).getLocalTypeDeclStmt().getControlFlowNode() = result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +276,7 @@ private module SsaImpl {
|
||||
|
||||
/** Holds if `VarAccess` `use` of `v` occurs in `b` at index `i`. */
|
||||
private predicate variableUse(TrackedVar v, VarRead use, BasicBlock b, int i) {
|
||||
v.getAnAccess() = use and b.getNode(i) = use
|
||||
v.getAnAccess() = use and b.getNode(i) = use.getControlFlowNode()
|
||||
}
|
||||
|
||||
/** Holds if the value of `v` is captured in `b` at index `i`. */
|
||||
@@ -423,7 +423,7 @@ private module SsaImpl {
|
||||
* `f` has an update somewhere.
|
||||
*/
|
||||
private predicate updateCandidate(TrackedField f, Call call, BasicBlock b, int i) {
|
||||
b.getNode(i) = call and
|
||||
b.getNode(i).asCall() = call and
|
||||
call.getEnclosingCallable() = f.getEnclosingCallable() and
|
||||
relevantFieldUpdate(_, f.getField(), _)
|
||||
}
|
||||
@@ -550,7 +550,7 @@ private module SsaImpl {
|
||||
/** Holds if `n` might update the locally tracked variable `v`. */
|
||||
cached
|
||||
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(Call c | c = n | updatesNamedField(c, v, _)) and
|
||||
exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and
|
||||
b.getNode(i) = n and
|
||||
hasDominanceInformation(b)
|
||||
or
|
||||
@@ -574,12 +574,16 @@ private module SsaImpl {
|
||||
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
|
||||
cached
|
||||
predicate hasEntryDef(TrackedVar v, BasicBlock b) {
|
||||
exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b |
|
||||
exists(LocalScopeVariable l, Callable c |
|
||||
v = TLocalVar(c, l) and c.getBody().getControlFlowNode() = b
|
||||
|
|
||||
l instanceof Parameter or
|
||||
l.getCallable() != c
|
||||
)
|
||||
or
|
||||
v instanceof SsaSourceField and v.getEnclosingCallable().getBody() = b and liveAtEntry(v, b)
|
||||
v instanceof SsaSourceField and
|
||||
v.getEnclosingCallable().getBody().getControlFlowNode() = b and
|
||||
liveAtEntry(v, b)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -882,7 +886,7 @@ private newtype TSsaVariable =
|
||||
} or
|
||||
TSsaEntryDef(TrackedVar v, BasicBlock b) { hasEntryDef(v, b) } or
|
||||
TSsaUntracked(SsaSourceField nf, ControlFlowNode n) {
|
||||
n = nf.getAnAccess().(FieldRead) and not trackField(nf)
|
||||
n = nf.getAnAccess().(FieldRead).getControlFlowNode() and not trackField(nf)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -940,7 +944,7 @@ class SsaVariable extends TSsaVariable {
|
||||
/** Gets an access of this SSA variable. */
|
||||
VarRead getAUse() {
|
||||
ssaDefReachesUse(_, this, result) or
|
||||
this = TSsaUntracked(_, result)
|
||||
this = TSsaUntracked(_, result.getControlFlowNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -954,7 +958,7 @@ class SsaVariable extends TSsaVariable {
|
||||
*/
|
||||
VarRead getAFirstUse() {
|
||||
firstUse(this, result) or
|
||||
this = TSsaUntracked(_, result)
|
||||
this = TSsaUntracked(_, result.getControlFlowNode())
|
||||
}
|
||||
|
||||
/** Holds if this SSA variable is live at the end of `b`. */
|
||||
@@ -990,7 +994,7 @@ class SsaUpdate extends SsaVariable {
|
||||
class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate {
|
||||
SsaExplicitUpdate() {
|
||||
exists(VariableUpdate upd |
|
||||
upd = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable()
|
||||
upd.getControlFlowNode() = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -998,7 +1002,8 @@ class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate {
|
||||
|
||||
/** Gets the `VariableUpdate` defining the SSA variable. */
|
||||
VariableUpdate getDefiningExpr() {
|
||||
result = this.getCfgNode() and getDestVar(result) = this.getSourceVariable()
|
||||
result.getControlFlowNode() = this.getCfgNode() and
|
||||
getDestVar(result) = this.getSourceVariable()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1038,7 +1043,7 @@ class SsaImplicitUpdate extends SsaUpdate {
|
||||
exists(SsaSourceField f, Callable setter |
|
||||
f = this.getSourceVariable() and
|
||||
relevantFieldUpdate(setter, f.getField(), result) and
|
||||
updatesNamedField(this.getCfgNode(), f, setter)
|
||||
updatesNamedField(this.getCfgNode().asCall(), f, setter)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1086,7 +1091,7 @@ class SsaImplicitInit extends SsaVariable, TSsaEntryDef {
|
||||
*/
|
||||
predicate isParameterDefinition(Parameter p) {
|
||||
this.getSourceVariable() = TLocalVar(p.getCallable(), p) and
|
||||
p.getCallable().getBody() = this.getCfgNode()
|
||||
p.getCallable().getBody().getControlFlowNode() = this.getCfgNode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -252,8 +252,8 @@ private module Input implements TypeFlowInput<Location> {
|
||||
downcastSuccessorAux(pragma[only_bind_into](cast), v, t, t1, t2) and
|
||||
t1.getASourceSupertype+() = t2 and
|
||||
va = v.getAUse() and
|
||||
dominates(cast, va) and
|
||||
dominates(cast.(ControlFlowNode).getANormalSuccessor(), va)
|
||||
dominates(cast.getControlFlowNode(), va.getControlFlowNode()) and
|
||||
dominates(cast.getControlFlowNode().getANormalSuccessor(), va.getControlFlowNode())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -73,15 +73,15 @@ private module SsaImpl {
|
||||
/** Holds if `n` updates the local variable `v`. */
|
||||
cached
|
||||
predicate variableUpdate(BaseSsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(VariableUpdate a | a = n | getDestVar(a) = v) and
|
||||
exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and
|
||||
b.getNode(i) = n and
|
||||
hasDominanceInformation(b)
|
||||
}
|
||||
|
||||
/** Gets the definition point of a nested class in the parent scope. */
|
||||
private ControlFlowNode parentDef(NestedClass nc) {
|
||||
nc.(AnonymousClass).getClassInstanceExpr() = result or
|
||||
nc.(LocalClass).getLocalTypeDeclStmt() = result
|
||||
nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or
|
||||
nc.(LocalClass).getLocalTypeDeclStmt().getControlFlowNode() = result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,7 +121,7 @@ private module SsaImpl {
|
||||
|
||||
/** Holds if `VarAccess` `use` of `v` occurs in `b` at index `i`. */
|
||||
private predicate variableUse(BaseSsaSourceVariable v, VarRead use, BasicBlock b, int i) {
|
||||
v.getAnAccess() = use and b.getNode(i) = use
|
||||
v.getAnAccess() = use and b.getNode(i) = use.getControlFlowNode()
|
||||
}
|
||||
|
||||
/** Holds if the value of `v` is captured in `b` at index `i`. */
|
||||
@@ -164,7 +164,9 @@ private module SsaImpl {
|
||||
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
|
||||
cached
|
||||
predicate hasEntryDef(BaseSsaSourceVariable v, BasicBlock b) {
|
||||
exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b |
|
||||
exists(LocalScopeVariable l, Callable c |
|
||||
v = TLocalVar(c, l) and c.getBody().getControlFlowNode() = b
|
||||
|
|
||||
l instanceof Parameter or
|
||||
l.getCallable() != c
|
||||
)
|
||||
@@ -537,7 +539,7 @@ class BaseSsaVariable extends TBaseSsaVariable {
|
||||
class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate {
|
||||
BaseSsaUpdate() {
|
||||
exists(VariableUpdate upd |
|
||||
upd = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable()
|
||||
upd.getControlFlowNode() = this.getCfgNode() and getDestVar(upd) = this.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -545,7 +547,8 @@ class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate {
|
||||
|
||||
/** Gets the `VariableUpdate` defining the SSA variable. */
|
||||
VariableUpdate getDefiningExpr() {
|
||||
result = this.getCfgNode() and getDestVar(result) = this.getSourceVariable()
|
||||
result.getControlFlowNode() = this.getCfgNode() and
|
||||
getDestVar(result) = this.getSourceVariable()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,7 +569,7 @@ class BaseSsaImplicitInit extends BaseSsaVariable, TSsaEntryDef {
|
||||
*/
|
||||
predicate isParameterDefinition(Parameter p) {
|
||||
this.getSourceVariable() = TLocalVar(p.getCallable(), p) and
|
||||
p.getCallable().getBody() = this.getCfgNode()
|
||||
p.getCallable().getBody().getControlFlowNode() = this.getCfgNode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ private module CaptureInput implements VariableCapture::InputSig<Location> {
|
||||
|
||||
Location getLocation() { result = super.getLocation() }
|
||||
|
||||
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.(J::BasicBlock).getNode(i) }
|
||||
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.(J::BasicBlock).getNode(i).asExpr() }
|
||||
}
|
||||
|
||||
class VariableWrite extends Expr instanceof VariableUpdate {
|
||||
|
||||
@@ -17,9 +17,11 @@ import DataFlowNodes::Public
|
||||
|
||||
/** Holds if `n` is an access to an unqualified `this` at `cfgnode`. */
|
||||
private predicate thisAccess(Node n, ControlFlowNode cfgnode) {
|
||||
n.(InstanceParameterNode).getCallable().getBody() = cfgnode
|
||||
n.(InstanceParameterNode).getCallable().getBody() = cfgnode.asStmt()
|
||||
or
|
||||
exists(InstanceAccess ia | ia = n.asExpr() and ia = cfgnode and ia.isOwnInstanceAccess())
|
||||
exists(InstanceAccess ia |
|
||||
ia = n.asExpr() and ia.getControlFlowNode() = cfgnode and ia.isOwnInstanceAccess()
|
||||
)
|
||||
or
|
||||
n.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getCfgNode() = cfgnode
|
||||
}
|
||||
|
||||
@@ -133,5 +133,5 @@ module Private {
|
||||
|
||||
predicate ssaUpdateStep = RU::ssaUpdateStep/3;
|
||||
|
||||
Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode() }
|
||||
Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode().asExpr() }
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ private module Impl {
|
||||
|
||||
Field getField(FieldAccess fa) { result = fa.getField() }
|
||||
|
||||
Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode() }
|
||||
Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode().asExpr() }
|
||||
|
||||
Guard getComparisonGuard(ComparisonExpr ce) { result = ce }
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@ class BasicBlock = BB::BasicBlock;
|
||||
/** Gets a basic block in which SSA variable `v` is read. */
|
||||
BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getAUse().getBasicBlock() }
|
||||
|
||||
private predicate id(BasicBlock x, BasicBlock y) { x = y }
|
||||
private predicate id(BB::ExprParent x, BB::ExprParent y) { x = y }
|
||||
|
||||
private predicate idOf(BasicBlock x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
private predicate idOfAst(BB::ExprParent x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
|
||||
private predicate idOf(BasicBlock x, int y) { idOfAst(x.getAstNode(), y) }
|
||||
|
||||
private int getId(BasicBlock bb) { idOf(bb, result) }
|
||||
|
||||
|
||||
@@ -109,12 +109,12 @@ predicate assertFail(BasicBlock bb, ControlFlowNode n) {
|
||||
bb = n.getBasicBlock() and
|
||||
(
|
||||
exists(AssertTrueMethod m |
|
||||
n = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = false))
|
||||
n.asExpr() = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = false))
|
||||
) or
|
||||
exists(AssertFalseMethod m |
|
||||
n = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = true))
|
||||
n.asExpr() = m.getACheck(any(BooleanLiteral b | b.getBooleanValue() = true))
|
||||
) or
|
||||
exists(AssertFailMethod m | n = m.getACheck()) or
|
||||
n.(AssertStmt).getExpr().(BooleanLiteral).getBooleanValue() = false
|
||||
exists(AssertFailMethod m | n.asExpr() = m.getACheck()) or
|
||||
n.asStmt().(AssertStmt).getExpr().(BooleanLiteral).getBooleanValue() = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -73,14 +73,14 @@ class MetricCallable extends Callable {
|
||||
// so there should be a branching point for each non-default switch
|
||||
// case (ignoring those that just fall through to the next case).
|
||||
private predicate branchingSwitchCase(ConstCase sc) {
|
||||
not sc.(ControlFlowNode).getASuccessor() instanceof SwitchCase and
|
||||
not sc.getControlFlowNode().getASuccessor().asStmt() instanceof SwitchCase and
|
||||
not defaultFallThrough(sc)
|
||||
}
|
||||
|
||||
private predicate defaultFallThrough(ConstCase sc) {
|
||||
exists(DefaultCase default | default.(ControlFlowNode).getASuccessor() = sc)
|
||||
exists(DefaultCase default | default.getControlFlowNode().getASuccessor().asStmt() = sc)
|
||||
or
|
||||
defaultFallThrough(sc.(ControlFlowNode).getAPredecessor())
|
||||
defaultFallThrough(sc.getControlFlowNode().getAPredecessor().asStmt())
|
||||
}
|
||||
|
||||
/** Holds if `stmt` is a branching statement used for the computation of cyclomatic complexity. */
|
||||
|
||||
@@ -29,15 +29,19 @@ private module ValidationMethod<DataFlow::guardChecksSig/3 validationGuard> {
|
||||
*/
|
||||
private predicate validationMethod(Method m, int arg) {
|
||||
exists(
|
||||
Guard g, SsaImplicitInit var, ControlFlowNode exit, ControlFlowNode normexit, boolean branch
|
||||
Guard g, SsaImplicitInit var, ControlFlow::ExitNode exit, ControlFlowNode normexit,
|
||||
boolean branch
|
||||
|
|
||||
validationGuard(g, var.getAUse(), branch) and
|
||||
var.isParameterDefinition(m.getParameter(arg)) and
|
||||
exit = m and
|
||||
exit.getEnclosingCallable() = m and
|
||||
normexit.getANormalSuccessor() = exit and
|
||||
1 = strictcount(ControlFlowNode n | n.getANormalSuccessor() = exit)
|
||||
|
|
||||
g.(ConditionNode).getABranchSuccessor(branch) = exit or
|
||||
exists(ConditionNode conditionNode |
|
||||
g = conditionNode.getCondition() and conditionNode.getABranchSuccessor(branch) = exit
|
||||
)
|
||||
or
|
||||
g.controls(normexit.getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ private predicate validatedAccess(VarAccess va) {
|
||||
bb.getNode(i + 1) = node.getANormalSuccessor()
|
||||
|
|
||||
bb.bbStrictlyDominates(va.getBasicBlock()) or
|
||||
bb.getNode(any(int j | j > i)) = va
|
||||
bb.getNode(any(int j | j > i)).asExpr() = va
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user