@@ -6,26 +6,158 @@ import csharp
66 * scope variables.
77 */
88module PreSsa {
9- import pressa.SsaImplSpecific
10- private import pressa.SsaImplCommon as SsaImpl
9+ private import AssignableDefinitions
10+ private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
11+ private import semmle.code.csharp.controlflow.internal.PreBasicBlocks as PreBasicBlocks
12+ private import semmle.code.csharp.dataflow.internal.SsaImplCommon as SsaImplCommon
13+
14+ private predicate definitionAt (
15+ AssignableDefinition def , SsaInput:: BasicBlock bb , int i , SsaInput:: SourceVariable v
16+ ) {
17+ bb .getElement ( i ) = def .getExpr ( ) and
18+ v = def .getTarget ( ) and
19+ // In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
20+ not exists ( TupleAssignmentDefinition first , TupleAssignmentDefinition second | first = def |
21+ second .getAssignment ( ) = first .getAssignment ( ) and
22+ second .getEvaluationOrder ( ) > first .getEvaluationOrder ( ) and
23+ second .getTarget ( ) = v
24+ )
25+ or
26+ def .( ImplicitParameterDefinition ) .getParameter ( ) = v and
27+ exists ( Callable c | v = c .getAParameter ( ) |
28+ scopeFirst ( c , bb ) and
29+ i = - 1
30+ )
31+ }
32+
33+ predicate implicitEntryDef ( Callable c , SsaInput:: BasicBlock bb , SsaInput:: SourceVariable v ) {
34+ not v instanceof LocalScopeVariable and
35+ c = v .getACallable ( ) and
36+ scopeFirst ( c , bb )
37+ }
38+
39+ module SsaInput implements SsaImplCommon:: InputSig {
40+ class BasicBlock = PreBasicBlocks:: PreBasicBlock ;
41+
42+ BasicBlock getImmediateBasicBlockDominator ( BasicBlock bb ) { result .immediatelyDominates ( bb ) }
43+
44+ BasicBlock getABasicBlockSuccessor ( BasicBlock bb ) { result = bb .getASuccessor ( ) }
45+
46+ class ExitBasicBlock extends BasicBlock {
47+ ExitBasicBlock ( ) { scopeLast ( _, this .getLastElement ( ) , _) }
48+ }
49+
50+ /** Holds if `a` is assigned in non-constructor callable `c`. */
51+ pragma [ nomagic]
52+ private predicate assignableDefinition ( Assignable a , Callable c ) {
53+ exists ( AssignableDefinition def | def .getTarget ( ) = a |
54+ c = def .getEnclosingCallable ( ) and
55+ not c instanceof Constructor
56+ )
57+ }
58+
59+ /** Holds if `a` is accessed in callable `c`. */
60+ pragma [ nomagic]
61+ private predicate assignableAccess ( Assignable a , Callable c ) {
62+ exists ( AssignableAccess aa | aa .getTarget ( ) = a | c = aa .getEnclosingCallable ( ) )
63+ }
64+
65+ pragma [ nomagic]
66+ private predicate assignableNoCapturing ( Assignable a , Callable c ) {
67+ assignableAccess ( a , c ) and
68+ /*
69+ * The code below is equivalent to
70+ * ```ql
71+ * not exists(Callable other | assignableDefinition(a, other) | other != c)
72+ * ```
73+ * but it avoids a Cartesian product in the compiler generated antijoin
74+ * predicate.
75+ */
76+
77+ (
78+ not assignableDefinition ( a , _)
79+ or
80+ c = unique( Callable c0 | assignableDefinition ( a , c0 ) | c0 )
81+ )
82+ }
83+
84+ pragma [ noinline]
85+ private predicate assignableNoComplexQualifiers ( Assignable a ) {
86+ forall ( QualifiableExpr qe | qe .( AssignableAccess ) .getTarget ( ) = a | qe .targetIsThisInstance ( ) )
87+ }
88+
89+ /**
90+ * A simple assignable. Either a local scope variable or a field/property
91+ * that behaves like a local scope variable.
92+ */
93+ class SourceVariable extends Assignable {
94+ private Callable c ;
95+
96+ SourceVariable ( ) {
97+ (
98+ this instanceof LocalScopeVariable
99+ or
100+ this = any ( Field f | not f .isVolatile ( ) )
101+ or
102+ this = any ( TrivialProperty tp | not tp .isOverridableOrImplementable ( ) )
103+ ) and
104+ assignableNoCapturing ( this , c ) and
105+ assignableNoComplexQualifiers ( this )
106+ }
107+
108+ /** Gets a callable in which this simple assignable can be analyzed. */
109+ Callable getACallable ( ) { result = c }
110+ }
111+
112+ predicate variableWrite ( BasicBlock bb , int i , SourceVariable v , boolean certain ) {
113+ exists ( AssignableDefinition def |
114+ definitionAt ( def , bb , i , v ) and
115+ if def .getTargetAccess ( ) .isRefArgument ( ) then certain = false else certain = true
116+ )
117+ or
118+ exists ( Callable c |
119+ implicitEntryDef ( c , bb , v ) and
120+ i = - 1 and
121+ certain = true
122+ )
123+ }
124+
125+ predicate variableRead ( BasicBlock bb , int i , SourceVariable v , boolean certain ) {
126+ exists ( AssignableRead read |
127+ read = bb .getElement ( i ) and
128+ read .getTarget ( ) = v and
129+ certain = true
130+ )
131+ or
132+ v =
133+ any ( LocalScopeVariable lsv |
134+ lsv .getCallable ( ) = bb .( ExitBasicBlock ) .getEnclosingCallable ( ) and
135+ i = bb .length ( ) and
136+ ( lsv .isRef ( ) or v .( Parameter ) .isOut ( ) ) and
137+ certain = false
138+ )
139+ }
140+ }
141+
142+ private module SsaImpl = SsaImplCommon:: Make< SsaInput > ;
11143
12144 class Definition extends SsaImpl:: Definition {
13145 final AssignableRead getARead ( ) {
14- exists ( BasicBlock bb , int i |
146+ exists ( SsaInput :: BasicBlock bb , int i |
15147 SsaImpl:: ssaDefReachesRead ( _, this , bb , i ) and
16148 result = bb .getElement ( i )
17149 )
18150 }
19151
20152 final AssignableDefinition getDefinition ( ) {
21- exists ( BasicBlock bb , int i , SourceVariable v |
153+ exists ( SsaInput :: BasicBlock bb , int i , SsaInput :: SourceVariable v |
22154 this .definesAt ( v , bb , i ) and
23155 definitionAt ( result , bb , i , v )
24156 )
25157 }
26158
27159 final AssignableRead getAFirstRead ( ) {
28- exists ( BasicBlock bb1 , int i1 , BasicBlock bb2 , int i2 |
160+ exists ( SsaInput :: BasicBlock bb1 , int i1 , SsaInput :: BasicBlock bb2 , int i2 |
29161 this .definesAt ( _, bb1 , i1 ) and
30162 SsaImpl:: adjacentDefRead ( this , bb1 , i1 , bb2 , i2 ) and
31163 result = bb2 .getElement ( i2 )
@@ -42,14 +174,14 @@ module PreSsa {
42174 not result instanceof PhiNode
43175 }
44176
45- final predicate isLiveAtEndOfBlock ( BasicBlock bb ) {
177+ final predicate isLiveAtEndOfBlock ( SsaInput :: BasicBlock bb ) {
46178 SsaImpl:: ssaDefReachesEndOfBlock ( bb , this , _)
47179 }
48180
49181 Location getLocation ( ) {
50182 result = this .getDefinition ( ) .getLocation ( )
51183 or
52- exists ( Callable c , BasicBlock bb , SourceVariable v |
184+ exists ( Callable c , SsaInput :: BasicBlock bb , SsaInput :: SourceVariable v |
53185 this .definesAt ( v , bb , - 1 ) and
54186 implicitEntryDef ( c , bb , v ) and
55187 result = c .getLocation ( )
@@ -64,9 +196,9 @@ module PreSsa {
64196 }
65197
66198 predicate adjacentReadPairSameVar ( AssignableRead read1 , AssignableRead read2 ) {
67- exists ( BasicBlock bb1 , int i1 , BasicBlock bb2 , int i2 |
199+ exists ( SsaInput :: BasicBlock bb1 , int i1 , SsaInput :: BasicBlock bb2 , int i2 |
68200 read1 = bb1 .getElement ( i1 ) and
69- variableRead ( bb1 , i1 , _, true ) and
201+ SsaInput :: variableRead ( bb1 , i1 , _, true ) and
70202 SsaImpl:: adjacentDefRead ( _, bb1 , i1 , bb2 , i2 ) and
71203 read2 = bb2 .getElement ( i2 )
72204 )
0 commit comments