|
| 1 | +/** |
| 2 | + * Defines entity discard predicates for Python overlay analysis. |
| 3 | + */ |
| 4 | +overlay[local?] |
| 5 | +module; |
| 6 | + |
| 7 | +import python |
| 8 | + |
| 9 | +/*- Predicates -*/ |
| 10 | +/** |
| 11 | + * Holds always for the overlay variant and never for the base variant. |
| 12 | + * This local predicate is used to define local predicates that behave |
| 13 | + * differently for the base and overlay variant. |
| 14 | + */ |
| 15 | +overlay[local] |
| 16 | +predicate isOverlay() { databaseMetadata("isOverlay", "true") } |
| 17 | + |
| 18 | +overlay[local] |
| 19 | +private string getRawPathForLocation(@location loc) { |
| 20 | + exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result)) |
| 21 | + or |
| 22 | + exists(@py_Module mod | locations_ast(loc, mod, _, _, _, _) | result = getRawPathForModule(mod)) |
| 23 | +} |
| 24 | + |
| 25 | +overlay[local] |
| 26 | +private string getRawPathForModule(@py_Module mod) { |
| 27 | + exists(@container fileOrFolder | py_module_path(mod, fileOrFolder) | |
| 28 | + result = getRawPathForContainer(fileOrFolder) |
| 29 | + ) |
| 30 | +} |
| 31 | + |
| 32 | +overlay[local] |
| 33 | +private string getRawPathForContainer(@container fileOrFolder) { |
| 34 | + files(fileOrFolder, result) or folders(fileOrFolder, result) |
| 35 | +} |
| 36 | + |
| 37 | +/*- Source elements -*/ |
| 38 | +/** |
| 39 | + * An abstract base class for all elements that can be discarded from the base. |
| 40 | + */ |
| 41 | +overlay[local] |
| 42 | +abstract private class Discardable extends @py_source_element { |
| 43 | + /** Gets the path to the file in which this element occurs. */ |
| 44 | + abstract string getPath(); |
| 45 | + |
| 46 | + /** Holds if this element exists in the base variant. */ |
| 47 | + predicate existsInBase() { not isOverlay() and exists(this) } |
| 48 | + |
| 49 | + /** Holds if this element exists in the overlay variant. */ |
| 50 | + predicate existsInOverlay() { isOverlay() and exists(this) } |
| 51 | + |
| 52 | + /** Gets a textual representation of this discardable element. */ |
| 53 | + string toString() { none() } |
| 54 | +} |
| 55 | + |
| 56 | +overlay[discard_entity] |
| 57 | +private predicate discardEntity(@py_source_element el) { |
| 58 | + exists(Discardable d | d = el | |
| 59 | + overlayChangedFiles(d.getPath()) and |
| 60 | + d.existsInBase() and |
| 61 | + not d.existsInOverlay() |
| 62 | + ) |
| 63 | +} |
| 64 | + |
| 65 | +/** |
| 66 | + * Discard all locatable AST nodes (`@py_location_parent`) in modified files |
| 67 | + * since they use *-ids and hence cannot be referenced across TRAP files. |
| 68 | + */ |
| 69 | +overlay[local] |
| 70 | +final private class DiscardableLocatable extends Discardable instanceof @py_location_parent { |
| 71 | + override string getPath() { |
| 72 | + exists(@location loc | py_locations(loc, this) | result = getRawPathForLocation(loc)) |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +/** |
| 77 | + * Discard scopes (classes, functions, modules) that were deleted in the overlay. |
| 78 | + */ |
| 79 | +overlay[local] |
| 80 | +final private class DiscardableScope extends Discardable instanceof @py_scope { |
| 81 | + override string getPath() { |
| 82 | + exists(@location loc | py_scope_location(loc, this) | result = getRawPathForLocation(loc)) |
| 83 | + or |
| 84 | + result = getRawPathForModule(this) |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +/** |
| 89 | + * Discard files and folders that were deleted in the overlay. |
| 90 | + */ |
| 91 | +overlay[local] |
| 92 | +final private class DiscardableContainer extends Discardable instanceof @container { |
| 93 | + override string getPath() { result = getRawPathForContainer(this) } |
| 94 | +} |
| 95 | + |
| 96 | +/*- CFG Nodes -*/ |
| 97 | +/** Discardable control flow nodes */ |
| 98 | +overlay[local] |
| 99 | +final private class DiscardableCfgNode instanceof @py_flow_node { |
| 100 | + string getPath() { |
| 101 | + exists(Discardable d | result = d.getPath() | |
| 102 | + py_flow_bb_node(this, d.(@py_ast_node), _, _) |
| 103 | + or |
| 104 | + py_scope_flow(this, d.(@py_scope), _) |
| 105 | + ) |
| 106 | + } |
| 107 | + |
| 108 | + predicate existsInBase() { not isOverlay() and exists(this) } |
| 109 | + |
| 110 | + predicate existsInOverlay() { isOverlay() and exists(this) } |
| 111 | + |
| 112 | + string toString() { none() } |
| 113 | +} |
| 114 | + |
| 115 | +overlay[discard_entity] |
| 116 | +private predicate discardCfgNode(@py_flow_node n) { |
| 117 | + exists(DiscardableCfgNode d | d = n | |
| 118 | + overlayChangedFiles(d.getPath()) and |
| 119 | + d.existsInBase() and |
| 120 | + not d.existsInOverlay() |
| 121 | + ) |
| 122 | +} |
| 123 | + |
| 124 | +/*- Variables -*/ |
| 125 | +/** Discardable (normal and SSA) variables */ |
| 126 | +overlay[local] |
| 127 | +abstract private class DiscardableBaseVar instanceof @py_base_var { |
| 128 | + abstract string getPath(); |
| 129 | + |
| 130 | + predicate existsInBase() { not isOverlay() and exists(this) } |
| 131 | + |
| 132 | + predicate existsInOverlay() { isOverlay() and exists(this) } |
| 133 | + |
| 134 | + string toString() { none() } |
| 135 | +} |
| 136 | + |
| 137 | +overlay[discard_entity] |
| 138 | +private predicate discardVar(@py_base_var n) { |
| 139 | + exists(DiscardableVar d | d = n | |
| 140 | + overlayChangedFiles(d.getPath()) and |
| 141 | + d.existsInBase() and |
| 142 | + not d.existsInOverlay() |
| 143 | + ) |
| 144 | +} |
| 145 | + |
| 146 | +final private class DiscardableVar extends DiscardableBaseVar instanceof @py_variable { |
| 147 | + override string getPath() { |
| 148 | + exists(Discardable parent | result = parent.getPath() | |
| 149 | + variable(this, parent.(@py_scope), _) |
| 150 | + or |
| 151 | + py_variables(this, parent.(@py_variable_parent)) |
| 152 | + ) |
| 153 | + } |
| 154 | +} |
| 155 | + |
| 156 | +final private class DiscardableSsaVar extends DiscardableBaseVar instanceof @py_ssa_var { |
| 157 | + override string getPath() { |
| 158 | + exists(DiscardableSsaVar other | result = other.getPath() | |
| 159 | + py_ssa_phi(this, other.(@py_ssa_var)) |
| 160 | + ) |
| 161 | + or |
| 162 | + exists(DiscardableVar other | result = other.getPath() | py_ssa_var(this, other)) |
| 163 | + or |
| 164 | + exists(DiscardableCfgNode node | result = node.getPath() | |
| 165 | + py_ssa_use(node, this) |
| 166 | + or |
| 167 | + py_ssa_defn(this, node) |
| 168 | + ) |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +/*- Locations -*/ |
| 173 | +overlay[local] |
| 174 | +private predicate locationExistsInBase(@location loc) { not isOverlay() and exists(loc) } |
| 175 | + |
| 176 | +overlay[local] |
| 177 | +private predicate locationExistsInOverlay(@location loc) { isOverlay() and exists(loc) } |
| 178 | + |
| 179 | +overlay[discard_entity] |
| 180 | +private predicate discardLocation(@location loc) { |
| 181 | + overlayChangedFiles(getRawPathForLocation(loc)) and |
| 182 | + locationExistsInBase(loc) and |
| 183 | + not locationExistsInOverlay(loc) |
| 184 | +} |
| 185 | + |
| 186 | +/*- XML -*/ |
| 187 | +overlay[local] |
| 188 | +private predicate baseXmlLocatable(@xmllocatable l) { |
| 189 | + not isOverlay() and not files(l, _) and not xmlNs(l, _, _, _) |
| 190 | +} |
| 191 | + |
| 192 | +overlay[local] |
| 193 | +private predicate overlayHasXmlLocatable() { |
| 194 | + isOverlay() and |
| 195 | + exists(@xmllocatable l | not files(l, _) and not xmlNs(l, _, _, _)) |
| 196 | +} |
| 197 | + |
| 198 | +overlay[discard_entity] |
| 199 | +private predicate discardBaseXmlLocatable(@xmllocatable el) { |
| 200 | + // The XML extractor is currently not incremental, so if |
| 201 | + // the overlay contains any XML locatables, the overlay should |
| 202 | + // contain a full extraction and all XML locatables from base |
| 203 | + // should be discarded. |
| 204 | + baseXmlLocatable(el) and overlayHasXmlLocatable() |
| 205 | +} |
| 206 | + |
| 207 | +/*- YAML -*/ |
| 208 | +overlay[local] |
| 209 | +private predicate baseYamlLocatable(@yaml_locatable l) { not isOverlay() and exists(l) } |
| 210 | + |
| 211 | +overlay[local] |
| 212 | +private predicate overlayHasYamlLocatable() { |
| 213 | + isOverlay() and |
| 214 | + exists(@yaml_locatable l) |
| 215 | +} |
| 216 | + |
| 217 | +overlay[discard_entity] |
| 218 | +private predicate discardBaseYamlLocatable(@yaml_locatable el) { |
| 219 | + // The Yaml extractor is currently not incremental, so if |
| 220 | + // the overlay contains any Yaml locatables, the overlay should |
| 221 | + // contain a full extraction and all Yaml locatables from base |
| 222 | + // should be discarded. |
| 223 | + baseYamlLocatable(el) and overlayHasYamlLocatable() |
| 224 | +} |
0 commit comments