|
1 | 1 | import python |
2 | | -import semmle.python.flow.NameNode |
3 | 2 | private import semmle.python.pointsto.PointsTo |
4 | 3 |
|
5 | 4 | /* Note about matching parent and child nodes and CFG splitting: |
@@ -961,10 +960,149 @@ class RaiseStmtNode extends ControlFlowNode { |
961 | 960 |
|
962 | 961 | } |
963 | 962 |
|
964 | | -private |
965 | | -predicate defined_by(NameNode def, Variable v) { |
966 | | - def.defines(v) or |
967 | | - exists(NameNode p | defined_by(p, v) and p.getASuccessor() = def and not p.defines(v)) |
| 963 | +/** A control flow node corresponding to a (plain variable) name expression, such as `var`. |
| 964 | + * `None`, `True` and `False` are excluded. |
| 965 | + */ |
| 966 | +class NameNode extends ControlFlowNode { |
| 967 | + |
| 968 | + NameNode() { |
| 969 | + exists(Name n | py_flow_bb_node(this, n, _, _)) |
| 970 | + or |
| 971 | + exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) |
| 972 | + } |
| 973 | + |
| 974 | + /** Whether this flow node defines the variable `v`. */ |
| 975 | + predicate defines(Variable v) { |
| 976 | + exists(Name d | this.getNode() = d and d.defines(v)) |
| 977 | + and not this.isLoad() |
| 978 | + } |
| 979 | + |
| 980 | + /** Whether this flow node deletes the variable `v`. */ |
| 981 | + predicate deletes(Variable v) { |
| 982 | + exists(Name d | this.getNode() = d and d.deletes(v)) |
| 983 | + } |
| 984 | + |
| 985 | + /** Whether this flow node uses the variable `v`. */ |
| 986 | + predicate uses(Variable v) { |
| 987 | + this.isLoad() and exists(Name u | this.getNode() = u and u.uses(v)) |
| 988 | + or |
| 989 | + exists(PlaceHolder u | this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load) |
| 990 | + or |
| 991 | + Scopes::use_of_global_variable(this, v.getScope(), v.getId()) |
| 992 | + } |
| 993 | + |
| 994 | + string getId() { |
| 995 | + result = this.getNode().(Name).getId() |
| 996 | + or |
| 997 | + result = this.getNode().(PlaceHolder).getId() |
| 998 | + } |
| 999 | + |
| 1000 | + /** Whether this is a use of a local variable. */ |
| 1001 | + predicate isLocal() { |
| 1002 | + Scopes::local(this) |
| 1003 | + } |
| 1004 | + |
| 1005 | + /** Whether this is a use of a non-local variable. */ |
| 1006 | + predicate isNonLocal() { |
| 1007 | + Scopes::non_local(this) |
| 1008 | + } |
| 1009 | + |
| 1010 | + /** Whether this is a use of a global (including builtin) variable. */ |
| 1011 | + predicate isGlobal() { |
| 1012 | + Scopes::use_of_global_variable(this, _, _) |
| 1013 | + } |
| 1014 | + |
| 1015 | + predicate isSelf() { |
| 1016 | + exists(SsaVariable selfvar | |
| 1017 | + selfvar.isSelf() and selfvar.getAUse() = this |
| 1018 | + ) |
| 1019 | + } |
| 1020 | + |
| 1021 | +} |
| 1022 | + |
| 1023 | +/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */ |
| 1024 | +class NameConstantNode extends NameNode { |
| 1025 | + |
| 1026 | + NameConstantNode() { |
| 1027 | + exists(NameConstant n | py_flow_bb_node(this, n, _, _)) |
| 1028 | + } |
| 1029 | + |
| 1030 | + override deprecated predicate defines(Variable v) { none() } |
| 1031 | + |
| 1032 | + override deprecated predicate deletes(Variable v) { none() } |
| 1033 | + |
| 1034 | + /* We ought to override uses as well, but that has |
| 1035 | + * a serious performance impact. |
| 1036 | + deprecated predicate uses(Variable v) { none() } |
| 1037 | + */ |
| 1038 | +} |
| 1039 | + |
| 1040 | +private module Scopes { |
| 1041 | + |
| 1042 | + private predicate fast_local(NameNode n) { |
| 1043 | + exists(FastLocalVariable v | |
| 1044 | + n.uses(v) and |
| 1045 | + v.getScope() = n.getScope() |
| 1046 | + ) |
| 1047 | + } |
| 1048 | + |
| 1049 | + predicate local(NameNode n) { |
| 1050 | + fast_local(n) |
| 1051 | + or |
| 1052 | + exists(SsaVariable var | |
| 1053 | + var.getAUse() = n and |
| 1054 | + n.getScope() instanceof Class and |
| 1055 | + exists(var.getDefinition()) |
| 1056 | + ) |
| 1057 | + } |
| 1058 | + |
| 1059 | + predicate non_local(NameNode n) { |
| 1060 | + exists(FastLocalVariable flv | |
| 1061 | + flv.getALoad() = n.getNode() and |
| 1062 | + not flv.getScope() = n.getScope() |
| 1063 | + ) |
| 1064 | + } |
| 1065 | + |
| 1066 | + // magic is fine, but we get questionable join-ordering of it |
| 1067 | + pragma [nomagic] |
| 1068 | + predicate use_of_global_variable(NameNode n, Module scope, string name) { |
| 1069 | + n.isLoad() and |
| 1070 | + not non_local(n) |
| 1071 | + and |
| 1072 | + not exists(SsaVariable var | |
| 1073 | + var.getAUse() = n | |
| 1074 | + var.getVariable() instanceof FastLocalVariable |
| 1075 | + or |
| 1076 | + n.getScope() instanceof Class and |
| 1077 | + not maybe_undefined(var) |
| 1078 | + ) |
| 1079 | + and name = n.getId() |
| 1080 | + and scope = n.getEnclosingModule() |
| 1081 | + } |
| 1082 | + |
| 1083 | + private predicate maybe_defined(SsaVariable var) { |
| 1084 | + exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete() |
| 1085 | + or |
| 1086 | + exists(SsaVariable input | |
| 1087 | + input = var.getAPhiInput() | |
| 1088 | + maybe_defined(input) |
| 1089 | + ) |
| 1090 | + } |
| 1091 | + |
| 1092 | + private predicate maybe_undefined(SsaVariable var) { |
| 1093 | + not exists(var.getDefinition()) and not py_ssa_phi(var, _) |
| 1094 | + or |
| 1095 | + var.getDefinition().isDelete() |
| 1096 | + or |
| 1097 | + maybe_undefined(var.getAPhiInput()) |
| 1098 | + or |
| 1099 | + exists(BasicBlock incoming | |
| 1100 | + exists(var.getAPhiInput()) and |
| 1101 | + incoming.getASuccessor() = var.getDefinition().getBasicBlock() and |
| 1102 | + not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) |
| 1103 | + ) |
| 1104 | + } |
| 1105 | + |
968 | 1106 | } |
969 | 1107 |
|
970 | 1108 | /** A basic block (ignoring exceptional flow edges to scope exit) */ |
|
0 commit comments