Skip to content

Commit 7595c1c

Browse files
committed
QL: Add a visitor for traversing recursive evaluations.
1 parent 0419b5b commit 7595c1c

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

ql/ql/src/codeql_ql/StructuredLogs.qll

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,10 @@ module KindPredicatesLog {
261261
float getCompletionTime() { result = stringToTimestamp(this.getCompletionTimeString()) }
262262

263263
float getResultSize() { result = this.getFloat("resultSize") }
264+
265+
Array getRA(string ordering) { result = this.getObject("ra").getArray(ordering) }
266+
267+
string getAnOrdering() { exists(this.getRA(result)) }
264268
}
265269

266270
class SentinelEmpty extends SummaryEvent {
@@ -317,12 +321,162 @@ module KindPredicatesLog {
317321
Predicate getPredicate() { result = getPredicateFromPosition(this.getPosition()) }
318322
}
319323

324+
/** Gets the `index`'th event that's evaluated by `recursive`. */
325+
private InLayer layerEventRank(ComputeRecursive recursive, int index) {
326+
result =
327+
rank[index + 1](InLayer cand, int startline, string filepath |
328+
cand.getComputeRecursiveEvent() = recursive and
329+
cand.hasLocationInfo(filepath, startline, _, _, _)
330+
|
331+
cand order by filepath, startline
332+
)
333+
}
334+
335+
/**
336+
* Gets the first predicate that's evaluated in an iteration
337+
* of the SCC computation rooted at `recursive`.
338+
*/
339+
private InLayer firstPredicate(ComputeRecursive recursive) {
340+
result = layerEventRank(recursive, 0)
341+
}
342+
343+
/**
344+
* Gets the last predicate that's evaluated in an iteration
345+
* of the SCC computation rooted at `recursive`.
346+
*/
347+
private InLayer lastPredicate(ComputeRecursive recursive) {
348+
exists(int n |
349+
result = layerEventRank(recursive, n) and
350+
not exists(layerEventRank(recursive, n + 1))
351+
)
352+
}
353+
354+
/**
355+
* Holds if the predicate represented by `next` was evaluated after the
356+
* predicate represented by `prev` in the SCC computation rooted at `recursive`.
357+
*/
358+
predicate successor(ComputeRecursive recursive, InLayer prev, InLayer next) {
359+
exists(int index |
360+
layerEventRank(recursive, index) = prev and
361+
layerEventRank(recursive, index + 1) = next
362+
)
363+
}
364+
365+
/**
366+
* Holds if the predicate represented by `inLayer` was run in the `iteration`'iteration
367+
* of the SCC computation rooted at `recursive`.
368+
*/
369+
private predicate ran(ComputeRecursive recursive, int iteration, InLayer inLayer) {
370+
exists(int index |
371+
inLayer = layerEventRank(recursive, index) and
372+
inLayer.getPredicateIterationMillis().getNumber(iteration) >= 0
373+
)
374+
}
375+
376+
/**
377+
* Gets the next iteration in which the predicate `pred` in the `iteration`'th iteration
378+
* of a recursive SCC rooted at `recursive` should be evaluated.
379+
*/
380+
int nextPipeline(ComputeRecursive recursive, int iteration, InLayer inLayer) {
381+
iteration = 0 and
382+
if ran(recursive, iteration, inLayer) then result = 1 else result = 0
383+
or
384+
iteration > 1 and
385+
exists(int n | n = nextPipeline(recursive, iteration - 1, inLayer) |
386+
if ran(recursive, iteration, inLayer) then result = n + 1 else result = n
387+
)
388+
}
389+
390+
bindingset[this]
391+
signature class ResultSig;
392+
393+
/**
394+
* A signature for generically traversing a SCC computation.
395+
*/
396+
signature module Fold<ResultSig R> {
397+
/**
398+
* Gets the base case for the fold. That is, the initial value that
399+
* is produced from the first evaluation of the first IN_LAYER event
400+
* in the recursive evaluation.
401+
*/
402+
bindingset[run]
403+
R base(PipeLineRun run);
404+
405+
/**
406+
* Gets the recursive case for the fold. That is, `r` is the accumulation
407+
* of the previous evaluations, and `run` is the pipeline of the next IN_LAYER
408+
* event that is evaluated.
409+
*/
410+
bindingset[run, r]
411+
R fold(PipeLineRun run, R r);
412+
}
413+
414+
module Iterate<ResultSig R, Fold<R> F> {
415+
private R iterate(ComputeRecursive recursive, int iteration, InLayer pred) {
416+
// Case: The first iteration
417+
iteration = 0 and
418+
(
419+
// Subcase: The first predicate in the first iteration
420+
pred = firstPredicate(recursive) and
421+
result = F::base(pred.getPipelineRuns().getRun(0))
422+
or
423+
// Subcase: The predicate has a predecessor
424+
exists(InLayer pred0, R r |
425+
successor(recursive, pred0, pred) and
426+
r = iterate(recursive, 0, pred0) and
427+
result = F::fold(pred.getPipelineRuns().getRun(0), r)
428+
)
429+
)
430+
or
431+
// Case: Not the first iteration
432+
iteration > 0 and
433+
(
434+
// Subcase: The first predicate in the iteration
435+
pred = firstPredicate(recursive) and
436+
exists(InLayer last, R r |
437+
last = lastPredicate(recursive) and
438+
r = iterate(recursive, iteration - 1, last) and
439+
result = F::fold(pred.getPipelineRuns().getRun(iteration), r)
440+
)
441+
or
442+
// Subcase: The predicate has a predecessor in the same iteration
443+
exists(InLayer pred0, R r |
444+
successor(recursive, pred0, pred) and
445+
r = iterate(recursive, iteration, pred0) and
446+
result = F::fold(pred.getPipelineRuns().getRun(iteration), r)
447+
)
448+
)
449+
}
450+
451+
R iterate(ComputeRecursive recursive) {
452+
exists(int iteration, InLayer pred |
453+
pred = lastPredicate(recursive) and
454+
result = iterate(recursive, iteration, pred) and
455+
not exists(iterate(recursive, iteration + 1, pred))
456+
)
457+
}
458+
}
459+
320460
class ComputeRecursive extends SummaryEvent {
321461
ComputeRecursive() { evaluationStrategy = "COMPUTE_RECURSIVE" }
322462
}
323463

324464
class InLayer extends SummaryEvent {
325465
InLayer() { evaluationStrategy = "IN_LAYER" }
466+
467+
string getMainHash() { result = this.getString("mainHash") }
468+
469+
ComputeRecursive getComputeRecursiveEvent() { result.getRAHash() = this.getMainHash() }
470+
471+
Array getPredicateIterationMillis() { result = this.getArray("predicateIterationMillis") }
472+
473+
float getPredicateIterationMillis(int i) {
474+
result = getRanked(this.getArray("predicateIterationMillis"), i)
475+
}
476+
477+
PipeLineRuns getPipelineRuns() { result = this.getArray("pipelineRuns") }
478+
479+
float getDeltaSize(int i) { result = getRanked(this.getArray("deltaSizes"), i) }
326480
}
327481

328482
class ComputedExtensional extends SummaryEvent {

ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,6 +1866,12 @@ module JSON {
18661866
/** Gets the location of this element. */
18671867
final L::Location getLocation() { json_ast_node_info(this, _, _, result) }
18681868

1869+
predicate hasLocationInfo(
1870+
string filepath, int startline, int startcolumn, int endline, int endcolumn
1871+
) {
1872+
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
1873+
}
1874+
18691875
/** Gets the parent of this element. */
18701876
final AstNode getParent() { json_ast_node_info(this, result, _, _) }
18711877

0 commit comments

Comments
 (0)