1 #!/usr/bin/env python 2 3 """ 4 Common classes. 5 6 Copyright (C) 2007, 2008, 2009, 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.stdcompiler import compiler 23 from compiler.ast import AssAttr, Getattr, Name 24 from micropython.basicdata import Const, Constant, TypedInstance 25 from micropython.data import BaseAttr, Class, Module 26 from micropython.errors import * 27 from os.path import split 28 29 try: 30 set 31 except NameError: 32 from sets import Set as set 33 34 # Visitors and activities related to node annotations. 35 36 class ASTVisitor: 37 38 "A base class for visitors." 39 40 def process_definitions(self, node): 41 42 """ 43 Process and return all definitions beneath 'node', but not definitions 44 within definitions. 45 """ 46 47 definitions = [] 48 for n in node.getChildNodes(): 49 if isinstance(n, (compiler.ast.Class, compiler.ast.Function)): 50 self.current_definition = n 51 definitions.append(self.dispatch(n)) 52 else: 53 definitions += self.process_definitions(n) 54 return definitions 55 56 def processing_definition(self, n): 57 return self.current_definition is n 58 59 # Visitor support methods. 60 61 def default(self, node, *args): 62 for n in node.getChildNodes(): 63 self.dispatch(n) 64 65 def dispatch(self, node, *args): 66 67 "Dispatch using 'node', annotating any raised exceptions." 68 69 # Dispatch via a generic visit method. 70 71 try: 72 return node.visit(self, *args) 73 74 # Annotate the exception in case of failure. 75 76 except NodeProcessingError, exc: 77 if exc.astnode is None: 78 exc.astnode = node 79 exc.unit_name = self.get_unit().full_name() 80 raise 81 82 # Deduction-related methods. 83 84 def get_attributes(self, targets, attrname): 85 86 "Return a list of attributes for 'targets' supporting 'attrname'." 87 88 attributes = set() 89 90 for target in targets: 91 try: 92 attributes.add(self.objtable.access(target.full_name(), attrname)) 93 except TableError: 94 pass 95 96 return attributes 97 98 def get_attribute_and_value(self, obj): 99 100 """ 101 Return (attribute, value) details for the given 'obj', where an 102 attribute of None can be returned for constant objects, and where None 103 can be returned as the result where no concrete details can be provided. 104 """ 105 106 if isinstance(obj, (Constant, TypedInstance)): 107 return None, obj 108 109 if isinstance(obj, BaseAttr): 110 return obj, obj.get_value() 111 112 return None 113 114 def provides_constant_result(self, value): 115 116 "Return whether 'value' provides a constant result." 117 118 return isinstance(value, (Const, Constant)) 119 120 def provides_self_access(self, expr, unit): 121 122 """ 123 Return whether the 'expr' in the given 'unit' provides a self-based 124 attribute access. 125 """ 126 127 attr_value = self.get_attribute_and_value(expr) 128 129 if attr_value: 130 target, value = attr_value 131 132 return target and target.name == "self" and target.parent is unit and \ 133 unit.is_method() 134 135 return False 136 137 def possible_attributes_from_annotation(self, expr, attr, attrname): 138 139 """ 140 Return (attribute, value) details provided by the 'expr' or 'attr' 141 annotations on a node for an access involving 'attrname'. 142 """ 143 144 attr_value = self.get_attribute_and_value(attr) 145 146 if attr_value: 147 return [attr_value] 148 149 attrs = set() 150 151 if expr: 152 153 # Permitting multiple expression types if they provide the 154 # attribute. 155 156 if isinstance(expr, BaseAttr): 157 exprs = expr.get_values() 158 else: 159 exprs = [expr] 160 161 # For each expression value try and get a concrete 162 # attribute. 163 164 for expr in exprs: 165 found_attr = expr.all_attributes().get(attrname) 166 167 # Where an attribute can be obtained, record its 168 # details. 169 170 if found_attr: 171 attrs.add((found_attr, found_attr.get_value())) 172 173 return attrs 174 175 def possible_accessor_types_from_usage(self, node, defining_users=1): 176 177 """ 178 Return a set of (target name, static) tuples from an investigation of 179 attribute usage observations stored on the given 'node'. 180 181 If 'defining_users' is set to a false value, attempt to get the type 182 names specifically applicable to the node, rather than retrieving more 183 general definition-based type observations. 184 """ 185 186 target_names = set() 187 188 if node._attrusers: 189 190 # Visit each attribute user. 191 192 for user in node._attrusers: 193 194 # Since users such as branches may not provide type information, 195 # attempt to find defining users. 196 197 if defining_users: 198 for def_user in user._attrdefs or [user]: 199 for target_name, is_static in def_user._attrtypes.get(node._username, []): 200 target_names.add((target_name, is_static)) 201 else: 202 for target_name, is_static in user._attrspecifictypes.get(node._username, []): 203 target_names.add((target_name, is_static)) 204 205 return target_names 206 207 def possible_accessors_from_usage(self, node, defining_users=1): 208 209 """ 210 Return possible accessors from the usage recorded on the given 'node'. 211 212 If 'defining_users' is set to a false value, attempt to get the type 213 names specifically applicable to the node, rather than retrieving more 214 general definition-based type observations. 215 """ 216 217 targets = set() 218 target_names = self.possible_accessor_types_from_usage(node, defining_users) 219 220 if target_names: 221 for target_name, is_static in target_names: 222 targets.add(self.objtable.get_object(target_name)) 223 224 return targets 225 226 def possible_accessors_for_attribute(self, attrname): 227 228 "Return possible accessors given the single 'attrname'." 229 230 targets = set() 231 232 for target_name in self.objtable.any_possible_objects([attrname]): 233 targets.add(self.objtable.get_object(target_name)) 234 235 return targets 236 237 def get_module_name(node, module): 238 239 """ 240 Using the given From 'node' and 'module' in which it is found, calculate 241 any relative import information, returning a tuple containing a module 242 to import along with any names to import based on the node's name 243 information. 244 245 Where the returned module is given as None, whole module imports should 246 be performed for the returned modules using the returned names. 247 """ 248 249 # Absolute import. 250 251 if node.level == 0: 252 return node.modname, node.names 253 254 # Relative to an ancestor of this module. 255 256 else: 257 path = module.full_name().split(".") 258 level = node.level 259 260 # Relative imports treat package roots as submodules. 261 262 if split(module.filename)[-1] == "__init__.py": 263 level -= 1 264 265 if level > len(path): 266 raise InspectError("Relative import %r involves too many levels up from module %r" % ( 267 ("%s%s" % ("." * node.level, node.modname or "")), module.full_name())) 268 269 basename = ".".join(path[:len(path)-level]) 270 271 # Name imports from a module. 272 273 if node.modname: 274 return "%s.%s" % (basename, node.modname), node.names 275 276 # Relative whole module imports. 277 278 else: 279 return None, [("%s.%s" % (basename, name), alias or name) for name, alias in node.names] 280 281 def used_by_unit(node): 282 283 """ 284 Return whether the definition made by a 'node' is actually employed by the 285 program unit within which it is found. 286 """ 287 288 return node.unit and node.unit.parent.has_key(node.unit.original_name) 289 290 # Useful data. 291 292 operator_functions = { 293 294 # Fundamental operations. 295 296 "in" : "in_", 297 "not in" : "not_in", 298 299 # Binary operations. 300 301 "Add" : "add", 302 "Bitand" : "and_", 303 "Bitor" : "or_", 304 "Bitxor" : "xor", 305 "Div" : "div", 306 "FloorDiv" : "floordiv", 307 "LeftShift" : "lshift", 308 "Mod" : "mod", 309 "Mul" : "mul", 310 "Power" : "pow", 311 "RightShift" : "rshift", 312 "Sub" : "sub", 313 314 # Unary operations. 315 316 "Invert" : "invert", 317 "UnaryAdd" : "pos", 318 "UnarySub" : "neg", 319 320 # Augmented assignment. 321 322 "+=" : "iadd", 323 "-=" : "isub", 324 "*=" : "imul", 325 "/=" : "idiv", 326 "//=" : "ifloordiv", 327 "%=" : "imod", 328 "**=" : "ipow", 329 "<<=" : "ilshift", 330 ">>=" : "irshift", 331 "&=" : "iand", 332 "^=" : "ixor", 333 "|=" : "ior", 334 335 # Comparisons. 336 337 "==" : "eq", 338 "!=" : "ne", 339 "<" : "lt", 340 "<=" : "le", 341 ">=" : "ge", 342 ">" : "gt", 343 344 # Access and slicing. 345 346 "AssSlice" : "setslice", # Python 2.7 347 "Slice" : "getslice", 348 "AssSubscript" : "setitem", # Python 2.7 349 "Subscript" : "getitem", 350 } 351 352 # vim: tabstop=4 expandtab shiftwidth=4