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 software is free software; you can redistribute it and/or 10 modify it under the terms of the GNU General Public License as 11 published by the Free Software Foundation; either version 2 of 12 the License, or (at your option) any later version. 13 14 This software is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public 20 License along with this library; see the file LICENCE.txt 21 If not, write to the Free Software Foundation, Inc., 22 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 23 24 -------- 25 26 To use this module, the easiest approach is to use the fix function: 27 28 fix(module) 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(module) 37 """ 38 39 from simplify.simplified import * 40 41 # Fixing of instance information. 42 43 class Fixer(Visitor): 44 45 """ 46 The name fixer which traverses the program nodes in a module, typically 47 depth-first, and eliminates references to superfluous instances, replacing 48 them with those from each class's distinct list, if necessary. 49 50 See the simplify.fixnames.Fixer class for a description of the 51 """ 52 53 def __init__(self): 54 55 "Initialise the name fixer." 56 57 Visitor.__init__(self) 58 59 # Satisfy visitor issues. 60 61 self.visitor = self 62 63 def process(self, module): 64 65 "Process the given 'module'." 66 67 # The fixer maintains a list of transformed subprograms (added for each 68 # of the processing "roots" and also for each invoked internal 69 # subprogram), along with a list of current subprograms (used to avoid 70 # recursion issues) and a list of current namespaces (used to recall 71 # namespaces upon invoking internal subprograms). 72 73 self.subprograms = [] 74 self.current_subprograms = [] 75 76 self.module = module 77 self.process_node(module) 78 79 # Then, 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 getattr(subprogram, "internal", 0): 87 for specialised in subprogram.active(): 88 self.subprograms.append(self.process_node(specialised)) 89 90 def process_structures(self, module): 91 92 "Process the structures of the given 'module'." 93 94 self.module = module 95 96 # Visit structures and instances. 97 98 for structure in self.module.simplifier.structures: 99 for instance in structure.get_instances(): 100 for name, attrs in instance.namespace.items(): 101 instance.namespace[name] = self._replace(attrs) 102 103 def process_node(self, node): 104 105 """ 106 Process a subprogram or module 'node', discovering from attributes on 107 'node' any initial locals. Return a modified subprogram or module. 108 """ 109 110 # Do not process subprograms already being processed. 111 112 if node in self.current_subprograms: 113 return None 114 115 # Record the current subprogram. 116 117 self.current_subprograms.append(node) 118 119 # Dispatch to the code itself. 120 121 result = self.dispatch(node) 122 123 # Restore the previous subprogram and namespace. 124 125 self.current_subprograms.pop() 126 127 return node 128 129 # Visitor methods. 130 131 def default(self, node): 132 133 """ 134 Process the given 'node', given that it does not have a specific 135 handler. 136 """ 137 138 # Process annotations. 139 140 for name in ("non_accesses", "non_writes", "raises", "returns", "types"): 141 if hasattr(node, name): 142 attrs = getattr(node, name) 143 setattr(node, name, self._replace(attrs)) 144 for name in ("accesses", "writes", "paramtypes"): 145 if hasattr(node, name): 146 d = getattr(node, name) 147 new_d = {} 148 for expr, attrs in d.items(): 149 new_d[self._get_replacement(expr)] = self._replace(attrs, name) 150 setattr(node, name, new_d) 151 152 # Visit program nodes. 153 154 for attr in ("pos_args",): 155 if hasattr(node, attr): 156 self.dispatches(getattr(node, attr)) 157 for attr in ("kw_args",): 158 if hasattr(node, attr): 159 self.dispatch_dict(getattr(node, attr)) 160 for attr in ("expr", "lvalue", "test", "star", "dstar"): 161 if hasattr(node, attr): 162 self.dispatch(getattr(node, attr)) 163 for attr in ("body", "else_", "handler", "finally_", "code", "choices", "nodes"): 164 if hasattr(node, attr): 165 self.dispatches(getattr(node, attr)) 166 if hasattr(node, "params"): 167 for param, default in node.params: 168 self.dispatch(default) 169 for attr in ("star", "dstar"): 170 if getattr(node, attr, None): 171 param, default = getattr(node, attr) 172 self.dispatch(default) 173 174 return node 175 176 def _replace(self, items, name=None): 177 178 """ 179 Produce a new list or set for the given 'items', acquired from the 180 annotation having the given 'name'. 181 """ 182 183 if name == "accesses": 184 new_items = [] 185 else: 186 new_items = set() 187 188 for item in list(items): 189 if name == "accesses": 190 attr, accessor = item 191 value = attr.type 192 new_items.append((Attribute(self._get_replacement(attr.context), self._get_replacement(value)), self._get_replacement(accessor))) 193 else: 194 attr = item 195 value = attr.type 196 new_items.add(Attribute(self._get_replacement(attr.context), self._get_replacement(value))) 197 198 return new_items 199 200 def _get_replacement(self, value): 201 202 "Get a replacement for the given 'value'." 203 204 # Find the distinct instance for any given instance. 205 206 if isinstance(value, Instance): 207 distinct_instances = value.get_class().get_distinct_instances() 208 return distinct_instances[value] 209 210 # For subprograms, find the distinct instance's copy for the owner 211 # instance; otherwise, return the original subprogram. 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 return value.copy_of.copies.get(instance, value) 219 else: 220 return value 221 222 # Return all other values as they are. 223 224 else: 225 return value 226 227 def dispatch(self, node, *args): 228 return Visitor.dispatch(self, node, *args) 229 230 def visitInvokeFunction(self, invoke): 231 232 "Transform the 'invoke' node, performing processing on subprograms." 233 234 return self.default(invoke) 235 236 def visitInvokeRef(self, invoke): 237 238 "Transform the 'invoke' node, performing processing on subprograms." 239 240 # The special case of internal subprogram invocation is addressed by 241 # propagating namespace information to the subprogram and processing it. 242 243 subprogram = self.process_node(invoke.ref) 244 245 if subprogram is not None: 246 self.subprograms.append(subprogram) 247 return invoke 248 249 # Convenience functions. 250 251 def fix_structures(module): 252 253 "Fix the structures in the given 'module'." 254 255 fixer = Fixer() 256 fixer.process_structures(module) 257 258 def fix(module): 259 260 "Fix the structure references in the given 'module'." 261 262 fixer = Fixer() 263 fixer.process(module) 264 265 # vim: tabstop=4 expandtab shiftwidth=4