@@ -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,10 @@ module API {
121
119
* end
122
120
* ```
123
121
*/
124
- DataFlow:: LocalSourceNode asSource ( ) { Impl:: use ( this , result ) }
122
+ pragma [ inline]
123
+ DataFlow:: LocalSourceNode asSource ( ) {
124
+ result = pragma [ only_bind_out ] ( this ) .( Node:: Internal ) .asSourceInternal ( )
125
+ }
125
126
126
127
/**
127
128
* Gets a data-flow node where this value leaves the current codebase and flows into an
@@ -167,6 +168,7 @@ module API {
167
168
/**
168
169
* Gets a call to a method on the receiver represented by this API component.
169
170
*/
171
+ pragma [ inline]
170
172
DataFlow:: CallNode getAMethodCall ( string method ) { result = this .getReturn ( method ) .asSource ( ) }
171
173
172
174
/**
@@ -177,15 +179,20 @@ module API {
177
179
* - A submodule of a module
178
180
* - An attribute of an object
179
181
*/
180
- bindingset [ m]
181
- bindingset [ result ]
182
- Node getMember ( string m ) { result = this .getASuccessor ( Label:: member ( m ) ) }
182
+ pragma [ inline]
183
+ Node getMember ( string m ) {
184
+ result = pragma [ only_bind_out ] ( this ) .( Node:: Internal ) .getMemberInternal ( m )
185
+ }
183
186
184
187
/**
185
188
* Gets a node representing a member of this API component where the name of the member may
186
189
* or may not be known statically.
187
190
*/
188
- Node getAMember ( ) { result = this .getASuccessor ( Label:: member ( _) ) }
191
+ cached
192
+ Node getAMember ( ) {
193
+ Impl:: forceCachingInSameStage ( ) and
194
+ result = this .getASuccessor ( Label:: member ( _) )
195
+ }
189
196
190
197
/**
191
198
* Gets a node representing an instance of this API component, that is, an object whose
@@ -198,41 +205,54 @@ module API {
198
205
* This predicate may have multiple results when there are multiple constructor calls invoking this API component.
199
206
* Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls.
200
207
*/
208
+ pragma [ inline]
201
209
Node getInstance ( ) { result = this .getASubclass ( ) .getReturn ( "new" ) }
202
210
203
211
/**
204
212
* Gets a node representing a call to `method` on the receiver represented by this node.
205
213
*/
214
+ pragma [ inline]
206
215
MethodAccessNode getMethod ( string method ) {
207
- result = this . getASubclass ( ) . getASuccessor ( Label :: method ( method ) )
216
+ result = pragma [ only_bind_out ] ( this ) . ( Node :: Internal ) . getMethodInternal ( method )
208
217
}
209
218
210
219
/**
211
220
* Gets a node representing the result of this call.
212
221
*/
213
- Node getReturn ( ) { result = this .getASuccessor ( Label:: return ( ) ) }
222
+ pragma [ inline]
223
+ Node getReturn ( ) { result = pragma [ only_bind_out ] ( this ) .( Node:: Internal ) .getReturnInternal ( ) }
214
224
215
225
/**
216
226
* Gets a node representing the result of calling a method on the receiver represented by this node.
217
227
*/
228
+ pragma [ inline]
218
229
Node getReturn ( string method ) { result = this .getMethod ( method ) .getReturn ( ) }
219
230
220
231
/** Gets an API node representing the `n`th positional parameter. */
221
- pragma [ nomagic]
222
- Node getParameter ( int n ) { result = this .getASuccessor ( Label:: parameter ( n ) ) }
232
+ cached
233
+ Node getParameter ( int n ) {
234
+ Impl:: forceCachingInSameStage ( ) and
235
+ result = this .getASuccessor ( Label:: parameter ( n ) )
236
+ }
223
237
224
238
/** Gets an API node representing the given keyword parameter. */
225
- pragma [ nomagic ]
239
+ cached
226
240
Node getKeywordParameter ( string name ) {
241
+ Impl:: forceCachingInSameStage ( ) and
227
242
result = this .getASuccessor ( Label:: keywordParameter ( name ) )
228
243
}
229
244
230
245
/** Gets an API node representing the block parameter. */
231
- Node getBlock ( ) { result = this .getASuccessor ( Label:: blockParameter ( ) ) }
246
+ cached
247
+ Node getBlock ( ) {
248
+ Impl:: forceCachingInSameStage ( ) and
249
+ result = this .getASuccessor ( Label:: blockParameter ( ) )
250
+ }
232
251
233
252
/**
234
253
* Gets a `new` call to the function represented by this API component.
235
254
*/
255
+ pragma [ inline]
236
256
DataFlow:: ExprNode getAnInstantiation ( ) { result = this .getInstance ( ) .asSource ( ) }
237
257
238
258
/**
@@ -255,12 +275,17 @@ module API {
255
275
* ```
256
276
* In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only.
257
277
*/
258
- Node getAnImmediateSubclass ( ) { result = this .getASuccessor ( Label:: subclass ( ) ) }
278
+ cached
279
+ Node getAnImmediateSubclass ( ) {
280
+ Impl:: forceCachingInSameStage ( ) and result = this .getASuccessor ( Label:: subclass ( ) )
281
+ }
259
282
260
283
/**
261
284
* Gets a node representing the `content` stored on the base object.
262
285
*/
286
+ cached
263
287
Node getContent ( DataFlow:: Content content ) {
288
+ Impl:: forceCachingInSameStage ( ) and
264
289
result = this .getASuccessor ( Label:: content ( content ) )
265
290
}
266
291
@@ -274,10 +299,16 @@ module API {
274
299
}
275
300
276
301
/** 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 ) ) }
302
+ cached
303
+ Node getField ( string name ) {
304
+ Impl:: forceCachingInSameStage ( ) and
305
+ result = this .getContent ( DataFlowPrivate:: TFieldContent ( name ) )
306
+ }
278
307
279
308
/** Gets a node representing an element of this collection (known or unknown). */
309
+ cached
280
310
Node getAnElement ( ) {
311
+ Impl:: forceCachingInSameStage ( ) and
281
312
result = this .getContents ( any ( DataFlow:: ContentSet set | set .isAnyElement ( ) ) )
282
313
}
283
314
@@ -337,7 +368,7 @@ module API {
337
368
/**
338
369
* Gets a textual representation of this element.
339
370
*/
340
- abstract string toString ( ) ;
371
+ string toString ( ) { none ( ) }
341
372
342
373
/**
343
374
* Gets a path of the given `length` from the root to this node.
@@ -363,6 +394,65 @@ module API {
363
394
int getDepth ( ) { result = Impl:: distanceFromRoot ( this ) }
364
395
}
365
396
397
+ /** Companion module to the `Node` class. */
398
+ module Node {
399
+ /**
400
+ * INTERNAL USE ONLY.
401
+ *
402
+ * An API node, with some internal predicates exposed.
403
+ */
404
+ class Internal extends Node {
405
+ /**
406
+ * INTERNAL USE ONLY.
407
+ *
408
+ * Same as `asSource()` but without join-order hints.
409
+ */
410
+ cached
411
+ DataFlow:: LocalSourceNode asSourceInternal ( ) {
412
+ Impl:: forceCachingInSameStage ( ) and
413
+ Impl:: use ( this , result )
414
+ }
415
+
416
+ /**
417
+ * Same as `getMember` but without join-order hints.
418
+ */
419
+ cached
420
+ Node getMemberInternal ( string m ) {
421
+ Impl:: forceCachingInSameStage ( ) and
422
+ result = this .getASuccessor ( Label:: member ( m ) )
423
+ }
424
+
425
+ /**
426
+ * Same as `getMethod` but without join-order hints.
427
+ */
428
+ cached
429
+ MethodAccessNode getMethodInternal ( string method ) {
430
+ Impl:: forceCachingInSameStage ( ) and
431
+ result = this .getASubclass ( ) .getASuccessor ( Label:: method ( method ) )
432
+ }
433
+
434
+ /**
435
+ * INTERNAL USE ONLY.
436
+ *
437
+ * Same as `getReturn()` but without join-order hints.
438
+ */
439
+ cached
440
+ Node getReturnInternal ( ) {
441
+ Impl:: forceCachingInSameStage ( ) and result = this .getASuccessor ( Label:: return ( ) )
442
+ }
443
+ }
444
+ }
445
+
446
+ bindingset [ node]
447
+ pragma [ inline_late]
448
+ private DataFlow:: Node getAValueReachableFromSourceInline ( Node node ) {
449
+ exists ( DataFlow:: LocalSourceNode src , DataFlow:: LocalSourceNode dst |
450
+ Impl:: use ( node , pragma [ only_bind_into ] ( src ) ) and
451
+ pragma [ only_bind_into ] ( dst ) = Impl:: trackUseNode ( src ) and
452
+ dst .flowsTo ( result )
453
+ )
454
+ }
455
+
366
456
/** The root node of an API graph. */
367
457
class Root extends Node , Impl:: MkRoot {
368
458
override string toString ( ) { result = "root" }
@@ -443,7 +533,10 @@ module API {
443
533
* you should use `.getMember` on the parent module/class. For example, for nodes corresponding to the class `Gem::Version`,
444
534
* use `getTopLevelMember("Gem").getMember("Version")`.
445
535
*/
446
- Node getTopLevelMember ( string m ) { result = root ( ) .getMember ( m ) }
536
+ cached
537
+ Node getTopLevelMember ( string m ) {
538
+ Impl:: forceCachingInSameStage ( ) and result = root ( ) .( Node:: Internal ) .getMemberInternal ( m )
539
+ }
447
540
448
541
/**
449
542
* Provides the actual implementation of API graphs, cached for performance.
@@ -469,6 +562,36 @@ module API {
469
562
*/
470
563
cached
471
564
private module Impl {
565
+ cached
566
+ predicate forceCachingInSameStage ( ) { any ( ) }
567
+
568
+ cached
569
+ predicate forceCachingBackref ( ) {
570
+ 1 = 1
571
+ or
572
+ exists ( getTopLevelMember ( _) )
573
+ or
574
+ exists (
575
+ any ( Node n )
576
+ .( Node:: Internal )
577
+ .getMemberInternal ( "foo" )
578
+ .getAMember ( )
579
+ .( Node:: Internal )
580
+ .getMethodInternal ( "foo" )
581
+ .( Node:: Internal )
582
+ .getReturnInternal ( )
583
+ .getParameter ( 0 )
584
+ .getKeywordParameter ( "foo" )
585
+ .getBlock ( )
586
+ .getAnImmediateSubclass ( )
587
+ .getContent ( _)
588
+ .getField ( _)
589
+ .getAnElement ( )
590
+ .( Node:: Internal )
591
+ .asSourceInternal ( )
592
+ )
593
+ }
594
+
472
595
cached
473
596
newtype TApiNode =
474
597
/** The root of the API graph. */
0 commit comments