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 class System: 43 44 """ 45 A class maintaining the state of the fixing system like that used by the 46 annotation system. When the system counter can no longer be incremented by 47 any fixing operation, the system may be considered fixed. 48 """ 49 50 def __init__(self): 51 self.count = 0 52 53 def fix(self, original_value, proposed_value): 54 55 """ 56 Update the counter depending on the 'original_value' and the 57 'proposed_value' when attempting to fix the instances of a class. 58 """ 59 60 if original_value is not proposed_value: 61 self.count += 1 62 63 system = System() 64 65 # Fixing of instance information. 66 67 class Fixer(Visitor): 68 69 """ 70 The name fixer which traverses the program nodes in a module, typically 71 depth-first, and eliminates references to superfluous instances, replacing 72 them with those from each class's distinct list, if necessary. 73 74 See the simplify.fixnames.Fixer class for a description of the mechanisms 75 used to deal with subprograms. 76 """ 77 78 def __init__(self): 79 80 "Initialise the name fixer." 81 82 Visitor.__init__(self) 83 84 # Satisfy visitor issues. 85 86 self.visitor = self 87 88 def process(self, module): 89 90 "Process the given 'module'." 91 92 # The fixer maintains a list of transformed subprograms (added for each 93 # of the processing "roots" and also for each invoked internal 94 # subprogram), along with a list of current subprograms (used to avoid 95 # recursion issues) and a list of current namespaces (used to recall 96 # namespaces upon invoking internal subprograms). 97 98 self.current_subprograms = [] 99 100 self.module = module 101 102 # Process all functions and methods. 103 104 for subprogram in self.module.simplifier.subprograms: 105 106 # Internal subprograms are skipped here and processed specially via 107 # Invoke nodes. 108 109 if not subprogram.internal: 110 for specialised in subprogram.active(): 111 self.process_node(specialised) 112 113 self.process_node(module) 114 115 # Fix the simplifier's subprograms list itself. 116 117 #subprograms = set() 118 #for subprogram in self.module.simplifier.subprograms: 119 # subprograms.add(self._get_replacement(subprogram)) 120 #self.module.simplifier.subprograms = subprograms 121 122 def process_structures(self, module): 123 124 "Process the structures of the given 'module'." 125 126 self.module = module 127 128 # Visit structures and instances. 129 130 for structure in self.module.simplifier.structures: 131 for instance in structure.get_instances(): 132 for name, attrs in instance.namespace.items(): 133 instance.namespace[name] = self._replace(attrs) 134 135 def process_signatures(self, module): 136 137 "Process the signatures of subprograms in this 'module'." 138 139 self.module = module 140 141 # Visit each subprogram, updating the signatures. 142 143 for subprogram in self.module.simplifier.subprograms: 144 for specialisation in subprogram.active(): 145 self._replace_dict(specialisation, "paramtypes") 146 147 def process_node(self, node): 148 149 """ 150 Process a subprogram or module 'node', discovering from attributes on 151 'node' any initial locals. Return a modified subprogram or module. 152 """ 153 154 # Do not process subprograms already being processed. 155 156 if node in self.current_subprograms: 157 return None 158 159 # Record the current subprogram. 160 161 self.current_subprograms.append(node) 162 163 # Dispatch to the code itself. 164 165 result = self.dispatch(node) 166 167 # Restore the previous subprogram and namespace. 168 169 self.current_subprograms.pop() 170 171 return node 172 173 # Visitor methods. 174 175 def default(self, node): 176 177 """ 178 Process the given 'node', given that it does not have a specific 179 handler. 180 """ 181 182 # Process annotations. 183 184 for name in ("non_accesses", "non_writes", "raises", "returns", "types", "invocations"): 185 if hasattr(node, name): 186 self._replace_list(node, name) 187 for name in ("accesses", "writes", "paramtypes"): 188 if hasattr(node, name): 189 self._replace_dict(node, name) 190 for name in ("consumed_args",): 191 if hasattr(node, name): 192 new_d = {} 193 for subprogram, args in getattr(node, name).items(): 194 for arg in args: 195 if isinstance(arg, Self): 196 self.dispatch(arg) 197 new_d[self._get_replacement(subprogram)] = args 198 setattr(node, name, new_d) 199 200 # Visit program nodes. 201 202 for attr in ("pos_args",): 203 if hasattr(node, attr): 204 self.dispatches(getattr(node, attr)) 205 for attr in ("kw_args",): 206 if hasattr(node, attr): 207 self.dispatch_dict(getattr(node, attr)) 208 for attr in ("expr", "lvalue", "test", "star", "dstar"): 209 if hasattr(node, attr): 210 self.dispatch(getattr(node, attr)) 211 for attr in ("body", "else_", "handler", "finally_", "code", "choices", "nodes"): 212 if hasattr(node, attr): 213 self.dispatches(getattr(node, attr)) 214 if hasattr(node, "params"): 215 for param, default in node.params: 216 self.dispatch(default) 217 for attr in ("star", "dstar"): 218 if getattr(node, attr, None): 219 param, default = getattr(node, attr) 220 self.dispatch(default) 221 222 return node 223 224 def _replace_list(self, node, name): 225 attrs = getattr(node, name) 226 setattr(node, name, self._replace(attrs, name)) 227 228 def _replace_dict(self, node, name): 229 d = getattr(node, name) 230 new_d = {} 231 for expr, attrs in d.items(): 232 new_d[self._get_replacement(expr)] = self._replace(attrs, name) 233 setattr(node, name, new_d) 234 235 def _replace(self, items, name=None): 236 237 """ 238 Produce a new list or set for the given 'items', acquired from the 239 annotation having the given 'name'. 240 """ 241 242 if name == "accesses": 243 new_items = [] 244 else: 245 new_items = set() 246 247 for item in items: 248 if name == "accesses": 249 attr, accessor = item 250 value = attr.type 251 new_items.append((Attribute(self._get_replacement(attr.context), self._get_replacement(value)), 252 self._get_replacement(accessor))) 253 elif name == "invocations": 254 new_items.add(self._get_replacement(item)) 255 else: 256 attr = item 257 value = attr.type 258 new_items.add(Attribute(self._get_replacement(attr.context), self._get_replacement(value))) 259 260 return new_items 261 262 def _get_replacement(self, value): 263 264 "Get a replacement for the given 'value'." 265 266 # Find the distinct instance for any given instance. 267 268 if isinstance(value, Instance): 269 distinct_instances = value.get_class().get_distinct_instances() 270 271 # Make the fix by returning the proposed distinct instance. 272 273 system.fix(value, distinct_instances[value]) 274 return distinct_instances[value] 275 276 # For subprograms, find the distinct instance's copy for the owner 277 # instance and assert that the signatures are the same; otherwise, 278 # return the original subprogram. 279 # NOTE: This needs to be verified in a somewhat more formal fashion. 280 281 elif isinstance(value, Subprogram): 282 if hasattr(value, "copy_of") and hasattr(value, "instance"): 283 cls = value.instance.get_class() 284 distinct = cls.get_distinct_instances() 285 instance = distinct[value.instance] 286 if value.copy_of.copies.has_key(instance): 287 subprogram = value.copy_of.copies[instance] 288 if subprogram.paramtypes == value.paramtypes: 289 290 # Make the fix by returning the proposed subprogram. 291 292 system.fix(value, subprogram) 293 return subprogram 294 295 return value 296 297 # Return all other values as they are. 298 299 else: 300 return value 301 302 def visitInvokeFunction(self, invoke): 303 304 "Transform the 'invoke' node, performing processing on subprograms." 305 306 return self.default(invoke) 307 308 def visitInvokeRef(self, invoke): 309 310 "Transform the 'invoke' node, performing processing on subprograms." 311 312 # The special case of internal subprogram invocation is addressed by 313 # propagating namespace information to the subprogram and processing it. 314 315 self.process_node(invoke.ref) 316 return invoke 317 318 # Convenience functions. 319 320 def fix_structures(module): 321 322 "Fix the structures in the given 'module'." 323 324 fixer = Fixer() 325 fixer.process_structures(module) 326 327 def fix_signatures(module): 328 329 "Fix the signatures in the given 'module'." 330 331 fixer = Fixer() 332 fixer.process_signatures(module) 333 334 def fix(module): 335 336 "Fix the structure references in the given 'module'." 337 338 fixer = Fixer() 339 fixer.process(module) 340 341 # vim: tabstop=4 expandtab shiftwidth=4