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 provides_self_access(self, node, unit): 80 81 """ 82 Return whether the 'node' in the given 'unit' provides a self-based 83 attribute access. 84 """ 85 86 attr_value = self.get_attribute_and_value(node._expr) 87 88 if attr_value: 89 target, value = attr_value 90 91 return target and target.name == "self" and target.parent is unit and \ 92 unit.is_method() 93 94 return False 95 96 def possible_accessor_types(self, node, defining_users=1): 97 98 """ 99 Given annotations made during the inspection process, return all possible 100 type names and indications of static usage for a 'node' involved in 101 attribute access. 102 103 If 'defining_users' is set to a false value, attempt to get the type 104 names specifically applicable to the node, rather than retrieving more 105 general definition-based type observations. 106 """ 107 108 all_target_names = [] 109 110 # Where an attribute could already be detected and where its nature is 111 # not that of a general instance or an unresolved name, attempt to 112 # identify it. 113 114 # Use explicit annotations on the node. 115 116 attrs = self.possible_attributes_from_annotation(node) 117 if attrs: 118 target_names = set() 119 for (attr, value) in attrs: 120 # NOTE: Ignoring constant objects. 121 if attr: 122 target_names.add((attr.parent.full_name(), attr.is_static_attribute())) 123 all_target_names.append(target_names) 124 125 # Use attribute usage observations. 126 127 target_names = self.possible_accessor_types_from_usage(node, defining_users) 128 if target_names: 129 all_target_names.append(target_names) 130 131 # Return the smallest set of target names. 132 133 all_target_names.sort(key=lambda x: len(x)) 134 135 return all_target_names and all_target_names[0] 136 137 def get_attribute_and_value(self, obj): 138 139 """ 140 Return (attribute, value) details for the given 'obj', where an 141 attribute of None can be returned for constant objects, and where None 142 can be returned as the result where no concrete details can be provided. 143 """ 144 145 if isinstance(obj, Constant): 146 return None, obj 147 148 if isinstance(obj, Attr): 149 return obj, obj.get_value() 150 151 return None 152 153 def possible_attributes_from_annotation(self, node): 154 155 """ 156 Return (attribute, value) details provided by any _expr or _attr 157 annotations on 'node'. 158 """ 159 160 attr_value = self.get_attribute_and_value(node._attr) 161 162 if attr_value: 163 return [attr_value] 164 165 attrs = set() 166 expr = node._expr 167 168 if expr: 169 170 # Permitting multiple expression types if they provide the 171 # attribute. 172 173 if isinstance(expr, Attr): 174 exprs = expr.get_values() 175 else: 176 exprs = [expr] 177 178 # For each expression value try and get a concrete 179 # attribute. 180 181 for expr in exprs: 182 attr = expr.all_attributes().get(node.attrname) 183 184 # Where an attribute can be obtained, record its 185 # details. 186 187 if attr: 188 attrs.add((attr, attr.get_value())) 189 190 return attrs 191 192 def possible_accessor_types_from_usage(self, node, defining_users=1): 193 194 """ 195 Return a set of (target name, static) tuples from an investigation of 196 attribute usage observations stored on the given 'node'. 197 198 If 'defining_users' is set to a false value, attempt to get the type 199 names specifically applicable to the node, rather than retrieving more 200 general definition-based type observations. 201 """ 202 203 target_names = set() 204 205 if node._attrusers: 206 207 # Visit each attribute user. 208 209 for user in node._attrusers: 210 211 # Since users such as branches may not provide type information, 212 # attempt to find defining users. 213 214 if defining_users: 215 for def_user in user._attrdefs or [user]: 216 for target_name, is_static in def_user._attrtypes.get(node._username, []): 217 target_names.add((target_name, is_static)) 218 else: 219 for target_name, is_static in user._attrspecifictypes.get(node._username, []): 220 target_names.add((target_name, is_static)) 221 222 return target_names 223 224 def possible_accessors_from_usage(self, node, defining_users=1): 225 226 """ 227 Return possible accessors from the usage recorded on the given 'node'. 228 229 If 'defining_users' is set to a false value, attempt to get the type 230 names specifically applicable to the node, rather than retrieving more 231 general definition-based type observations. 232 """ 233 234 targets = set() 235 target_names = self.possible_accessor_types_from_usage(node, defining_users) 236 237 if target_names: 238 for target_name, is_static in target_names: 239 targets.add(self.objtable.get_object(target_name)) 240 241 return targets 242 243 def used_by_unit(node): 244 245 """ 246 Return whether the definition made by a 'node' is actually employed by the 247 program unit within which it is found. 248 """ 249 250 return node.unit and node.unit.parent.has_key(node.unit.name) 251 252 # Useful data. 253 254 operator_functions = { 255 256 # Binary operations. 257 258 "Add" : "add", 259 "Bitand" : "and_", 260 "Bitor" : "or_", 261 "Bitxor" : "xor", 262 "Div" : "div", 263 "FloorDiv" : "floordiv", 264 "LeftShift" : "lshift", 265 "Mod" : "mod", 266 "Mul" : "mul", 267 "Power" : "pow", 268 "RightShift" : "rshift", 269 "Sub" : "sub", 270 271 # Unary operations. 272 273 "Invert" : "invert", 274 "UnaryAdd" : "pos", 275 "UnarySub" : "neg", 276 277 # Augmented assignment. 278 279 "+=" : "iadd", 280 "-=" : "isub", 281 "*=" : "imul", 282 "/=" : "idiv", 283 "//=" : "ifloordiv", 284 "%=" : "imod", 285 "**=" : "ipow", 286 "<<=" : "ilshift", 287 ">>=" : "irshift", 288 "&=" : "iand", 289 "^=" : "ixor", 290 "|=" : "ior", 291 292 # Comparisons. 293 294 "==" : "eq", 295 "!=" : "ne", 296 "<" : "lt", 297 "<=" : "le", 298 ">=" : "ge", 299 ">" : "gt", 300 301 # Access and slicing. 302 303 "AssSlice" : "setslice", # Python 2.7 304 "Slice" : "getslice", 305 "AssSubscript" : "setitem", # Python 2.7 306 "Subscript" : "getitem", 307 } 308 309 # vim: tabstop=4 expandtab shiftwidth=4