1 #!/usr/bin/env python 2 3 """ 4 Perform deductions on an inspected program. 5 6 Copyright (C) 2006, 2007, 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.common import * 23 from micropython.data import * 24 from micropython.errors import * 25 import compiler.ast 26 27 # Source code classes. 28 29 class DeducedSource(ASTVisitor): 30 31 "A module upon which deductions of code behaviour are made." 32 33 def __init__(self, module, program): 34 self.visitor = self 35 self.module = module 36 self.program = program 37 self.objtable = program.get_object_table() 38 self.units = [] 39 40 def get_unit(self): 41 return self.units[-1] 42 43 def get_module(self): 44 return self.units[0] 45 46 def deduce(self): 47 48 "Process the module, making deductions." 49 50 self.dispatch(self.module.astnode) 51 52 def dispatch(self, node, *args): 53 54 "NOTE: From compiler.visitor.ASTVisitor." 55 56 try: 57 return node.visit(self.visitor, *args) 58 except AttributeError: 59 # NOTE: Obligatory hack to find real attribute errors. 60 if isinstance(node, (Getattr, AssAttr)): 61 raise 62 return self.visitor.default(node, *args) 63 64 def _visitUnit(self, node): 65 66 """ 67 Track entry into program units in order to support various attribute 68 access operations. 69 """ 70 71 if not used_by_unit(node): 72 return 73 74 self.units.append(node.unit) 75 self.dispatch(node.node) 76 self.units.pop() 77 78 visitModule = visitClass = visitFunction = _visitUnit 79 80 def _visitAttr(self, node): 81 82 """ 83 Perform deductions on attribute accesses, adding annotations to the node 84 that can be used by subsequent activities. 85 """ 86 87 unit = self.get_unit() 88 89 # The target, on which the access is performed, may influence the effect 90 # on the context. We can only reliably assume that a literal constant is 91 # an instance: all other "instances" may actually be classes in certain 92 # cases. 93 94 target = node._expr 95 instance_target = isinstance(target, Const) 96 97 # Attempt to deduce attributes from explicit annotations. 98 99 node._attrs_deduced = attrs = self.possible_attributes_from_annotation(node) 100 101 if len(attrs) == 1: 102 for attr, value in attrs: 103 104 # Constant values can be obtained directly. 105 106 if self.provides_constant_result(value): 107 node._access_type = "constant" 108 node._value_deduced = value 109 return 110 111 # Static attributes can be obtained via their parent. 112 113 if attr.is_static_attribute(): 114 node._access_type = "static" 115 node._attr_deduced = attr 116 node._set_context = instance_target and "set" or None 117 return 118 119 # Attributes of self, which is by definition an instance. 120 121 if self.provides_self_access(node, unit): 122 123 # Find instance attributes. 124 125 attr = unit.parent.instance_attributes().get(node.attrname) 126 127 if attr: 128 node._access_type = "instance" 129 node._attr_deduced = attr 130 return 131 132 # Find class attributes. 133 # The context will be overridden for compatible class attributes 134 # only. 135 136 attr = unit.parent.get(node.attrname) 137 138 if attr: 139 140 # Constant attributes. 141 142 if attr.is_strict_constant(): 143 if self.provides_constant_result(attr.get_value()): 144 node._access_type = "constant" 145 node._value_deduced = attr.get_value() 146 return 147 148 # Compatible class attributes. 149 150 if attr.defined_within_hierarchy(): 151 node._access_type = "static" 152 node._attr_deduced = attr 153 node._set_context = "set" 154 return 155 156 # Incompatible class attributes. 157 158 elif attr.defined_outside_hierarchy(): 159 node._access_type = "static" 160 node._attr_deduced = attr 161 return 162 163 # Unknown or mixed compatibility. 164 165 node._access_type = "static" 166 node._attr_deduced = attr 167 node._set_context = "cond" 168 return 169 170 # Usage observations, both specific to this node's region of the program 171 # and also applicable to the lifespan of the affected name. 172 173 specific_targets = self.possible_accessors_from_usage(node, defining_users=0) 174 targets = self.possible_accessors_from_usage(node, defining_users=1) 175 176 # Record whether types were already deduced. If not, get types using 177 # only this attribute. 178 179 if not specific_targets or not targets: 180 attribute_targets = self.possible_accessors_for_attribute(node.attrname) 181 if not specific_targets: 182 specific_targets = attribute_targets 183 if not targets: 184 targets = attribute_targets 185 186 node._attrs_deduced_from_specific_usage = self.get_attributes(specific_targets, node.attrname) 187 node._attrs_deduced_from_usage = attrs = self.get_attributes(targets, node.attrname) 188 189 # Generate optimisations where only a single attribute applies. 190 191 if attrs and len(attrs) == 1: 192 for attr in attrs: 193 194 # Static attributes, but potentially non-static targets. 195 196 if attr.is_static_attribute(): 197 198 # Static attributes may be accompanied by a different context 199 # depending on the accessor. 200 # NOTE: Should determine whether the context is always replaced. 201 202 node._access_type = "static" 203 node._attr_deduced = attr 204 node._set_context = instance_target and "set" or "cond" 205 return 206 207 # Non-static attributes. 208 209 node._access_type = "instance" 210 node._attr_deduced = attr 211 return 212 213 # Test for compatible attribute positioning. 214 215 elif attrs: 216 positions = set([(attr.is_static_attribute(), attr.position) for attr in attrs]) 217 218 # Permit a position-based access only on non-static attributes since 219 # access to static attributes may happen via instances and thus not 220 # be relative to the accessor but to its parent. 221 222 if len(positions) == 1: 223 for position in positions: 224 if not position[0]: 225 node._access_type = "positioned" 226 node._position_deduced = position[0] 227 return 228 229 # With no usable deductions, generate a table-based access. 230 231 node._access_type = "unknown" 232 node._set_context = "cond" 233 234 visitAssAttr = visitGetattr = _visitAttr 235 236 # Convenience functions. 237 238 def deduce(program): 239 for module in program.get_importer().get_modules(): 240 DeducedSource(module, program).deduce() 241 242 # vim: tabstop=4 expandtab shiftwidth=4