11/** Provides the class `ControlFlowElement`. */
22
3- import csharp
3+ import csharp
44private import semmle.code.csharp.ExprOrStmtParent
5+ private import ControlFlow
6+ private import ControlFlow:: BasicBlocks
7+ private import SuccessorTypes
58
69/**
710 * A program element that can possess control flow. That is, either a statement or
@@ -25,21 +28,21 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
2528 * several `ControlFlow::Node`s, for example to represent the continuation
2629 * flow in a `try/catch/finally` construction.
2730 */
28- ControlFlow :: Node getAControlFlowNode ( ) {
31+ Node getAControlFlowNode ( ) {
2932 result .getElement ( ) = this
3033 }
3134
3235 /**
3336 * Gets a first control flow node executed within this element.
3437 */
35- ControlFlow :: Node getAControlFlowEntryNode ( ) {
38+ Node getAControlFlowEntryNode ( ) {
3639 result = ControlFlowGraph:: Internal:: getAControlFlowEntryNode ( this ) .getAControlFlowNode ( )
3740 }
3841
3942 /**
4043 * Gets a potential last control flow node executed within this element.
4144 */
42- ControlFlow :: Node getAControlFlowExitNode ( ) {
45+ Node getAControlFlowExitNode ( ) {
4346 result = ControlFlowGraph:: Internal:: getAControlFlowExitNode ( this ) .getAControlFlowNode ( )
4447 }
4548
@@ -59,7 +62,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
5962 /** Gets an element that is reachable from this element. */
6063 ControlFlowElement getAReachableElement ( ) {
6164 // Reachable in same basic block
62- exists ( ControlFlow :: BasicBlock bb , int i , int j |
65+ exists ( BasicBlock bb , int i , int j |
6366 bb .getNode ( i ) = getAControlFlowNode ( ) and
6467 bb .getNode ( j ) = result .getAControlFlowNode ( ) and
6568 i < j
@@ -68,4 +71,91 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
6871 // Reachable in different basic blocks
6972 getAControlFlowNode ( ) .getBasicBlock ( ) .getASuccessor + ( ) .getANode ( ) = result .getAControlFlowNode ( )
7073 }
74+
75+ /**
76+ * Holds if basic block `succ` is immediately controlled by this control flow
77+ * element with conditional value `s`. That is, `succ` can only be reached from
78+ * the callable entry point by going via the `s` edge out of *some* basic block
79+ * `pred` ending with this element, and `pred` is an immediate predecessor
80+ * of `succ`.
81+ *
82+ * This predicate is different from
83+ * `this.getAControlFlowNode().getBasicBlock().(ConditionBlock).immediatelyControls(succ, s)`
84+ * in that it takes control flow splitting into account.
85+ */
86+ pragma [ nomagic]
87+ private predicate immediatelyControls ( BasicBlock succ , ConditionalSuccessor s ) {
88+ exists ( ConditionBlock cb |
89+ cb .getLastNode ( ) = this .getAControlFlowNode ( ) |
90+ succ = cb .getASuccessorByType ( s ) and
91+ forall ( BasicBlock pred , SuccessorType t |
92+ pred = succ .getAPredecessorByType ( t ) and pred != cb |
93+ succ .dominates ( pred )
94+ or
95+ // `pred` might be another split of `cfe`
96+ pred .getLastNode ( ) .getElement ( ) = this and
97+ pred .getASuccessorByType ( t ) = succ and
98+ t = s
99+ )
100+ )
101+ }
102+
103+ pragma [ nomagic]
104+ private JoinBlockPredecessor getAPossiblyControlledPredecessor ( JoinBlock controlled , ConditionalSuccessor s ) {
105+ exists ( BasicBlock mid |
106+ this .immediatelyControls ( mid , s ) |
107+ result = mid .getASuccessor * ( )
108+ ) and
109+ result .getASuccessor ( ) = controlled
110+ }
111+
112+ pragma [ nomagic]
113+ private predicate isPossiblyControlledJoinBlock ( JoinBlock controlled , ConditionalSuccessor s ) {
114+ exists ( this .getAPossiblyControlledPredecessor ( controlled , s ) ) and
115+ forall ( BasicBlock pred |
116+ pred = controlled .getAPredecessor ( ) |
117+ pred = this .getAPossiblyControlledPredecessor ( controlled , s )
118+ )
119+ }
120+
121+ /**
122+ * Holds if basic block `controlled` is controlled by this control flow element
123+ * with conditional value `s`. That is, `controlled` can only be reached from
124+ * the callable entry point by going via the `s` edge out of *some* basic block
125+ * ending with this element.
126+ *
127+ * This predicate is different from
128+ * `this.getAControlFlowNode().getBasicBlock().(ConditionBlock).controls(controlled, s)`
129+ * in that it takes control flow splitting into account.
130+ */
131+ cached
132+ predicate controlsBlock ( BasicBlock controlled , ConditionalSuccessor s ) {
133+ this .immediatelyControls ( controlled , s )
134+ or
135+ if controlled instanceof JoinBlock then
136+ this .isPossiblyControlledJoinBlock ( controlled , s ) and
137+ forall ( BasicBlock pred |
138+ pred = this .getAPossiblyControlledPredecessor ( controlled , s ) |
139+ this .controlsBlock ( pred , s )
140+ )
141+ else
142+ this .controlsBlock ( controlled .getAPredecessor ( ) , s )
143+ }
144+
145+ /**
146+ * Holds if control flow element `controlled` is controlled by this control flow
147+ * element with conditional value `s`. That is, `controlled` can only be reached
148+ * from the callable entry point by going via the `s` edge out of this element.
149+ *
150+ * This predicate is different from
151+ * `this.getAControlFlowNode().getBasicBlock().(ConditionBlock).controls(controlled.getAControlFlowNode().getBasicBlock(), s)`
152+ * in that it takes control flow splitting into account.
153+ */
154+ pragma [ inline] // potentially very large predicate, so must be inlined
155+ predicate controlsElement ( ControlFlowElement controlled , ConditionalSuccessor s ) {
156+ forex ( BasicBlock bb |
157+ bb = controlled .getAControlFlowNode ( ) .getBasicBlock ( ) |
158+ this .controlsBlock ( bb , s )
159+ )
160+ }
71161}
0 commit comments