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, node, unit): 121 122 """ 123 Return whether the 'node' in the given 'unit' provides a self-based 124 attribute access. 125 """ 126 127 attr_value = self.get_attribute_and_value(node._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, node): 138 139 """ 140 Return (attribute, value) details provided by any _expr or _attr 141 annotations on 'node'. 142 """ 143 144 attr_value = self.get_attribute_and_value(node._attr) 145 146 if attr_value: 147 return [attr_value] 148 149 attrs = set() 150 expr = node._expr 151 152 if expr: 153 154 # Permitting multiple expression types if they provide the 155 # attribute. 156 157 if isinstance(expr, BaseAttr): 158 exprs = expr.get_values() 159 else: 160 exprs = [expr] 161 162 # For each expression value try and get a concrete 163 # attribute. 164 165 for expr in exprs: 166 attr = expr.all_attributes().get(node.attrname) 167 168 # Where an attribute can be obtained, record its 169 # details. 170 171 if attr: 172 attrs.add((attr, attr.get_value())) 173 174 return attrs 175 176 def possible_accessor_types_from_usage(self, node, defining_users=1): 177 178 """ 179 Return a set of (target name, static) tuples from an investigation of 180 attribute usage observations stored on the given 'node'. 181 182 If 'defining_users' is set to a false value, attempt to get the type 183 names specifically applicable to the node, rather than retrieving more 184 general definition-based type observations. 185 """ 186 187 target_names = set() 188 189 if node._attrusers: 190 191 # Visit each attribute user. 192 193 for user in node._attrusers: 194 195 # Since users such as branches may not provide type information, 196 # attempt to find defining users. 197 198 if defining_users: 199 for def_user in user._attrdefs or [user]: 200 for target_name, is_static in def_user._attrtypes.get(node._username, []): 201 target_names.add((target_name, is_static)) 202 else: 203 for target_name, is_static in user._attrspecifictypes.get(node._username, []): 204 target_names.add((target_name, is_static)) 205 206 return target_names 207 208 def possible_accessors_from_usage(self, node, defining_users=1): 209 210 """ 211 Return possible accessors from the usage recorded on the given 'node'. 212 213 If 'defining_users' is set to a false value, attempt to get the type 214 names specifically applicable to the node, rather than retrieving more 215 general definition-based type observations. 216 """ 217 218 targets = set() 219 target_names = self.possible_accessor_types_from_usage(node, defining_users) 220 221 if target_names: 222 for target_name, is_static in target_names: 223 targets.add(self.objtable.get_object(target_name)) 224 225 return targets 226 227 def possible_accessors_for_attribute(self, attrname): 228 229 "Return possible accessors given the single 'attrname'." 230 231 targets = set() 232 233 for target_name in self.objtable.any_possible_objects([attrname]): 234 targets.add(self.objtable.get_object(target_name)) 235 236 return targets 237 238 def get_module_name(node, module): 239 240 """ 241 Using the given From 'node' and 'module' in which it is found, calculate 242 any relative import information, returning a tuple containing a module 243 to import along with any names to import based on the node's name 244 information. 245 246 Where the returned module is given as None, whole module imports should 247 be performed for the returned modules using the returned names. 248 """ 249 250 # Absolute import. 251 252 if node.level == 0: 253 return node.modname, node.names 254 255 # Relative to an ancestor of this module. 256 257 else: 258 path = module.full_name().split(".") 259 level = node.level 260 261 # Relative imports treat package roots as submodules. 262 263 if split(module.filename)[-1] == "__init__.py": 264 level -= 1 265 266 if level > len(path): 267 raise InspectError("Relative import %r involves too many levels up from module %r" % ( 268 ("%s%s" % ("." * node.level, node.modname or "")), module.full_name())) 269 270 basename = ".".join(path[:len(path)-level]) 271 272 # Name imports from a module. 273 274 if node.modname: 275 return "%s.%s" % (basename, node.modname), node.names 276 277 # Relative whole module imports. 278 279 else: 280 return None, [("%s.%s" % (basename, name), alias or name) for name, alias in node.names] 281 282 def used_by_unit(node): 283 284 """ 285 Return whether the definition made by a 'node' is actually employed by the 286 program unit within which it is found. 287 """ 288 289 return node.unit and node.unit.parent.has_key(node.unit.original_name) 290 291 # Useful data. 292 293 operator_functions = { 294 295 # Fundamental operations. 296 297 "in" : "in_", 298 "not in" : "not_in", 299 300 # Binary operations. 301 302 "Add" : "add", 303 "Bitand" : "and_", 304 "Bitor" : "or_", 305 "Bitxor" : "xor", 306 "Div" : "div", 307 "FloorDiv" : "floordiv", 308 "LeftShift" : "lshift", 309 "Mod" : "mod", 310 "Mul" : "mul", 311 "Power" : "pow", 312 "RightShift" : "rshift", 313 "Sub" : "sub", 314 315 # Unary operations. 316 317 "Invert" : "invert", 318 "UnaryAdd" : "pos", 319 "UnarySub" : "neg", 320 321 # Augmented assignment. 322 323 "+=" : "iadd", 324 "-=" : "isub", 325 "*=" : "imul", 326 "/=" : "idiv", 327 "//=" : "ifloordiv", 328 "%=" : "imod", 329 "**=" : "ipow", 330 "<<=" : "ilshift", 331 ">>=" : "irshift", 332 "&=" : "iand", 333 "^=" : "ixor", 334 "|=" : "ior", 335 336 # Comparisons. 337 338 "==" : "eq", 339 "!=" : "ne", 340 "<" : "lt", 341 "<=" : "le", 342 ">=" : "ge", 343 ">" : "gt", 344 345 # Access and slicing. 346 347 "AssSlice" : "setslice", # Python 2.7 348 "Slice" : "getslice", 349 "AssSubscript" : "setitem", # Python 2.7 350 "Subscript" : "getitem", 351 } 352 353 # vim: tabstop=4 expandtab shiftwidth=4