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