1 #!/usr/bin/env python 2 3 """ 4 Fix instances, removing those which are not part of the distinct set for a given 5 class. 6 7 Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk> 8 9 This program is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation; either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT 15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 details. 18 19 You should have received a copy of the GNU General Public License along with 20 this program. If not, see <http://www.gnu.org/licenses/>. 21 22 -------- 23 24 To use this module, the easiest approach is to use the fix and fix_structures 25 functions: 26 27 fix_structures(module) # to fix the structures 28 fix(module) # to fix references to the structures 29 30 The more complicated approach involves instantiating a Fixer object: 31 32 fixer = Fixer() 33 34 Then, applying the fixer to an existing module: 35 36 fixer.process_structures(module) 37 fixer.process(module) 38 """ 39 40 from simplify.simplified import * 41 42 # Fixing of instance information. 43 44 class Fixer(Visitor): 45 46 """ 47 The name fixer which traverses the program nodes in a module, typically 48 depth-first, and eliminates references to superfluous instances, replacing 49 them with those from each class's distinct list, if necessary. 50 51 See the simplify.fixnames.Fixer class for a description of the mechanisms 52 used to deal with subprograms. 53 """ 54 55 def __init__(self): 56 57 "Initialise the name fixer." 58 59 Visitor.__init__(self) 60 61 # Satisfy visitor issues. 62 63 self.visitor = self 64 65 def process(self, module): 66 67 "Process the given 'module'." 68 69 # The fixer maintains a list of transformed subprograms (added for each 70 # of the processing "roots" and also for each invoked internal 71 # subprogram), along with a list of current subprograms (used to avoid 72 # recursion issues) and a list of current namespaces (used to recall 73 # namespaces upon invoking internal subprograms). 74 75 self.current_subprograms = [] 76 77 self.module = module 78 79 # Process all functions and methods. 80 81 for subprogram in self.module.simplifier.subprograms: 82 83 # Internal subprograms are skipped here and processed specially via 84 # Invoke nodes. 85 86 if not subprogram.internal: 87 for specialised in subprogram.active(): 88 self.process_node(specialised) 89 90 self.process_node(module) 91 92 # Fix the simplifier's subprograms list itself. 93 94 #subprograms = set() 95 #for subprogram in self.module.simplifier.subprograms: 96 # subprograms.add(self._get_replacement(subprogram)) 97 #self.module.simplifier.subprograms = subprograms 98 99 def process_structures(self, module): 100 101 "Process the structures of the given 'module'." 102 103 self.module = module 104 105 # Visit structures and instances. 106 107 for structure in self.module.simplifier.structures: 108 for instance in structure.get_instances(): 109 for name, attrs in instance.namespace.items(): 110 instance.namespace[name] = self._replace(attrs) 111 112 def process_signatures(self, module): 113 114 "Process the signatures of subprograms in this 'module'." 115 116 self.module = module 117 118 # Visit each subprogram, updating the signatures. 119 120 for subprogram in self.module.simplifier.subprograms: 121 for specialisation in subprogram.active(): 122 self._replace_dict(specialisation, "paramtypes") 123 124 def process_node(self, node): 125 126 """ 127 Process a subprogram or module 'node', discovering from attributes on 128 'node' any initial locals. Return a modified subprogram or module. 129 """ 130 131 # Do not process subprograms already being processed. 132 133 if node in self.current_subprograms: 134 return None 135 136 # Record the current subprogram. 137 138 self.current_subprograms.append(node) 139 140 # Dispatch to the code itself. 141 142 result = self.dispatch(node) 143 144 # Restore the previous subprogram and namespace. 145 146 self.current_subprograms.pop() 147 148 return node 149 150 # Visitor methods. 151 152 def default(self, node): 153 154 """ 155 Process the given 'node', given that it does not have a specific 156 handler. 157 """ 158 159 # Process annotations. 160 161 for name in ("non_accesses", "non_writes", "raises", "returns", "types", "invocations"): 162 if hasattr(node, name): 163 self._replace_list(node, name) 164 for name in ("accesses", "writes", "paramtypes"): 165 if hasattr(node, name): 166 self._replace_dict(node, name) 167 for name in ("consumed_args",): 168 if hasattr(node, name): 169 new_d = {} 170 for subprogram, args in getattr(node, name).items(): 171 for arg in args: 172 if isinstance(arg, Self): 173 self.dispatch(arg) 174 new_d[self._get_replacement(subprogram)] = args 175 setattr(node, name, new_d) 176 177 # Visit program nodes. 178 179 for attr in ("pos_args",): 180 if hasattr(node, attr): 181 self.dispatches(getattr(node, attr)) 182 for attr in ("kw_args",): 183 if hasattr(node, attr): 184 self.dispatch_dict(getattr(node, attr)) 185 for attr in ("expr", "lvalue", "test", "star", "dstar"): 186 if hasattr(node, attr): 187 self.dispatch(getattr(node, attr)) 188 for attr in ("body", "else_", "handler", "finally_", "code", "choices", "nodes"): 189 if hasattr(node, attr): 190 self.dispatches(getattr(node, attr)) 191 if hasattr(node, "params"): 192 for param, default in node.params: 193 self.dispatch(default) 194 for attr in ("star", "dstar"): 195 if getattr(node, attr, None): 196 param, default = getattr(node, attr) 197 self.dispatch(default) 198 199 return node 200 201 def _replace_list(self, node, name): 202 attrs = getattr(node, name) 203 setattr(node, name, self._replace(attrs, name)) 204 205 def _replace_dict(self, node, name): 206 d = getattr(node, name) 207 new_d = {} 208 for expr, attrs in d.items(): 209 new_d[self._get_replacement(expr)] = self._replace(attrs, name) 210 setattr(node, name, new_d) 211 212 def _replace(self, items, name=None): 213 214 """ 215 Produce a new list or set for the given 'items', acquired from the 216 annotation having the given 'name'. 217 """ 218 219 if name == "accesses": 220 new_items = [] 221 else: 222 new_items = set() 223 224 for item in items: 225 if name == "accesses": 226 attr, accessor = item 227 value = attr.type 228 new_items.append((Attribute(self._get_replacement(attr.context), self._get_replacement(value)), self._get_replacement(accessor))) 229 elif name == "invocations": 230 new_items.add(self._get_replacement(item)) 231 else: 232 attr = item 233 value = attr.type 234 new_items.add(Attribute(self._get_replacement(attr.context), self._get_replacement(value))) 235 236 return new_items 237 238 def _get_replacement(self, value): 239 240 "Get a replacement for the given 'value'." 241 242 # Find the distinct instance for any given instance. 243 244 if isinstance(value, Instance): 245 distinct_instances = value.get_class().get_distinct_instances() 246 return distinct_instances[value] 247 248 # For subprograms, find the distinct instance's copy for the owner 249 # instance and assert that the signatures are the same; otherwise, 250 # return the original subprogram. 251 # NOTE: This needs to be verified in a somewhat more formal fashion. 252 253 elif isinstance(value, Subprogram): 254 if hasattr(value, "copy_of") and hasattr(value, "instance"): 255 cls = value.instance.get_class() 256 distinct = cls.get_distinct_instances() 257 instance = distinct[value.instance] 258 if value.copy_of.copies.has_key(instance): 259 subprogram = value.copy_of.copies[instance] 260 if subprogram.paramtypes == value.paramtypes: 261 return subprogram 262 263 return value 264 265 # Return all other values as they are. 266 267 else: 268 return value 269 270 def visitInvokeFunction(self, invoke): 271 272 "Transform the 'invoke' node, performing processing on subprograms." 273 274 return self.default(invoke) 275 276 def visitInvokeRef(self, invoke): 277 278 "Transform the 'invoke' node, performing processing on subprograms." 279 280 # The special case of internal subprogram invocation is addressed by 281 # propagating namespace information to the subprogram and processing it. 282 283 self.process_node(invoke.ref) 284 return invoke 285 286 # Convenience functions. 287 288 def fix_structures(module): 289 290 "Fix the structures in the given 'module'." 291 292 fixer = Fixer() 293 fixer.process_structures(module) 294 295 def fix_signatures(module): 296 297 "Fix the signatures in the given 'module'." 298 299 fixer = Fixer() 300 fixer.process_signatures(module) 301 302 def fix(module): 303 304 "Fix the structure references in the given 'module'." 305 306 fixer = Fixer() 307 fixer.process(module) 308 309 # vim: tabstop=4 expandtab shiftwidth=4