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