forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathControlFlowGraph.qll
More file actions
1857 lines (1653 loc) · 64.1 KB
/
ControlFlowGraph.qll
File metadata and controls
1857 lines (1653 loc) · 64.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* Provides a shared implementation of control flow graphs (CFGs).
*
* The implementation is built on a common AST signature, which contains many
* AST constructs that are common across languages. Language-specific AST
* constructs can be given control flow semantics separately and seamlessly
* integrated into the shared CFG. Any parts of the AST without explicit
* control flow semantics will be given a default left-to-right evaluation
* order with an option to choose between pre-order and post-order. By default,
* most expressions are evaluated in post-order, while most statements are
* evaluated in pre-order, but there are several exceptions to this.
*
* Control flow nodes are synthesized such that each AST node is represented by
* a unique control flow node. Each AST node also gets associated "before" and
* "after" control flow nodes, which represent the points in the control flow
* before and after the normal execution of the AST node, respectively. For
* simple leaf nodes, the "before" and "after" nodes are merged into a single
* node. For AST nodes in conditional contexts, there are two different "after"
* nodes representing the different possible values of the AST node.
*/
overlay[local?]
module;
private import codeql.util.Boolean
private import codeql.util.FileSystem
private import codeql.util.Location
private import SuccessorType
signature class TypSig;
signature module AstSig<LocationSig Location> {
/** An AST node. */
class AstNode {
/** Gets a textual representation of this AST node. */
string toString();
/** Gets the location of this AST node. */
Location getLocation();
}
/** Gets the child of this AST node at the specified index. */
AstNode getChild(AstNode n, int index);
/** Gets the immediately enclosing callable that contains this node. */
Callable getEnclosingCallable(AstNode node);
/** A callable, for example a function, method, constructor, or top-level script. */
class Callable extends AstNode;
/** Gets the body of this callable, if any. */
AstNode callableGetBody(Callable c);
/** A statement. */
class Stmt extends AstNode;
/** An expression. */
class Expr extends AstNode;
/**
* A block statement, which is a sequence of statements that are executed in
* order.
*/
class BlockStmt extends Stmt {
/** Gets the `n`th (zero-based) statement in this block. */
Stmt getStmt(int n);
/** Gets the last statement in this block. */
Stmt getLastStmt();
}
/** An expression statement. */
class ExprStmt extends Stmt {
/** Gets the expression in this expression statement. */
Expr getExpr();
}
/** An `if` statement. */
class IfStmt extends Stmt {
/** Gets the condition of this `if` statement. */
Expr getCondition();
/** Gets the `then` (true) branch of this `if` statement. */
Stmt getThen();
/** Gets the `else` (false) branch of this `if` statement, if any. */
Stmt getElse();
}
/**
* A loop statement. Loop statements are further subclassed into specific
* types of loops.
*/
class LoopStmt extends Stmt {
/** Gets the body of this loop statement. */
Stmt getBody();
}
/** A `while` loop statement. */
class WhileStmt extends LoopStmt {
/** Gets the boolean condition of this `while` loop. */
Expr getCondition();
}
/** A `do-while` loop statement. */
class DoStmt extends LoopStmt {
/** Gets the boolean condition of this `do-while` loop. */
Expr getCondition();
}
/** A traditional C-style `for` loop. */
class ForStmt extends LoopStmt {
/** Gets the initializer expression of the loop at the specified (zero-based) position, if any. */
Expr getInit(int index);
/** Gets the boolean condition of this `for` loop. */
Expr getCondition();
/** Gets the update expression of this loop at the specified (zero-based) position, if any. */
Expr getUpdate(int index);
}
/** A for-loop that iterates over the elements of a collection. */
class ForeachStmt extends LoopStmt {
/** Gets the variable declaration of this `foreach` loop. */
Expr getVariable();
/** Gets the collection expression of this `foreach` loop. */
Expr getCollection();
}
/**
* A `break` statement.
*
* Break statements complete abruptly and break out of a loop or switch.
*/
class BreakStmt extends Stmt;
/**
* A `continue` statement.
*
* Continue statements complete abruptly and continue to the next iteration
* of a loop.
*/
class ContinueStmt extends Stmt;
/**
* A `return` statement.
*
* Return statements complete abruptly and return control to the caller of
* the enclosing callable.
*/
class ReturnStmt extends Stmt {
/** Gets the expression being returned, if any. */
Expr getExpr();
}
/**
* A `throw` statement.
*
* Throw statements complete abruptly and throw an exception.
*/
class ThrowStmt extends Stmt {
/** Gets the expression being thrown. */
Expr getExpr();
}
/** A `try` statement with `catch` and/or `finally` clauses. */
class TryStmt extends Stmt {
/** Gets the body of this `try` statement. */
Stmt getBody();
/**
* Gets the `catch` clause at the specified (zero-based) position `index`
* in this `try` statement.
*/
CatchClause getCatch(int index);
/** Gets the `finally` block of this `try` statement, if any. */
Stmt getFinally();
}
/**
* Gets the initializer of this `try` statement at the specified (zero-based)
* position `index`, if any.
*
* An example of this are resource declarations in Java's try-with-resources
* statement.
*/
default AstNode getTryInit(TryStmt try, int index) { none() }
/**
* Gets the `else` block of this `try` statement, if any.
*
* Only some languages (e.g. Python) support `try-else` constructs.
*/
default AstNode getTryElse(TryStmt try) { none() }
/** A catch clause in a try statement. */
class CatchClause extends AstNode {
/** Gets the variable declared by this catch clause. */
AstNode getVariable();
/** Gets the guard condition of this catch clause, if any. */
Expr getCondition();
/** Gets the body of this catch clause. */
Stmt getBody();
}
/**
* A switch.
*
* A switch tests an expression against a number of cases, and executes the
* body of the first matching case.
*/
class Switch extends AstNode {
/**
* Gets the expression being switched on.
*
* In some languages this is optional, in which case this predicate then
* might not hold.
*/
Expr getExpr();
/** Gets the case at the specified (zero-based) `index`. */
Case getCase(int index);
}
/**
* Gets an integer indicating the control flow order of a case within a switch.
* This is most often the same as the AST order, but can be different in some
* languages if the language allows a default case to appear before other
* cases.
*
* The values do not need to be contiguous; only the relative ordering matters.
*/
default int getCaseControlFlowOrder(Switch s, Case c) { s.getCase(result) = c }
/** A case in a switch. */
class Case extends AstNode {
/** Gets a pattern being matched by this case. */
AstNode getAPattern();
/** Gets the guard expression of this case, if any. */
Expr getGuard();
/**
* Gets the body element of this case at the specified (zero-based) `index`.
*
* This is either unique when the case has a single right-hand side, or it
* is the sequence of statements between this case and the next case.
*/
AstNode getBodyElement(int index);
}
/**
* Holds if this case can fall through to the next case if it is not
* otherwise prevented with a `break` or similar.
*/
default predicate fallsThrough(Case c) { none() }
/** A ternary conditional expression. */
class ConditionalExpr extends Expr {
/** Gets the condition of this expression. */
Expr getCondition();
/** Gets the true branch of this expression. */
Expr getThen();
/** Gets the false branch of this expression. */
Expr getElse();
}
/** A binary expression. */
class BinaryExpr extends Expr {
/** Gets the left operand of this binary expression. */
Expr getLeftOperand();
/** Gets the right operand of this binary expression. */
Expr getRightOperand();
}
/** A short-circuiting logical AND expression. */
class LogicalAndExpr extends BinaryExpr;
/** A short-circuiting logical OR expression. */
class LogicalOrExpr extends BinaryExpr;
/** A short-circuiting null-coalescing expression. */
class NullCoalescingExpr extends BinaryExpr;
/** A unary expression. */
class UnaryExpr extends Expr {
/** Gets the operand of this unary expression. */
Expr getOperand();
}
/** A logical NOT expression. */
class LogicalNotExpr extends UnaryExpr;
/** A boolean literal expression. */
class BooleanLiteral extends Expr {
/** Gets the boolean value of this literal. */
boolean getValue();
}
}
/**
* Constructs the initial setup for a control flow graph. The construction is
* completed by subsequent instatiation of `Make1` and `Make2`.
*
* A complete instantiation can look as follows:
* ```ql
* private module Input implements InputSig1, InputSig2 { .. }
* private module Cfg0 = Make0<Location, Ast>;
* private module Cfg1 = Make1<Input>;
* private module Cfg2 = Make2<Input>;
* private import Cfg0
* private import Cfg1
* private import Cfg2
* import Public
* ```
*/
module Make0<LocationSig Location, AstSig<Location> Ast> {
private import Ast
signature module InputSig1 {
/**
* Reference to the cached stage of the control flow graph. Should be
* instantiated with `CfgCachedStage::ref()`.
*/
predicate cfgCachedStageRef();
/**
* A label used for matching jump sources and targets, for example in goto
* statements.
*/
class Label {
/** Gets a textual representation of this label. */
string toString();
}
/**
* Holds if the node `n` has the label `l`. For example, a label in a goto
* statement or a goto target.
*/
default predicate hasLabel(AstNode n, Label l) { none() }
/**
* Holds if the value of `child` is propagated to `parent`. For example,
* the right-hand side of short-circuiting expressions.
*
* This predicate is only relevant for AST constructs that are not already
* handled by this library.
*/
default predicate propagatesValue(AstNode child, AstNode parent) { none() }
/**
* Holds if `n` is in a conditional context of kind `kind`. For example,
* the left-hand side of a short-circuiting `&&` expression is in a
* boolean conditional context.
*
* This predicate is only relevant for AST constructs that are not already
* handled by this library.
*/
default predicate inConditionalContext(AstNode n, ConditionKind kind) { none() }
/**
* Holds if `e` is executed in pre-order. This is typical for expressions
* that are pure control-flow constructions without calculation or side
* effects, such as `ConditionalExpr` and `Switch` expressions.
*
* This predicate is only relevant for AST constructs that are not already
* handled by this library.
*/
default predicate preOrderExpr(Expr e) { none() }
/**
* Holds if `n` is executed in post-order or in-order. This means that an
* additional node is created to represent `n` in the control flow graph.
* Otherwise, `n` is represented by the "before" node.
*
* This predicate is only relevant for AST constructs that are not already
* handled by this library.
*/
default predicate postOrInOrder(AstNode n) { none() }
/**
* Holds if an additional node tagged with `tag` should be created for
* `n`. Edges targeting such nodes are labeled with `t` and therefore `t`
* should be unique for a given `(n,tag)` pair.
*
* This predicate is only relevant for AST constructs that are not already
* handled by this library.
*/
default predicate additionalNode(AstNode n, string tag, NormalSuccessor t) { none() }
/**
* Holds if `t1` implies `t2`.
*
* For example, in JavaScript, true (truthy) implies not-null, and null implies false (falsy).
*/
default predicate successorValueImplies(ConditionalSuccessor t1, ConditionalSuccessor t2) {
none()
}
}
/**
* Partially constructs the control flow graph. The construction is completed
* by subsequent instatiation of `Make2`.
*/
module Make1<InputSig1 Input1> {
/**
* Holds if `n` is executed in post-order or in-order. This means that an
* additional node is created to represent `n` in the control flow graph.
* Otherwise, `n` is represented by the "before" node.
*/
cached
private predicate postOrInOrder(AstNode n) {
Input1::cfgCachedStageRef() and
Input1::postOrInOrder(n)
or
n instanceof ReturnStmt
or
n instanceof ThrowStmt
or
n instanceof BreakStmt
or
n instanceof ContinueStmt
or
n instanceof Expr and
exists(getChild(n, _)) and
not Input1::preOrderExpr(n) and
not n instanceof LogicalAndExpr and
not n instanceof LogicalOrExpr and
not n instanceof NullCoalescingExpr and
not n instanceof LogicalNotExpr and
not n instanceof ConditionalExpr and
not n instanceof Switch and
not n instanceof Case
}
/**
* Holds if `expr` is a short-circuiting expression and `shortcircuitValue`
* is the value that causes the short-circuit.
*/
private predicate shortCircuiting(BinaryExpr expr, ConditionalSuccessor shortcircuitValue) {
expr instanceof LogicalAndExpr and shortcircuitValue.(BooleanSuccessor).getValue() = false
or
expr instanceof LogicalOrExpr and shortcircuitValue.(BooleanSuccessor).getValue() = true
or
expr instanceof NullCoalescingExpr and shortcircuitValue.(NullnessSuccessor).getValue() = true
}
/**
* Holds if the value of `child` is propagated to `parent`. For example,
* the right-hand side of short-circuiting expressions.
*/
private predicate propagatesValue(AstNode child, AstNode parent) {
Input1::propagatesValue(child, parent)
or
// For now, the `not postOrInOrder(parent)` is superfluous, as we don't
// have any short-circuiting post-order expressions yet, but this will
// change once we add support for e.g. C#'s `??=`.
shortCircuiting(parent, _) and
not postOrInOrder(parent) and
parent.(BinaryExpr).getRightOperand() = child
or
parent = any(ConditionalExpr ce | child = [ce.getThen(), ce.getElse()])
or
parent.(BlockStmt).getLastStmt() = child
or
parent.(ExprStmt).getExpr() = child
}
/**
* Holds if `n` is in a conditional context of kind `kind`. For example,
* the left-hand side of a short-circuiting `&&` expression is in a
* boolean conditional context.
*/
private predicate inConditionalContext(AstNode n, ConditionKind kind) {
Input1::inConditionalContext(n, kind)
or
exists(AstNode parent |
propagatesValue(n, parent) and
inConditionalContext(parent, kind)
)
or
exists(LogicalNotExpr notexpr |
n = notexpr.getOperand() and
inConditionalContext(notexpr, kind) and
kind.isBoolean()
)
or
exists(BinaryExpr binexpr, ConditionalSuccessor shortcircuitValue |
shortCircuiting(binexpr, shortcircuitValue) and
n = binexpr.getLeftOperand() and
kind = shortcircuitValue.getKind()
)
or
kind.isBoolean() and
(
any(IfStmt ifstmt).getCondition() = n or
any(WhileStmt whilestmt).getCondition() = n or
any(DoStmt dostmt).getCondition() = n or
any(ForStmt forstmt).getCondition() = n or
any(ConditionalExpr condexpr).getCondition() = n or
any(CatchClause catch).getCondition() = n or
any(Case case).getGuard() = n
)
or
any(ForeachStmt foreachstmt).getCollection() = n and kind.isEmptiness()
or
n instanceof CatchClause and kind.isMatching()
or
n instanceof Case and kind.isMatching()
}
/**
* Holds if `n` is a simple leaf node in the AST that does not appear in a
* conditional context. For such nodes, there is no need to create separate
* "before" and "after" control flow nodes, so we merge them.
*/
cached
private predicate simpleLeafNode(AstNode n) {
Input1::cfgCachedStageRef() and
not exists(getChild(n, _)) and
not postOrInOrder(n) and
not inConditionalContext(n, _)
}
private string loopHeaderTag() { result = "[LoopHeader]" }
/**
* Holds if an additional node tagged with `tag` should be created for
* `n`. Edges targeting such nodes are labeled with `t` and therefore `t`
* should be unique for a given `(n,tag)` pair.
*/
private predicate additionalNode(AstNode n, string tag, NormalSuccessor t) {
Input1::additionalNode(n, tag, t)
or
n instanceof LoopStmt and
tag = loopHeaderTag() and
t instanceof DirectSuccessor
}
/**
* Holds if `n` cannot terminate normally. For these cases there is no
* need to create an "after" node as that would be unreachable.
* Furthermore, skipping these nodes improves precision slightly for
* finally blocks, as the corresponding try blocks are otherwise generally
* assumed to be able to terminate normally, and hence allows for
* a normal successor from the finally block.
*/
private predicate cannotTerminateNormally(AstNode n) {
n instanceof BreakStmt
or
n instanceof ContinueStmt
or
n instanceof ReturnStmt
or
n instanceof ThrowStmt
or
cannotTerminateNormally(n.(BlockStmt).getLastStmt())
or
cannotTerminateNormally(n.(ExprStmt).getExpr())
or
exists(IfStmt ifstmt |
ifstmt = n and
cannotTerminateNormally(ifstmt.getThen()) and
cannotTerminateNormally(ifstmt.getElse())
)
or
exists(TryStmt trystmt |
trystmt = n and
cannotTerminateNormally(trystmt.getBody()) and
forall(CatchClause catch | trystmt.getCatch(_) = catch |
cannotTerminateNormally(catch.getBody())
)
)
}
/*
* - Every AST node has "before" and "after" control flow nodes (except simple leaf nodes).
* - CFG snippets always start at the "before" node.
* - In case of normal termination, the final node is an "after" node.
* - Boolean and other conditional completions are encoded in the "after" nodes.
* - The number of "after" nodes for a given AST node depends on whether the AST
* node is in a conditional context.
* - Successors are specified as simple steps between control flow nodes for
* NormalSuccessors, and as pairs of half-edges for AbruptSuccessors. This
* allows all specifications to be local.
* - Every AST node has a unique control flow node representing it. For
* preorder this is the "before" node, and for inorder/postorder this is an
* additional node that typically sits just before "after" (but may or may
* not step to it, since "after" represents normal termination).
*/
cached
private newtype TNode =
TBeforeNode(AstNode n) { Input1::cfgCachedStageRef() and exists(getEnclosingCallable(n)) } or
TAstNode(AstNode n) { postOrInOrder(n) and exists(getEnclosingCallable(n)) } or
TAfterValueNode(AstNode n, ConditionalSuccessor t) {
inConditionalContext(n, t.getKind()) and exists(getEnclosingCallable(n))
} or
TAfterNode(AstNode n) {
exists(getEnclosingCallable(n)) and
not inConditionalContext(n, _) and
not cannotTerminateNormally(n) and
not simpleLeafNode(n)
} or
TAdditionalNode(AstNode n, string tag) {
additionalNode(n, tag, _) and exists(getEnclosingCallable(n))
} or
TEntryNode(Callable c) { exists(callableGetBody(c)) } or
TAnnotatedExitNode(Callable c, Boolean normal) { exists(callableGetBody(c)) } or
TExitNode(Callable c) { exists(callableGetBody(c)) }
private class NodeImpl extends TNode {
/**
* Holds if this is the node representing the point in the control flow
* before the execution of `n`.
*/
predicate isBefore(AstNode n) { this = TBeforeNode(n) }
/**
* Holds if this is a node representing the point in the control flow
* after the normal termination of `n`. For simple leaf nodes, this is
* merged with the "before" node and is hence equal to it. For nodes in
* conditional contexts, this may be one of two possible "after" nodes
* representing the different possible values of `n`.
*/
predicate isAfter(AstNode n) {
this = TAfterNode(n)
or
this = TAfterValueNode(n, _)
or
this = TBeforeNode(n) and simpleLeafNode(n)
}
/**
* Holds if this is the node representing the normal termination of `n`
* with the value `t`.
*
* Note that `n`, and most importantly `t`, must be bound, and if this
* predicate is used to identify the starting point of a step, then
* `inConditionalContext(n, t.getKind())` must hold. On the other hand, if
* this is used to identify the end point of a step, then there is no
* such requirement - in that case `t` will be translated to the
* appropriate `SuccessorType` for `n`.
*/
bindingset[n, t]
predicate isAfterValue(AstNode n, ConditionalSuccessor t) {
this = TAfterNode(n) and exists(t)
or
this = TBeforeNode(n) and simpleLeafNode(n) and exists(t)
or
this = TAfterValueNode(n, t)
or
exists(ConditionalSuccessor t0 | this = TAfterValueNode(n, t0) |
// When this predicate is used to identify the end point of a step,
// the kinds of `t` and `t0` may not match. For example, in
// `(x || y) ?? z`, the `||` may short-circuit with a known boolean
// value `t`, but it occurs in a nullness conditional context, which
// means that the `t0` has nullness kind. In these cases we check
// whether there is an implication that allows translation from `t`
// to `t0`, and if not `t0` is simply unrestricted. If the kinds did
// match, then no translation is needed and we're covered by the
// `this = TAfterValueNode(n, t)` case above.
Input1::successorValueImplies(t, t0)
or
not Input1::successorValueImplies(t, _) and
t.getKind() != t0.getKind()
)
}
/**
* Holds if this is the node representing the evaluation of `n` to the
* value `true`.
*
* Note that if this predicate is used to identify the starting point of
* a step, then `inConditionalContext(n, BooleanCondition())` must hold.
* On the other hand, if this is used to identify the end point of a
* step, then there is no such requirement.
*/
predicate isAfterTrue(AstNode n) {
this.isAfterValue(n, any(BooleanSuccessor b | b.getValue() = true))
}
/**
* Holds if this is the node representing the evaluation of `n` to the
* value `false`.
*
* Note that if this predicate is used to identify the starting point of
* a step, then `inConditionalContext(n, BooleanCondition())` must hold.
* On the other hand, if this is used to identify the end point of a
* step, then there is no such requirement.
*/
predicate isAfterFalse(AstNode n) {
this.isAfterValue(n, any(BooleanSuccessor b | b.getValue() = false))
}
/**
* Holds if this is the node representing the given AST node when `n`
* has an in-order or post-order execution.
*/
predicate isIn(AstNode n) { this = TAstNode(n) }
/**
* Holds if this is an additional control flow node with the given tag
* for the given AST node.
*/
predicate isAdditional(AstNode n, string tag) { this = TAdditionalNode(n, tag) }
/**
* Holds if this is the unique control flow node that represents the
* given AST node.
*/
predicate injects(AstNode n) {
if postOrInOrder(n) then this = TAstNode(n) else this = TBeforeNode(n)
}
/** Gets the statement this control flow node uniquely represents, if any. */
Stmt asStmt() { this.injects(result) }
/** Gets the expression this control flow node uniquely represents, if any. */
Expr asExpr() { this.injects(result) }
/** Gets the enclosing callable of this control flow node. */
Callable getEnclosingCallable() { result = getEnclosingCallable(this.getAstNode()) }
/**
* Gets the AST node with which this control flow node is associated.
* Note that several control flow nodes are usually associated with the
* same AST node, but each control flow node is associated with a unique
* AST node.
*/
abstract AstNode getAstNode();
/**
* INTERNAL: Do not use.
*
* Gets a tag such that the pair `(getAstNode(), getIdTag())` uniquely
* identifies this node.
*/
abstract string getIdTag();
/** Gets a textual representation of this node. */
abstract string toString();
/** Gets the source location for this node. */
Location getLocation() { result = this.getAstNode().getLocation() }
}
/**
* A control flow node without the successor relation. This is used to
* reference control flow nodes during the construction of the control flow
* graph.
*/
final class PreControlFlowNode = NodeImpl;
private class BeforeNode extends NodeImpl, TBeforeNode {
private AstNode n;
BeforeNode() { this = TBeforeNode(n) }
override AstNode getAstNode() { result = n }
override string getIdTag() { result = "before" }
override string toString() {
if postOrInOrder(n) then result = "Before " + n else result = n.toString()
}
}
private class MidNode extends NodeImpl, TAstNode {
private AstNode n;
MidNode() { this = TAstNode(n) }
override AstNode getAstNode() { result = n }
override string getIdTag() { result = "ast" }
override string toString() { result = n.toString() }
}
private class AfterValueNode extends NodeImpl, TAfterValueNode {
private AstNode n;
private ConditionalSuccessor t;
AfterValueNode() { this = TAfterValueNode(n, t) }
override AstNode getAstNode() { result = n }
override string getIdTag() {
t.getValue() = true and result = "after-true"
or
t.getValue() = false and result = "after-false"
}
override string toString() { result = "After " + n + " [" + t + "]" }
}
private class AfterNode extends NodeImpl, TAfterNode {
private AstNode n;
AfterNode() { this = TAfterNode(n) }
override AstNode getAstNode() { result = n }
override string getIdTag() { result = "after" }
override string toString() { result = "After " + n }
}
private class AdditionalNode extends NodeImpl, TAdditionalNode {
private AstNode n;
private string tag;
AdditionalNode() { this = TAdditionalNode(n, tag) }
override AstNode getAstNode() { result = n }
NormalSuccessor getSuccessorType() { additionalNode(n, tag, result) }
override string getIdTag() { result = "add. " + tag }
override string toString() { result = tag + " " + n }
}
final private class EntryNodeImpl extends NodeImpl, TEntryNode {
private Callable c;
EntryNodeImpl() { this = TEntryNode(c) }
override Callable getEnclosingCallable() { result = c }
override AstNode getAstNode() { result = c }
override string getIdTag() { result = "entry" }
override string toString() { result = "Entry" }
}
/** A control flow node indicating the normal or exceptional termination of a callable. */
final private class AnnotatedExitNodeImpl extends NodeImpl, TAnnotatedExitNode {
Callable c;
boolean normal;
AnnotatedExitNodeImpl() { this = TAnnotatedExitNode(c, normal) }
override Callable getEnclosingCallable() { result = c }
override AstNode getAstNode() { result = c }
override string getIdTag() {
normal = true and result = "exit-normal"
or
normal = false and result = "exit-exc"
}
override string toString() {
normal = true and result = "Normal Exit"
or
normal = false and result = "Exceptional Exit"
}
}
/** A control flow node indicating normal termination of a callable. */
final private class NormalExitNodeImpl extends AnnotatedExitNodeImpl {
NormalExitNodeImpl() { this = TAnnotatedExitNode(_, true) }
}
/** A control flow node indicating exceptional termination of a callable. */
final private class ExceptionalExitNodeImpl extends AnnotatedExitNodeImpl {
ExceptionalExitNodeImpl() { this = TAnnotatedExitNode(_, false) }
}
/** A control flow node indicating the termination of a callable. */
final private class ExitNodeImpl extends NodeImpl, TExitNode {
Callable c;
ExitNodeImpl() { this = TExitNode(c) }
override Callable getEnclosingCallable() { result = c }
override AstNode getAstNode() { result = c }
override string getIdTag() { result = "exit" }
override string toString() { result = "Exit" }
}
private newtype TAbruptCompletion =
TSimpleAbruptCompletion(AbruptSuccessor t) or
TLabeledAbruptCompletion(JumpSuccessor t, Input1::Label l)
/**
* A value indicating an abrupt completion of an AST node in the control
* flow graph. This is mostly equivalent to an AbruptSuccessor, but may
* also carry a label to, for example, link a goto statement with its target.
*/
class AbruptCompletion extends TAbruptCompletion {
/** Gets a textual representation of this abrupt completion. */
string toString() {
exists(AbruptSuccessor t | this = TSimpleAbruptCompletion(t) and result = t.toString())
or
exists(AbruptSuccessor t, Input1::Label l |
this = TLabeledAbruptCompletion(t, l) and
result = t + " " + l
)
}
/** Gets the successor type of this abrupt completion. */
AbruptSuccessor getSuccessorType() {
this = TSimpleAbruptCompletion(result) or this = TLabeledAbruptCompletion(result, _)
}
/**
* Gets the successor type of this abrupt completion, if this is an
* abrupt completion without a label.
*/
AbruptSuccessor asSimpleAbruptCompletion() { this = TSimpleAbruptCompletion(result) }
/** Holds if this abrupt completion has label `l`. */
predicate hasLabel(Input1::Label l) { this = TLabeledAbruptCompletion(_, l) }
}
signature module InputSig2 {
/** Holds if this catch clause catches all exceptions. */
default predicate catchAll(CatchClause catch) { none() }
/**
* Holds if this case matches all possible values, for example, if it is a
* `default` case or a match-all pattern like `Object o` or if it is the
* final case in a switch that is known to be exhaustive.
*
* A match-all case can still ultimately fail to match if it has a guard.
*/
default predicate matchAll(Case c) { none() }
/**
* Holds if `ast` may result in an abrupt completion `c` originating at
* `n`. The boolean `always` indicates whether the abrupt completion
* always occurs or whether `n` may also terminate normally.
*
* This predicate is only relevant for AST constructs that are not already
* handled by this library.
*/
predicate beginAbruptCompletion(
AstNode ast, PreControlFlowNode n, AbruptCompletion c, boolean always
);
/**
* Holds if an abrupt completion `c` from within `ast` is caught with
* flow continuing at `n`.
*
* This predicate is only relevant for AST constructs that are not already
* handled by this library.
*/
predicate endAbruptCompletion(AstNode ast, PreControlFlowNode n, AbruptCompletion c);
/**
* Holds if there is a local non-abrupt step from `n1` to `n2`.
*
* This predicate is only relevant for AST constructs that are not already
* handled by this library.
*/
predicate step(PreControlFlowNode n1, PreControlFlowNode n2);
}
/** Completes the construction of the control flow graph. */
module Make2<InputSig2 Input2> {
/**
* Holds if `ast` may result in an abrupt completion `c` originating at
* `n`. The boolean `always` indicates whether the abrupt completion
* always occurs or whether `n` may also terminate normally.
*/
private predicate beginAbruptCompletion(
AstNode ast, PreControlFlowNode n, AbruptCompletion c, boolean always
) {
Input2::beginAbruptCompletion(ast, n, c, always)
or
n.isIn(ast) and
always = true and
(
ast instanceof ReturnStmt and
c.getSuccessorType() instanceof ReturnSuccessor
or
ast instanceof ThrowStmt and
c.getSuccessorType() instanceof ExceptionSuccessor
or
ast instanceof BreakStmt and
c.getSuccessorType() instanceof BreakSuccessor
or
ast instanceof ContinueStmt and
c.getSuccessorType() instanceof ContinueSuccessor
) and
(