Skip to content

Commit 76ed5f3

Browse files
committed
temp2
1 parent a2cc202 commit 76ed5f3

File tree

6 files changed

+339
-90
lines changed

6 files changed

+339
-90
lines changed
Lines changed: 155 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,215 @@
1+
/**
2+
* Provides classes and predicates for working with captured variables.
3+
*
4+
* We model reads from and writes to captured variable using field-flow.
5+
*
6+
* Example:
7+
*
8+
* ```rb
9+
* def foo
10+
* x = 1
11+
* puts x
12+
* fn = -> {
13+
* puts x
14+
* x = 2
15+
* }
16+
* fn.call
17+
* puts x
18+
* end
19+
* ```
20+
*
21+
* We create a new kind of synthetic local variable, one per capturing callable,
22+
* in this case `fn`, let's call it `fn_closure`.
23+
*
24+
* `fn_closure` will have an implicit entry definition at the start of `foo`,
25+
* as well as at the start of the lambda itself, in which it behaves like a
26+
* `self` parameter.
27+
*
28+
* Writes to captured variables will be turned into store steps, where the target
29+
* of the store is the synthetic variable, and dually for reads.
30+
*
31+
* ```rb
32+
* def foo
33+
* # fn_closure = implicit
34+
* x = 1 # fn_closure.x = 1
35+
* puts x # puts fn_closure.x
36+
* fn = -> {
37+
* # fn_closure = self
38+
* puts x # puts fn_closure.x
39+
* x = 2 # fn_closure.x = 2
40+
* }
41+
* fn.call
42+
* puts x # puts fn_closure.x
43+
* end
44+
* ```
45+
*
46+
* We will use SSA to construct adjacent use-use steps for `fn_closure`.
47+
*
48+
* Now we need to actually model flow in/out of the lambda. We do this by
49+
* adding another synthetic access to `fn_closure` at the very definiton
50+
* of the lambda, add a local flow step from this access to the lambda
51+
* itself, and ensure that the receiver of a lambda call is a `self` argument:
52+
*
53+
* ```rb
54+
* def foo
55+
* # fn_closure = implicit
56+
* x = 1 # fn_closure.x = 1
57+
* puts x # puts fn_closure.x
58+
* fn = # fn_closure, local step into the lambda below
59+
* -> {
60+
* # fn_closure = self
61+
* puts x # puts fn_closure.x
62+
* x = 2 # fn_closure.x = 2
63+
* }
64+
* fn.call # fn is a `self` argument
65+
* puts x # puts fn_closure.x
66+
* end
67+
* ```
68+
*
69+
* The above ensures flow _into_ the lambda. For flow out
70+
*/
71+
172
private import codeql.ssa.Ssa as SsaImplCommon
273
private import codeql.ruby.AST
374
private import codeql.ruby.CFG as Cfg
475
private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared as ControlFlowGraphImplShared
576
private import codeql.ruby.dataflow.SSA
6-
private import codeql.ruby.ast.Variable
777
private import Cfg::CfgNodes::ExprNodes
78+
private import DataFlowPrivate as DataFlowPrivate
79+
private import codeql.util.Unit
880

9-
class CapturedVariableAccess extends LocalVariableAccess {
81+
private class CapturedVariableAccess extends LocalVariableAccess {
1082
CapturedVariableAccess() { this.isCapturedAccess() }
1183
}
1284

85+
/** A variable that is captured. */
1386
class CapturedVariable extends LocalVariable {
1487
CapturedVariable() { this.isCaptured() }
1588
}
1689

90+
/** A callable that captures a local variable. */
1791
class CapturingCallable extends Callable {
1892
CapturingCallable() { any(CapturedVariableAccess access).getCfgScope() = this }
1993

94+
/** Gets a local variable captured by this callable. */
2095
CapturedVariable getACapturedVariable() { result.getAnAccess().getCfgScope() = this }
2196
}
2297

23-
private predicate entryWrite(Cfg::EntryBasicBlock bb, int i, CapturingCallable v) {
24-
i = -1 and
25-
(
26-
bb.getScope() = v
27-
or
28-
bb.getScope() = v.getACapturedVariable().getDeclaringScope()
29-
)
30-
}
31-
98+
// private predicate entryWrite(Cfg::EntryBasicBlock bb, int i, CapturingCallable c) {
99+
// i = -1 and
100+
// (
101+
// bb.getScope() = c
102+
// or
103+
// bb.getScope() = c.getACapturedVariable().getDeclaringScope()
104+
// )
105+
// }
106+
// private predicate entryWrite(Cfg::EntryBasicBlock bb, CapturingCallable c) {
107+
// bb.getScope() instanceof CapturingCallable
108+
// or
109+
// or
110+
// bb.getScope() = c.getACapturedVariable().getDeclaringScope()
111+
// )
112+
// }
32113
private predicate captureRead1(
33-
Cfg::BasicBlock bb, int i, CapturedVariable var, VariableAccessCfgNode access, CapturingCallable v
114+
Cfg::BasicBlock bb, int i, CapturedVariable var, VariableAccessCfgNode access, CapturingCallable c
34115
) {
35116
bb.getNode(i) = access and
36117
access.getNode() = var.getAnAccess() and
37-
var = v.getACapturedVariable() and
38-
(bb.getScope() = v or bb.getScope() = var.getDefiningAccess().getCfgScope())
118+
var = c.getACapturedVariable() and
119+
(bb.getScope() = c or bb.getScope() = var.getDefiningAccess().getCfgScope())
39120
}
40121

41-
private predicate captureRead2(Cfg::BasicBlock bb, int i, CapturingCallable v) {
42-
bb.getNode(i).getNode() = v
122+
private predicate captureRead2(Cfg::BasicBlock bb, int i, Callable c) {
123+
bb.getNode(i).getNode() = c
43124
}
44125

45-
newtype TCaptureAccess =
46-
TEntryDef(Cfg::EntryBasicBlock bb, int i, CapturingCallable v) { entryWrite(bb, i, v) } or
126+
private predicate captureRead3(Cfg::BasicBlock bb, int i) {
127+
// todo: BlockParameterNode
128+
// DataFlowPrivate::lambdaCall(_, bb.getNode(i)) and
129+
// (
130+
// bb.getScope() = c
131+
// or
132+
// bb.getScope() = c.getACapturedVariable().getDeclaringScope()
133+
// )
134+
bb.getNode(i) instanceof DataFlowPrivate::Argument
135+
or
136+
DataFlowPrivate::isBlockArgument(_, bb.getNode(i))
137+
}
138+
139+
cached
140+
private newtype TCaptureAccess =
141+
TEntryDef(Cfg::EntryBasicBlock bb) or
47142
TVariableAccess(VariableAccessCfgNode access, CapturingCallable v) {
48143
captureRead1(_, _, _, access, v)
49144
} or
50-
TLambdaAccess(Cfg::BasicBlock bb, int i, CapturingCallable v) { captureRead2(bb, i, v) }
145+
TLambdaAccess(Cfg::BasicBlock bb, int i, Callable c) { captureRead2(bb, i, c) } or
146+
TCallAccess(Cfg::BasicBlock bb, int i) { captureRead3(bb, i) }
51147

52148
class CaptureAccess extends TCaptureAccess {
53149
string toString() { result = "<captured variable>" }
54150

55151
Location getLocation() {
56-
exists(Cfg::EntryBasicBlock bb, int i, CapturingCallable v |
57-
this = TEntryDef(bb, i, v) and
152+
exists(Cfg::EntryBasicBlock bb |
153+
this = TEntryDef(bb) and
58154
result = bb.getLocation()
59155
)
60156
or
61157
exists(VariableAccessCfgNode access | this = TVariableAccess(access, _) |
62158
result = access.getLocation()
63159
)
64160
or
65-
exists(CapturingCallable c |
161+
exists(Callable c |
66162
this = TLambdaAccess(_, _, c) and
67163
result = c.getLocation()
68164
)
165+
or
166+
exists(Cfg::BasicBlock bb, int i |
167+
this = TCallAccess(bb, i) and
168+
result = bb.getNode(i).getLocation()
169+
)
69170
}
70171

71172
Cfg::CfgScope getCfgScope() {
72-
exists(Cfg::EntryBasicBlock bb, int i, CapturingCallable v |
73-
this = TEntryDef(bb, i, v) and
173+
exists(Cfg::EntryBasicBlock bb |
174+
this = TEntryDef(bb) and
74175
result = bb.getScope()
75176
)
76177
or
77178
exists(VariableAccessCfgNode access | this = TVariableAccess(access, _) |
78179
result = access.getScope()
79180
)
80181
or
81-
exists(CapturingCallable c |
82-
this = TLambdaAccess(_, _, c) and
83-
result = c.getCfgScope()
182+
exists(Cfg::BasicBlock bb |
183+
this = TLambdaAccess(bb, _, _) and
184+
result = bb.getScope()
185+
)
186+
or
187+
exists(Cfg::BasicBlock bb |
188+
this = TCallAccess(bb, _) and
189+
result = bb.getScope()
84190
)
85191
}
86192

87-
predicate isLambdaAccess(Cfg::BasicBlock bb, int i, CapturingCallable v) {
88-
captureRead2(bb, i, v) and
89-
this = TLambdaAccess(bb, i, v)
90-
}
193+
predicate isLambdaAccess(Cfg::BasicBlock bb, int i, Callable c) { this = TLambdaAccess(bb, i, c) }
194+
195+
predicate isCallAccess(Cfg::BasicBlock bb, int i) { this = TCallAccess(bb, i) }
91196

92-
predicate isRead(Cfg::BasicBlock bb, int i, CapturingCallable v) {
93-
exists(VariableAccessCfgNode acc |
197+
predicate isRead(Cfg::BasicBlock bb, int i) {
198+
exists(VariableAccessCfgNode acc, CapturingCallable v |
94199
captureRead1(bb, i, _, acc, v) and
95200
this = TVariableAccess(acc, v)
96201
)
97202
or
98-
this.isLambdaAccess(bb, i, v)
203+
this.isLambdaAccess(bb, i, _)
204+
or
205+
this.isCallAccess(bb, i)
99206
}
100207

101-
predicate isWrite(Cfg::BasicBlock bb, int i, CapturingCallable v) {
102-
entryWrite(bb, i, v) and
103-
this = TEntryDef(bb, i, v)
104-
}
208+
predicate isWrite(Cfg::BasicBlock bb, int i) { this = TEntryDef(bb) and i = -1 }
105209

106-
predicate isReadOrWrite(Cfg::BasicBlock bb, int i, CapturingCallable v) {
107-
this.isRead(bb, i, v) or
108-
this.isWrite(bb, i, v)
210+
predicate isReadOrWrite(Cfg::BasicBlock bb, int i) {
211+
this.isRead(bb, i) or
212+
this.isWrite(bb, i)
109213
}
110214
}
111215

@@ -120,20 +224,22 @@ private module SsaInput implements SsaImplCommon::InputSig {
120224

121225
class ExitBasicBlock = BasicBlocks::ExitBasicBlock;
122226

123-
class SourceVariable = CapturingCallable;
227+
class SourceVariable = Unit;
124228

125229
/**
126230
* Holds if the statement at index `i` of basic block `bb` contains a write to variable `v`.
127231
* `certain` is true if the write definitely occurs.
128232
*/
129233
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
130-
any(CaptureAccess access).isWrite(bb, i, v) and
131-
certain = true
234+
any(CaptureAccess access).isWrite(bb, i) and
235+
certain = true and
236+
exists(v)
132237
}
133238

134239
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
135-
any(CaptureAccess access).isRead(bb, i, v) and
136-
certain = true
240+
any(CaptureAccess access).isRead(bb, i) and
241+
certain = true and
242+
exists(v)
137243
}
138244
}
139245

@@ -142,13 +248,9 @@ private import SsaImplCommon::Make<SsaInput> as Impl
142248
class Definition = Impl::Definition;
143249

144250
predicate adjacentStep(CaptureAccess access1, CaptureAccess access2) {
145-
exists(
146-
Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2,
147-
SsaInput::SourceVariable v
148-
|
251+
exists(Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 |
149252
Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and
150-
v = def.getSourceVariable() and
151-
access1.isReadOrWrite(bb1, i1, v) and
152-
access2.isReadOrWrite(bb2, i2, v)
253+
access1.isReadOrWrite(bb1, i1) and
254+
access2.isReadOrWrite(bb2, i2)
153255
)
154256
}

0 commit comments

Comments
 (0)