@@ -74,6 +74,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
7474 capturedEntryWrite ( bb , i , v )
7575 ) and
7676 certain = true
77+ or
78+ capturedCallWrite ( _, bb , i , v ) and certain = false
7779 }
7880
7981 predicate variableRead ( BasicBlock bb , int i , SourceVariable v , boolean certain ) {
@@ -99,6 +101,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
99101 certain = false
100102 )
101103 or
104+ capturedCallRead ( _, bb , i , v ) and certain = false
105+ or
102106 capturedExitRead ( bb , i , v ) and certain = false
103107 }
104108}
@@ -145,6 +149,35 @@ private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
145149 )
146150}
147151
152+ /**
153+ * Holds if captured variable `v` is written directly inside `scope`,
154+ * or inside a (transitively) nested scope of `scope`.
155+ */
156+ pragma [ noinline]
157+ private predicate hasCapturedWrite ( Variable v , Cfg:: CfgScope scope ) {
158+ any ( VariableWriteAccess write | write .getVariable ( ) = v and scope = write .getEnclosingCallable * ( ) )
159+ .isCapture ( )
160+ }
161+
162+ /**
163+ * Holds if `v` is read inside basic block `bb` at index `i`, which is in the
164+ * immediate outer scope of `scope`.
165+ */
166+ pragma [ noinline]
167+ private predicate variableReadActualInOuterScope (
168+ BasicBlock bb , int i , Variable v , Cfg:: CfgScope scope
169+ ) {
170+ variableReadActual ( bb , i , v ) and bb .getScope ( ) = scope .getEnclosingCallable ( )
171+ }
172+
173+ pragma [ noinline]
174+ private predicate hasVariableReadWithCapturedWrite (
175+ BasicBlock bb , int i , Variable v , Cfg:: CfgScope scope
176+ ) {
177+ hasCapturedWrite ( v , scope ) and
178+ variableReadActualInOuterScope ( bb , i , v , scope )
179+ }
180+
148181private predicate adjacentDefReachesRead (
149182 Definition def , BasicBlock bb1 , int i1 , BasicBlock bb2 , int i2
150183) {
@@ -223,6 +256,40 @@ private predicate readsCapturedVariable(BasicBlock bb, Variable v) {
223256 getCapturedVariableAccess ( bb , v ) instanceof VariableReadAccess
224257}
225258
259+ /**
260+ * Holds if captured variable `v` is read directly inside `scope`,
261+ * or inside a (transitively) nested scope of `scope`.
262+ */
263+ pragma [ noinline]
264+ private predicate hasCapturedRead ( Variable v , Cfg:: CfgScope scope ) {
265+ any ( VariableReadAccess read | read .getVariable ( ) = v and scope = read .getEnclosingCallable * ( ) )
266+ .isCapture ( )
267+ }
268+
269+ /**
270+ * Holds if `v` is written inside basic block `bb` at index `i`, which is in
271+ * the immediate outer scope of `scope`.
272+ */
273+ pragma [ noinline]
274+ private predicate variableWriteInOuterScope ( BasicBlock bb , int i , Variable v , Cfg:: CfgScope scope ) {
275+ SsaInput:: variableWrite ( bb , i , v , _) and scope .getEnclosingCallable ( ) = bb .getScope ( )
276+ }
277+
278+ /**
279+ * Holds if the call `call` at index `i` in basic block `bb` may reach
280+ * a callable that reads captured variable `v`.
281+ */
282+ private predicate capturedCallRead ( CallExprBase call , BasicBlock bb , int i , Variable v ) {
283+ exists ( Cfg:: CfgScope scope |
284+ hasCapturedRead ( v , scope ) and
285+ (
286+ variableWriteInOuterScope ( bb , any ( int j | j < i ) , v , scope ) or
287+ variableWriteInOuterScope ( bb .getAPredecessor + ( ) , _, v , scope )
288+ ) and
289+ call = bb .getNode ( i ) .getAstNode ( )
290+ )
291+ }
292+
226293/**
227294 * Holds if a pseudo read of captured variable `v` should be inserted
228295 * at index `i` in exit block `bb`.
@@ -245,6 +312,20 @@ private module Cached {
245312 i = - 1
246313 }
247314
315+ /**
316+ * Holds if the call `call` at index `i` in basic block `bb` may reach a callable
317+ * that writes captured variable `v`.
318+ */
319+ cached
320+ predicate capturedCallWrite ( CallExprBase call , BasicBlock bb , int i , Variable v ) {
321+ call = bb .getNode ( i ) .getAstNode ( ) and
322+ exists ( Cfg:: CfgScope scope |
323+ hasVariableReadWithCapturedWrite ( bb , any ( int j | j > i ) , v , scope )
324+ or
325+ hasVariableReadWithCapturedWrite ( bb .getASuccessor + ( ) , _, v , scope )
326+ )
327+ }
328+
248329 /**
249330 * Holds if `v` is written at index `i` in basic block `bb`, and the corresponding
250331 * AST write access is `write`.
0 commit comments