1 #!/usr/bin/env python 2 3 """ 4 Perform deductions on an inspected program. 5 6 Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from micropython.common import * 23 from micropython.data import * 24 from micropython.errors import * 25 import compiler.ast 26 27 # Source code classes. 28 29 class DeducedSource(ASTVisitor): 30 31 "A module upon which deductions of code behaviour are made." 32 33 def __init__(self, module, program): 34 self.visitor = self 35 self.module = module 36 self.program = program 37 self.objtable = program.get_object_table() 38 self.units = [] 39 40 def get_unit(self): 41 return self.units[-1] 42 43 def get_module(self): 44 return self.units[0] 45 46 def deduce(self): 47 48 "Process the module, making deductions." 49 50 self.dispatch(self.module.astnode) 51 52 def dispatch(self, node, *args): 53 54 "NOTE: From compiler.visitor.ASTVisitor." 55 56 try: 57 return node.visit(self.visitor, *args) 58 except AttributeError: 59 # NOTE: Obligatory hack to find real attribute errors. 60 if isinstance(node, (Getattr, AssAttr)): 61 raise 62 return self.visitor.default(node, *args) 63 64 def _visitUnit(self, node): 65 66 """ 67 Track entry into program units in order to support various attribute 68 access operations. 69 """ 70 71 self.units.append(node.unit) 72 self.dispatch(node.node) 73 self.units.pop() 74 75 def _visitOptionalUnit(self, node): 76 77 "Optionally visit a unit, depending on whether it is used." 78 79 if not used_by_unit(node): 80 return 81 self._visitUnit(node) 82 83 visitModule = _visitUnit 84 visitClass = visitFunction = _visitOptionalUnit 85 86 def _visitAttr(self, node): 87 88 """ 89 Perform deductions on attribute accesses, adding annotations to the node 90 that can be used by subsequent activities. 91 """ 92 93 unit = self.get_unit() 94 95 # Remember to permit deductions on the expression node. Here, we may 96 # also obtain a concrete type associated with an instantiation. 97 98 expr_type = self.dispatch(node.expr) 99 if not node._expr or isinstance(node._expr, Instance): 100 node._expr = expr_type 101 102 # The target, on which the access is performed, may influence the effect 103 # on the context. We can only reliably assume that a literal constant is 104 # an instance: all other "instances" may actually be classes in certain 105 # cases. 106 107 target = node._expr 108 instance_target = isinstance(target, Const) 109 110 # Attempt to deduce attributes from explicit annotations. 111 112 node._attrs_deduced = attrs = self.possible_attributes_from_annotation(node) 113 114 if len(attrs) == 1: 115 for attr, value in attrs: 116 117 # Constant values can be obtained directly. 118 119 if self.provides_constant_result(value): 120 node._access_type = "constant" 121 node._value_deduced = value 122 return 123 124 # Static attributes can be obtained via their parent. 125 126 if attr.is_static_attribute(): 127 node._access_type = "static" 128 node._attr_deduced = attr 129 node._set_context = instance_target and "set" or None 130 return 131 132 # If a reliable target was originally specified, any usable attributes 133 # should have been detected above, and any attributes deduced by other 134 # means will not be compatible with the target. Thus, the nature of the 135 # target is investigated: it must be an inspectable namespace or be an 136 # attribute only providing such namespaces; otherwise, it is possible 137 # that deduced attributes might be appropriate. 138 139 if target and ( 140 isinstance(target, (Class, Module)) or 141 isinstance(target, Attr) and not [v for v in target.get_values() if not isinstance(v, (Class, Module))] 142 ): 143 node._access_type = "impossible" 144 return 145 146 # Attributes of self, which is by definition an instance. 147 148 if self.provides_self_access(node, unit): 149 150 # Find instance attributes. 151 152 attr = unit.parent.instance_attributes().get(node.attrname) 153 attrs = filter(None, [cls.instance_attributes().get(node.attrname) for cls in unit.parent.descendants]) 154 155 # A "leaf" class whose instances provide an attribute. 156 157 if attr and not attrs: 158 node._access_type = "instance" 159 node._attr_deduced = attr 160 return 161 162 # A class where instances of subclasses may provide an attribute. 163 164 elif attrs: 165 if attr: 166 attrs.append(attr) 167 168 node._attrs_deduced = [(a, a.get_value()) for a in attrs] 169 170 # The instances may provide the attribute at the same position. 171 172 positions = set([a.position for a in attrs]) 173 if len(positions) == 1: 174 for position in positions: 175 node._access_type = "positioned" 176 node._position_deduced = position 177 return 178 179 # Otherwise, accessing the attributes is more work. 180 181 node._access_type = "instance" 182 return 183 184 # Find class attributes. 185 # The context will be overridden for compatible class attributes 186 # only. 187 188 attr = unit.parent.get(node.attrname) 189 190 if attr: 191 192 # Constant attributes. 193 194 if attr.is_strict_constant(): 195 if self.provides_constant_result(attr.get_value()): 196 node._access_type = "constant" 197 node._value_deduced = attr.get_value() 198 return 199 200 # Compatible class attributes. 201 202 if attr.defined_within_hierarchy(): 203 node._access_type = "static" 204 node._attr_deduced = attr 205 node._set_context = "set" 206 return 207 208 # Incompatible class attributes. 209 210 elif attr.defined_outside_hierarchy(): 211 node._access_type = "static" 212 node._attr_deduced = attr 213 return 214 215 # Unknown or mixed compatibility. 216 217 node._access_type = "static" 218 node._attr_deduced = attr 219 node._set_context = "cond" 220 return 221 222 # Usage observations, both specific to this node's region of the program 223 # and also applicable to the lifespan of the affected name. 224 225 specific_targets = self.possible_accessors_from_usage(node, defining_users=0) 226 targets = self.possible_accessors_from_usage(node, defining_users=1) 227 228 # Record whether types were already deduced. If not, get types using 229 # only this attribute. 230 231 if not specific_targets or not targets: 232 attribute_targets = self.possible_accessors_for_attribute(node.attrname) 233 if not specific_targets: 234 specific_targets = attribute_targets 235 if not targets: 236 targets = attribute_targets 237 238 # Get the attributes from the deduced targets. 239 240 node._attrs_deduced_from_specific_usage = self.get_attributes(specific_targets, node.attrname) 241 node._attrs_deduced_from_usage = attrs = self.get_attributes(targets, node.attrname) 242 243 # Generate optimisations where only a single attribute applies. 244 245 if attrs and len(attrs) == 1: 246 for attr in attrs: 247 248 # Static attributes, but potentially non-static targets. 249 250 if attr.is_static_attribute(): 251 252 # Static attributes may be accompanied by a different context 253 # depending on the accessor. 254 # NOTE: Should determine whether the context is always replaced. 255 256 node._access_type = "static" 257 node._attr_deduced = attr 258 node._set_context = instance_target and "set" or "cond" 259 return 260 261 # Non-static attributes. 262 263 node._access_type = "instance" 264 node._attr_deduced = attr 265 return 266 267 # Test for compatible attribute positioning. 268 269 elif attrs: 270 positions = set([(attr.is_static_attribute(), attr.position) for attr in attrs]) 271 272 # Permit a position-based access only on non-static attributes since 273 # access to static attributes may happen via instances and thus not 274 # be relative to the accessor but to its parent. 275 276 if len(positions) == 1: 277 for position in positions: 278 if not position[0]: 279 node._access_type = "positioned" 280 node._position_deduced = position[0] 281 return 282 283 # With no usable deductions, generate a table-based access. 284 285 node._access_type = "unknown" 286 node._set_context = "cond" 287 288 visitAssAttr = visitGetattr = _visitAttr 289 290 def visitCallFunc(self, node): 291 292 "Identify any concrete types involved with instantiation." 293 294 for n in node.getChildNodes(): 295 self.dispatch(n) 296 297 # Determine whether the target of the invocation refers to a class. 298 299 attr = node.node._attr 300 301 if attr: 302 value = attr.get_value() 303 if value and isinstance(value, Class): 304 return attr 305 306 # Convenience functions. 307 308 def deduce(program): 309 for module in program.get_importer().get_modules(): 310 DeducedSource(module, program).deduce() 311 312 # vim: tabstop=4 expandtab shiftwidth=4