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 function: 25 26 fix(module) 27 28 The more complicated approach involves instantiating a Fixer object: 29 30 fixer = Fixer() 31 32 Then, applying the fixer to an existing module: 33 34 fixer.process(module) 35 """ 36 37 from simplify.simplified import * 38 39 # Fixing of instance information. 40 41 class Fixer(Visitor): 42 43 """ 44 The name fixer which traverses the program nodes in a module, typically 45 depth-first, and eliminates references to superfluous instances, replacing 46 them with those from each class's distinct list, if necessary. 47 48 See the simplify.fixnames.Fixer class for a description of the 49 """ 50 51 def __init__(self): 52 53 "Initialise the name fixer." 54 55 Visitor.__init__(self) 56 57 # Satisfy visitor issues. 58 59 self.visitor = self 60 61 def process(self, module): 62 63 "Process the given 'module'." 64 65 # The fixer maintains a list of transformed subprograms (added for each 66 # of the processing "roots" and also for each invoked internal 67 # subprogram), along with a list of current subprograms (used to avoid 68 # recursion issues) and a list of current namespaces (used to recall 69 # namespaces upon invoking internal subprograms). 70 71 self.subprograms = [] 72 self.current_subprograms = [] 73 74 self.module = module 75 self.process_node(module) 76 77 # Then, process all functions and methods. 78 79 for subprogram in self.module.simplifier.subprograms: 80 81 # Internal subprograms are skipped here and processed specially via 82 # Invoke nodes. 83 84 if not getattr(subprogram, "internal", 0): 85 for specialised in subprogram.active(): 86 self.subprograms.append(self.process_node(specialised)) 87 88 def process_structures(self, module): 89 90 "Process the structures of the given 'module'." 91 92 self.module = module 93 94 # Visit structures and instances. 95 96 for structure in self.module.simplifier.structures: 97 for instance in structure.get_instances(): 98 for name, attrs in instance.namespace.items(): 99 instance.namespace[name] = self._replace(attrs) 100 101 def process_node(self, node): 102 103 """ 104 Process a subprogram or module 'node', discovering from attributes on 105 'node' any initial locals. Return a modified subprogram or module. 106 """ 107 108 # Do not process subprograms already being processed. 109 110 if node in self.current_subprograms: 111 return None 112 113 # Record the current subprogram. 114 115 self.current_subprograms.append(node) 116 117 # Dispatch to the code itself. 118 119 result = self.dispatch(node) 120 121 # Restore the previous subprogram and namespace. 122 123 self.current_subprograms.pop() 124 125 return node 126 127 # Visitor methods. 128 129 def default(self, node): 130 131 """ 132 Process the given 'node', given that it does not have a specific 133 handler. 134 """ 135 136 # Process annotations. 137 138 for name in ("non_accesses", "non_writes", "raises", "returns", "types"): 139 if hasattr(node, name): 140 attrs = getattr(node, name) 141 setattr(node, name, self._replace(attrs)) 142 for name in ("accesses", "writes", "paramtypes"): 143 if hasattr(node, name): 144 d = getattr(node, name) 145 new_d = {} 146 for expr, attrs in d.items(): 147 new_d[self._get_replacement(expr)] = self._replace(attrs, name) 148 setattr(node, name, new_d) 149 150 # Visit program nodes. 151 152 for attr in ("pos_args",): 153 if hasattr(node, attr): 154 self.dispatches(getattr(node, attr)) 155 for attr in ("kw_args",): 156 if hasattr(node, attr): 157 self.dispatch_dict(getattr(node, attr)) 158 for attr in ("expr", "lvalue", "test", "star", "dstar"): 159 if hasattr(node, attr): 160 self.dispatch(getattr(node, attr)) 161 for attr in ("body", "else_", "handler", "finally_", "code", "choices", "nodes"): 162 if hasattr(node, attr): 163 self.dispatches(getattr(node, attr)) 164 if hasattr(node, "params"): 165 for param, default in node.params: 166 self.dispatch(default) 167 for attr in ("star", "dstar"): 168 if getattr(node, attr, None): 169 param, default = getattr(node, attr) 170 self.dispatch(default) 171 172 return node 173 174 def _replace(self, items, name=None): 175 176 """ 177 Produce a new list or set for the given 'items', acquired from the 178 annotation having the given 'name'. 179 """ 180 181 if name == "accesses": 182 new_items = [] 183 else: 184 new_items = set() 185 186 for item in list(items): 187 if name == "accesses": 188 attr, accessor = item 189 value = attr.type 190 new_items.append((Attribute(self._get_replacement(attr.context), self._get_replacement(value)), self._get_replacement(accessor))) 191 else: 192 attr = item 193 value = attr.type 194 new_items.add(Attribute(self._get_replacement(attr.context), self._get_replacement(value))) 195 196 return new_items 197 198 def _get_replacement(self, value): 199 200 "Get a replacement for the given 'value'." 201 202 # Find the distinct instance for any given instance. 203 204 if isinstance(value, Instance): 205 distinct_instances = value.get_class().get_distinct_instances() 206 return distinct_instances[value] 207 208 # For subprograms, find the distinct instance's copy for the owner 209 # instance and assert that the signatures are the same; otherwise, 210 # return the original subprogram. 211 # NOTE: This needs to be verified in a somewhat more formal fashion. 212 213 elif isinstance(value, Subprogram): 214 if hasattr(value, "copy_of") and hasattr(value, "instance"): 215 cls = value.instance.get_class() 216 distinct = cls.get_distinct_instances() 217 instance = distinct[value.instance] 218 if value.copy_of.copies.has_key(instance): 219 subprogram = value.copy_of.copies[instance] 220 if subprogram.paramtypes == value.paramtypes: 221 return subprogram 222 223 return value 224 225 # Return all other values as they are. 226 227 else: 228 return value 229 230 def dispatch(self, node, *args): 231 return Visitor.dispatch(self, node, *args) 232 233 def visitInvokeFunction(self, invoke): 234 235 "Transform the 'invoke' node, performing processing on subprograms." 236 237 return self.default(invoke) 238 239 def visitInvokeRef(self, invoke): 240 241 "Transform the 'invoke' node, performing processing on subprograms." 242 243 # The special case of internal subprogram invocation is addressed by 244 # propagating namespace information to the subprogram and processing it. 245 246 subprogram = self.process_node(invoke.ref) 247 248 if subprogram is not None: 249 self.subprograms.append(subprogram) 250 return invoke 251 252 # Convenience functions. 253 254 def fix_structures(module): 255 256 "Fix the structures in the given 'module'." 257 258 fixer = Fixer() 259 fixer.process_structures(module) 260 261 def fix(module): 262 263 "Fix the structure references in the given 'module'." 264 265 fixer = Fixer() 266 fixer.process(module) 267 268 # vim: tabstop=4 expandtab shiftwidth=4