Java: IPA the CFG

This commit is contained in:
Owen Mansel-Chan
2024-11-12 17:01:51 +00:00
parent 0f5786e4e6
commit 0f3dd6d8f1
79 changed files with 455 additions and 348 deletions

View File

@@ -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() }
}

View File

@@ -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() }

View File

@@ -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 }

View File

@@ -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 }
}

View File

@@ -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() }

View File

@@ -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)
)
}

View File

@@ -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)
)
}

View File

@@ -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 |

View File

@@ -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
)
)
}

View File

@@ -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

View File

@@ -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) }

View File

@@ -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()
}
}

View File

@@ -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())
)
}

View File

@@ -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()
}
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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() }
}

View File

@@ -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 }
}

View File

@@ -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) }

View File

@@ -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
)
}

View File

@@ -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. */

View File

@@ -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)
)
}

View File

@@ -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
)
)
)