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 return None 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_accessor_types(self, node, defining_users=1): 133 134 """ 135 Given annotations made during the inspection process, return all possible 136 type names and indications of static usage for a 'node' involved in 137 attribute access. 138 139 If 'defining_users' is set to a false value, attempt to get the type 140 names specifically applicable to the node, rather than retrieving more 141 general definition-based type observations. 142 """ 143 144 all_target_names = [] 145 146 # Where an attribute could already be detected and where its nature is 147 # not that of a general instance or an unresolved name, attempt to 148 # identify it. 149 150 # Use explicit annotations on the node. 151 152 attrs = self.possible_attributes_from_annotation(node) 153 if attrs: 154 target_names = set() 155 for (attr, value) in attrs: 156 # NOTE: Ignoring constant objects. 157 if attr: 158 target_names.add((attr.parent.full_name(), attr.is_static_attribute())) 159 all_target_names.append(target_names) 160 161 # Use attribute usage observations. 162 163 target_names = self.possible_accessor_types_from_usage(node, defining_users) 164 if target_names: 165 all_target_names.append(target_names) 166 167 # Return the smallest set of target names. 168 169 all_target_names.sort(key=lambda x: len(x)) 170 171 return all_target_names and all_target_names[0] 172 173 def possible_attributes_from_annotation(self, node): 174 175 """ 176 Return (attribute, value) details provided by any _expr or _attr 177 annotations on 'node'. 178 """ 179 180 attr_value = self.get_attribute_and_value(node._attr) 181 182 if attr_value: 183 return [attr_value] 184 185 attrs = set() 186 expr = node._expr 187 188 if expr: 189 190 # Permitting multiple expression types if they provide the 191 # attribute. 192 193 if isinstance(expr, Attr): 194 exprs = expr.get_values() 195 else: 196 exprs = [expr] 197 198 # For each expression value try and get a concrete 199 # attribute. 200 201 for expr in exprs: 202 attr = expr.all_attributes().get(node.attrname) 203 204 # Where an attribute can be obtained, record its 205 # details. 206 207 if attr: 208 attrs.add((attr, attr.get_value())) 209 210 return attrs 211 212 def possible_accessor_types_from_usage(self, node, defining_users=1): 213 214 """ 215 Return a set of (target name, static) tuples from an investigation of 216 attribute usage observations stored on the given 'node'. 217 218 If 'defining_users' is set to a false value, attempt to get the type 219 names specifically applicable to the node, rather than retrieving more 220 general definition-based type observations. 221 """ 222 223 target_names = set() 224 225 if node._attrusers: 226 227 # Visit each attribute user. 228 229 for user in node._attrusers: 230 231 # Since users such as branches may not provide type information, 232 # attempt to find defining users. 233 234 if defining_users: 235 for def_user in user._attrdefs or [user]: 236 for target_name, is_static in def_user._attrtypes.get(node._username, []): 237 target_names.add((target_name, is_static)) 238 else: 239 for target_name, is_static in user._attrspecifictypes.get(node._username, []): 240 target_names.add((target_name, is_static)) 241 242 return target_names 243 244 def possible_accessors_from_usage(self, node, defining_users=1): 245 246 """ 247 Return possible accessors from the usage recorded on the given 'node'. 248 249 If 'defining_users' is set to a false value, attempt to get the type 250 names specifically applicable to the node, rather than retrieving more 251 general definition-based type observations. 252 """ 253 254 targets = set() 255 target_names = self.possible_accessor_types_from_usage(node, defining_users) 256 257 if target_names: 258 for target_name, is_static in target_names: 259 targets.add(self.objtable.get_object(target_name)) 260 261 return targets 262 263 def possible_accessors_for_attribute(self, attrname): 264 265 "Return possible accessors given the single 'attrname'." 266 267 targets = set() 268 269 for target_name in self.objtable.any_possible_objects([attrname]): 270 targets.add(self.objtable.get_object(target_name)) 271 272 return targets 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.name) 282 283 # Useful data. 284 285 operator_functions = { 286 287 # Binary operations. 288 289 "Add" : "add", 290 "Bitand" : "and_", 291 "Bitor" : "or_", 292 "Bitxor" : "xor", 293 "Div" : "div", 294 "FloorDiv" : "floordiv", 295 "LeftShift" : "lshift", 296 "Mod" : "mod", 297 "Mul" : "mul", 298 "Power" : "pow", 299 "RightShift" : "rshift", 300 "Sub" : "sub", 301 302 # Unary operations. 303 304 "Invert" : "invert", 305 "UnaryAdd" : "pos", 306 "UnarySub" : "neg", 307 308 # Augmented assignment. 309 310 "+=" : "iadd", 311 "-=" : "isub", 312 "*=" : "imul", 313 "/=" : "idiv", 314 "//=" : "ifloordiv", 315 "%=" : "imod", 316 "**=" : "ipow", 317 "<<=" : "ilshift", 318 ">>=" : "irshift", 319 "&=" : "iand", 320 "^=" : "ixor", 321 "|=" : "ior", 322 323 # Comparisons. 324 325 "==" : "eq", 326 "!=" : "ne", 327 "<" : "lt", 328 "<=" : "le", 329 ">=" : "ge", 330 ">" : "gt", 331 332 # Access and slicing. 333 334 "AssSlice" : "setslice", # Python 2.7 335 "Slice" : "getslice", 336 "AssSubscript" : "setitem", # Python 2.7 337 "Subscript" : "getitem", 338 } 339 340 # vim: tabstop=4 expandtab shiftwidth=4