@@ -99,9 +99,7 @@ module API {
99
99
*/
100
100
pragma [ inline]
101
101
DataFlow:: Node getAValueReachableFromSource ( ) {
102
- exists ( DataFlow:: LocalSourceNode src | Impl:: use ( this , src ) |
103
- Impl:: trackUseNode ( src ) .flowsTo ( result )
104
- )
102
+ result = getAValueReachableFromSourceInline ( this )
105
103
}
106
104
107
105
/**
@@ -121,7 +119,19 @@ module API {
121
119
* end
122
120
* ```
123
121
*/
124
- DataFlow:: LocalSourceNode asSource ( ) { Impl:: use ( this , result ) }
122
+ pragma [ inline]
123
+ DataFlow:: LocalSourceNode asSource ( ) { result = pragma [ only_bind_out ] ( this ) .asSourceInternal ( ) }
124
+
125
+ /**
126
+ * INTERNAL USE ONLY.
127
+ *
128
+ * Same as `asSource()` but without join-order hints.
129
+ */
130
+ cached
131
+ DataFlow:: LocalSourceNode asSourceInternal ( ) {
132
+ Impl:: forceCachingInSameStage ( ) and
133
+ Impl:: use ( this , result )
134
+ }
125
135
126
136
/**
127
137
* Gets a data-flow node where this value leaves the current codebase and flows into an
@@ -167,6 +177,7 @@ module API {
167
177
/**
168
178
* Gets a call to a method on the receiver represented by this API component.
169
179
*/
180
+ pragma [ inline]
170
181
DataFlow:: CallNode getAMethodCall ( string method ) { result = this .getReturn ( method ) .asSource ( ) }
171
182
172
183
/**
@@ -177,15 +188,29 @@ module API {
177
188
* - A submodule of a module
178
189
* - An attribute of an object
179
190
*/
180
- bindingset [ m]
181
- bindingset [ result ]
182
- Node getMember ( string m ) { result = this .getASuccessor ( Label:: member ( m ) ) }
191
+ pragma [ inline]
192
+ Node getMember ( string m ) { result = pragma [ only_bind_out ] ( this ) .getMemberInternal ( m ) }
193
+
194
+ /**
195
+ * INTERNAL USE ONLY.
196
+ *
197
+ * Same as `getMember` but without join-order hints.
198
+ */
199
+ cached
200
+ Node getMemberInternal ( string m ) {
201
+ Impl:: forceCachingInSameStage ( ) and
202
+ result = this .getASuccessor ( Label:: member ( m ) )
203
+ }
183
204
184
205
/**
185
206
* Gets a node representing a member of this API component where the name of the member may
186
207
* or may not be known statically.
187
208
*/
188
- Node getAMember ( ) { result = this .getASuccessor ( Label:: member ( _) ) }
209
+ cached
210
+ Node getAMember ( ) {
211
+ Impl:: forceCachingInSameStage ( ) and
212
+ result = this .getASuccessor ( Label:: member ( _) )
213
+ }
189
214
190
215
/**
191
216
* Gets a node representing an instance of this API component, that is, an object whose
@@ -198,41 +223,75 @@ module API {
198
223
* This predicate may have multiple results when there are multiple constructor calls invoking this API component.
199
224
* Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls.
200
225
*/
226
+ pragma [ inline]
201
227
Node getInstance ( ) { result = this .getASubclass ( ) .getReturn ( "new" ) }
202
228
203
229
/**
204
230
* Gets a node representing a call to `method` on the receiver represented by this node.
205
231
*/
232
+ pragma [ inline]
206
233
MethodAccessNode getMethod ( string method ) {
234
+ result = pragma [ only_bind_out ] ( this ) .getMethodInternal ( method )
235
+ }
236
+
237
+ /**
238
+ * INTERNAL USE ONLY.
239
+ *
240
+ * Same as `getMethod` but without join-order hints.
241
+ */
242
+ cached
243
+ MethodAccessNode getMethodInternal ( string method ) {
244
+ Impl:: forceCachingInSameStage ( ) and
207
245
result = this .getASubclass ( ) .getASuccessor ( Label:: method ( method ) )
208
246
}
209
247
210
248
/**
211
249
* Gets a node representing the result of this call.
212
250
*/
213
- Node getReturn ( ) { result = this .getASuccessor ( Label:: return ( ) ) }
251
+ pragma [ inline]
252
+ Node getReturn ( ) { result = pragma [ only_bind_out ] ( this ) .getReturnInternal ( ) }
253
+
254
+ /**
255
+ * INTERNAL USE ONLY.
256
+ *
257
+ * Same as `getReturn()` but without join-order hints.
258
+ */
259
+ cached
260
+ Node getReturnInternal ( ) {
261
+ Impl:: forceCachingInSameStage ( ) and result = this .getASuccessor ( Label:: return ( ) )
262
+ }
214
263
215
264
/**
216
265
* Gets a node representing the result of calling a method on the receiver represented by this node.
217
266
*/
267
+ pragma [ inline]
218
268
Node getReturn ( string method ) { result = this .getMethod ( method ) .getReturn ( ) }
219
269
220
270
/** Gets an API node representing the `n`th positional parameter. */
221
- pragma [ nomagic]
222
- Node getParameter ( int n ) { result = this .getASuccessor ( Label:: parameter ( n ) ) }
271
+ cached
272
+ Node getParameter ( int n ) {
273
+ Impl:: forceCachingInSameStage ( ) and
274
+ result = this .getASuccessor ( Label:: parameter ( n ) )
275
+ }
223
276
224
277
/** Gets an API node representing the given keyword parameter. */
225
- pragma [ nomagic ]
278
+ cached
226
279
Node getKeywordParameter ( string name ) {
280
+ Impl:: forceCachingInSameStage ( ) and
227
281
result = this .getASuccessor ( Label:: keywordParameter ( name ) )
228
282
}
229
283
230
284
/** Gets an API node representing the block parameter. */
231
- Node getBlock ( ) { result = this .getASuccessor ( Label:: blockParameter ( ) ) }
285
+ cached
286
+ Node getBlock ( ) {
287
+ Impl:: forceCachingInSameStage ( ) and
288
+ result = this .getASuccessor ( Label:: blockParameter ( ) )
289
+ }
232
290
233
291
/**
234
292
* Gets a `new` call to the function represented by this API component.
235
293
*/
294
+ pragma [ inline]
236
295
DataFlow:: ExprNode getAnInstantiation ( ) { result = this .getInstance ( ) .asSource ( ) }
237
296
238
297
/**
@@ -255,12 +314,17 @@ module API {
255
314
* ```
256
315
* In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only.
257
316
*/
258
- Node getAnImmediateSubclass ( ) { result = this .getASuccessor ( Label:: subclass ( ) ) }
317
+ cached
318
+ Node getAnImmediateSubclass ( ) {
319
+ Impl:: forceCachingInSameStage ( ) and result = this .getASuccessor ( Label:: subclass ( ) )
320
+ }
259
321
260
322
/**
261
323
* Gets a node representing the `content` stored on the base object.
262
324
*/
325
+ cached
263
326
Node getContent ( DataFlow:: Content content ) {
327
+ Impl:: forceCachingInSameStage ( ) and
264
328
result = this .getASuccessor ( Label:: content ( content ) )
265
329
}
266
330
@@ -274,10 +338,16 @@ module API {
274
338
}
275
339
276
340
/** Gets a node representing the instance field of the given `name`, which must include the `@` character. */
277
- Node getField ( string name ) { result = this .getContent ( DataFlowPrivate:: TFieldContent ( name ) ) }
341
+ cached
342
+ Node getField ( string name ) {
343
+ Impl:: forceCachingInSameStage ( ) and
344
+ result = this .getContent ( DataFlowPrivate:: TFieldContent ( name ) )
345
+ }
278
346
279
347
/** Gets a node representing an element of this collection (known or unknown). */
348
+ cached
280
349
Node getAnElement ( ) {
350
+ Impl:: forceCachingInSameStage ( ) and
281
351
result = this .getContents ( any ( DataFlow:: ContentSet set | set .isAnyElement ( ) ) )
282
352
}
283
353
@@ -363,6 +433,16 @@ module API {
363
433
int getDepth ( ) { result = Impl:: distanceFromRoot ( this ) }
364
434
}
365
435
436
+ bindingset [ node]
437
+ pragma [ inline_late]
438
+ private DataFlow:: Node getAValueReachableFromSourceInline ( Node node ) {
439
+ exists ( DataFlow:: LocalSourceNode src , DataFlow:: LocalSourceNode dst |
440
+ Impl:: use ( node , pragma [ only_bind_into ] ( src ) ) and
441
+ pragma [ only_bind_into ] ( dst ) = Impl:: trackUseNode ( src ) and
442
+ dst .flowsTo ( result )
443
+ )
444
+ }
445
+
366
446
/** The root node of an API graph. */
367
447
class Root extends Node , Impl:: MkRoot {
368
448
override string toString ( ) { result = "root" }
@@ -443,7 +523,10 @@ module API {
443
523
* you should use `.getMember` on the parent module/class. For example, for nodes corresponding to the class `Gem::Version`,
444
524
* use `getTopLevelMember("Gem").getMember("Version")`.
445
525
*/
446
- Node getTopLevelMember ( string m ) { result = root ( ) .getMember ( m ) }
526
+ cached
527
+ Node getTopLevelMember ( string m ) {
528
+ Impl:: forceCachingInSameStage ( ) and result = root ( ) .getMemberInternal ( m )
529
+ }
447
530
448
531
/**
449
532
* Provides the actual implementation of API graphs, cached for performance.
@@ -469,6 +552,32 @@ module API {
469
552
*/
470
553
cached
471
554
private module Impl {
555
+ cached
556
+ predicate forceCachingInSameStage ( ) { any ( ) }
557
+
558
+ cached
559
+ predicate forceCachingBackref ( ) {
560
+ 1 = 1
561
+ or
562
+ exists ( getTopLevelMember ( _) )
563
+ or
564
+ exists (
565
+ any ( Node n )
566
+ .getMemberInternal ( "foo" )
567
+ .getAMember ( )
568
+ .getMethodInternal ( "foo" )
569
+ .getReturnInternal ( )
570
+ .getParameter ( 0 )
571
+ .getKeywordParameter ( "foo" )
572
+ .getBlock ( )
573
+ .getAnImmediateSubclass ( )
574
+ .getContent ( _)
575
+ .getField ( _)
576
+ .getAnElement ( )
577
+ .asSourceInternal ( )
578
+ )
579
+ }
580
+
472
581
cached
473
582
newtype TApiNode =
474
583
/** The root of the API graph. */
0 commit comments