1 #!/usr/bin/env python 2 3 """ 4 Annotate program node structures. The code in this module operates upon nodes 5 which are produced when simplifying AST node trees originating from the compiler 6 module. 7 8 Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk> 9 10 This software is free software; you can redistribute it and/or 11 modify it under the terms of the GNU General Public License as 12 published by the Free Software Foundation; either version 2 of 13 the License, or (at your option) any later version. 14 15 This software is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public 21 License along with this library; see the file LICENCE.txt 22 If not, write to the Free Software Foundation, Inc., 23 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 24 25 -------- 26 27 To use this module, the easiest approach is to use the annotate function: 28 29 annotate(module, builtins) 30 31 The more complicated approach involves obtaining an Annotator: 32 33 annotator = Annotator() 34 35 Then, processing an existing module with it: 36 37 annotator.process(module) 38 39 If a module containing built-in classes and functions has already been 40 annotated, such a module should be passed in as an additional argument: 41 42 annotator.process(module, builtins) 43 """ 44 45 from simplified import * 46 import compiler 47 48 class System: 49 50 """ 51 A class maintaining the state of the annotation system. When the system 52 counter can no longer be incremented by any annotation operation, the 53 system may be considered stable and fully annotated. 54 """ 55 56 def __init__(self): 57 self.count = 0 58 def init(self, node): 59 if not hasattr(node, "types"): 60 node.types = [] 61 def annotate(self, node, types): 62 self.init(node) 63 for type in types: 64 if type not in node.types: 65 node.types.append(type) 66 self.count += 1 67 68 system = System() 69 70 # Exceptions. 71 72 class AnnotationError(SimplifiedError): 73 74 "An error in the annotation process." 75 76 pass 77 78 class AnnotationMessage(Exception): 79 80 "A lesser annotation error." 81 82 pass 83 84 # Annotation. 85 86 class Annotator(Visitor): 87 88 """ 89 The type annotator which traverses the program nodes, typically depth-first, 90 and maintains a record of the current set of types applying to the currently 91 considered operation. Such types are also recorded on the nodes, and a 92 special "system" record is maintained to monitor the level of annotation 93 activity with a view to recognising when no more annotations are possible. 94 95 Throughout the annotation activity, type information consists of lists of 96 Attribute objects where such objects retain information about the context of 97 the type (since a value in the program may be associated with an object or 98 class) and the actual type of the value being manipulated. Upon accessing 99 attribute information on namespaces, additional accessor information is also 100 exchanged - this provides a means of distinguishing between the different 101 types possible when the means of constructing the namespace may depend on 102 run-time behaviour. 103 """ 104 105 def __init__(self): 106 107 "Initialise the visitor." 108 109 Visitor.__init__(self) 110 self.system = system 111 112 # Satisfy visitor issues. 113 114 self.visitor = self 115 116 def process(self, module, builtins=None): 117 118 """ 119 Process the given 'module', using the optional 'builtins' to access 120 built-in classes and functions. 121 """ 122 123 self.subprograms = [] 124 self.current_subprograms = [] 125 self.current_namespaces = [] 126 self.namespace = None 127 self.module = module 128 129 # Give constants their own namespace. 130 131 for value, constant in module.simplifier.constants.items(): 132 constant.namespace = Namespace() 133 134 # Process the module, supplying builtins if possible. 135 136 self.builtins = builtins 137 self.global_namespace = Namespace() 138 139 if builtins is not None: 140 self.builtins_namespace = builtins.namespace 141 else: 142 self.builtins_namespace = self.global_namespace 143 144 return self.process_node(module, self.global_namespace) 145 146 def process_node(self, node, locals): 147 148 """ 149 Process a subprogram or module 'node', indicating the initial 'locals'. 150 Return an annotated subprogram or module. Note that this method may 151 mutate nodes in the original program. 152 """ 153 154 # Record the current subprogram and namespace. 155 156 self.current_subprograms.append(node) 157 158 # Determine the namespace. 159 160 self.current_namespaces.append(self.namespace) 161 self.namespace = locals 162 163 # Add namespace details to any structure involved. 164 165 if getattr(node, "structure", None) is not None: 166 node.structure.namespace = Namespace() 167 168 # Initialise bases where appropriate. 169 170 if hasattr(node.structure, "bases"): 171 base_refs = [] 172 for base in node.structure.bases: 173 self.dispatch(base) 174 base_refs.append(self.namespace.types) 175 node.structure.base_refs = base_refs 176 177 # Dispatch to the code itself. 178 179 node.namespace = self.namespace 180 result = self.dispatch(node) 181 result.namespace = self.namespace 182 183 # Obtain the return values. 184 185 self.last_returns = self.namespace.returns 186 self.last_raises = self.namespace.raises 187 self.returned_locals = self.namespace.return_locals 188 189 # Restore the previous subprogram and namespace. 190 191 self.namespace = self.current_namespaces.pop() 192 self.current_subprograms.pop() 193 194 return result 195 196 def annotate(self, node, types=None): 197 198 """ 199 Annotate the given 'node' in the system, using either the optional 200 'types' or the namespace's current type information. 201 """ 202 203 self.system.annotate(node, types or self.namespace.types) 204 205 # Visitor methods. 206 207 def default(self, node): 208 209 """ 210 Process the given 'node', given that it does not have a specific 211 handler. 212 """ 213 214 raise AnnotationMessage, "Node '%s' not supported." % node 215 216 def dispatch(self, node, *args): 217 try: 218 return Visitor.dispatch(self, node, *args) 219 except AnnotationError, exc: 220 exc.add(node) 221 raise 222 except AnnotationMessage, exc: 223 raise AnnotationError(exc, node) 224 225 # Program structure/control-flow. 226 227 def visitAssign(self, assign): 228 assign.code = self.dispatches(assign.code) 229 return assign 230 231 def visitConditional(self, conditional): 232 233 # Conditionals keep local namespace changes isolated. 234 # With Return nodes inside the body/else sections, the changes are 235 # communicated to the caller. 236 237 is_module = self.namespace is self.module.namespace 238 239 # Where the test is closely associated with the body, save the namespace 240 # before entering the test. 241 242 if conditional.isolate_test: 243 saved_namespace = self.namespace 244 self.namespace = Namespace() 245 if is_module: 246 self.module.namespace = self.namespace 247 self.namespace.merge_namespace(saved_namespace) 248 249 conditional.test = self.dispatch(conditional.test) 250 251 # Where the test may affect the body and the else clause, save the 252 # namespace after processing the test. 253 254 if not conditional.isolate_test: 255 saved_namespace = self.namespace 256 self.namespace = Namespace() 257 if is_module: 258 self.module.namespace = self.namespace 259 self.namespace.merge_namespace(saved_namespace) 260 261 # Process the body clause. 262 263 conditional.body = self.dispatches(conditional.body) 264 body_namespace = self.namespace 265 266 # Use the saved namespace as a template for the else clause. 267 268 self.namespace = Namespace() 269 if is_module: 270 self.module.namespace = self.namespace 271 self.namespace.merge_namespace(saved_namespace) 272 273 # Process the else clause. 274 275 conditional.else_ = self.dispatches(conditional.else_) 276 else_namespace = self.namespace 277 278 # Merge the body and else namespaces. 279 280 self.namespace = Namespace() 281 if is_module: 282 self.module.namespace = self.namespace 283 self.namespace.merge_namespace(body_namespace) 284 self.namespace.merge_namespace(else_namespace) 285 286 return conditional 287 288 def visitModule(self, module): 289 module.code = self.dispatches(module.code) 290 return module 291 292 def visitPass(self, pass_): 293 return pass_ 294 295 def visitSubprogram(self, subprogram): 296 subprogram.code = self.dispatches(subprogram.code) 297 return subprogram 298 299 def visitTry(self, try_): 300 is_module = self.namespace is self.module.namespace 301 302 try_.body = self.dispatches(try_.body) 303 304 # Save the namespace from the body. 305 306 body_namespace = Namespace() 307 body_namespace.merge_namespace(self.namespace) 308 309 # Process the handler. 310 311 if hasattr(try_, "handler"): 312 try_.handler = self.dispatches(try_.handler) 313 314 # Save the namespace from the handler. 315 316 handler_namespace = Namespace() 317 handler_namespace.merge_namespace(self.namespace) 318 319 # Remember the raised exceptions encountered so far. 320 321 raises = self.namespace.raises 322 323 # Process the else clause. 324 325 if hasattr(try_, "else_"): 326 327 # Restore the body namespace for the else clause. 328 329 self.namespace = body_namespace 330 if is_module: 331 self.module.namespace = self.namespace 332 333 # Empty the raised exceptions for the else clause. 334 335 self.namespace.raises = [] 336 try_.else_ = self.dispatches(try_.else_) 337 self.namespace.raises = raises 338 339 # Merge the namespaces. 340 341 self.namespace = Namespace() 342 if is_module: 343 self.module.namespace = self.namespace 344 self.namespace.merge_namespace(body_namespace) 345 self.namespace.merge_namespace(handler_namespace) 346 347 # Process the finally clause, if any. 348 349 try_.finally_ = self.dispatches(try_.finally_) 350 return try_ 351 352 # Namespace operations. 353 354 def visitCheckExc(self, checkexc): 355 checkexc.expr = self.dispatch(checkexc.expr) 356 expr_types = self.namespace.types 357 choice_types = [] 358 choices = [] 359 for choice in checkexc.choices: 360 choices.append(self.dispatch(choice)) 361 choice_types += self.namespace.types 362 for expr_type in expr_types: 363 if expr_type.type.get_class() not in choice_types: 364 self._prune_non_accesses(checkexc.expr, expr_type) 365 return checkexc 366 367 def visitLoadAttr(self, loadattr): 368 loadattr.expr = self.dispatch(loadattr.expr) 369 types = [] 370 non_accesses = [] 371 accesses = {} 372 for attr in self.namespace.types: 373 attributes = get_attributes(attr.type, loadattr.name) 374 if not attributes: 375 if not attr.type in non_accesses: 376 non_accesses.append(attr) 377 378 # Revoke this type from any name involved. 379 380 self._prune_non_accesses(loadattr.expr, attr) 381 382 for attribute, accessor in attributes: 383 if attribute is not None: 384 types.append(attribute) 385 if not accesses.has_key(attr.type): 386 accesses[attr.type] = [] 387 if not (attribute, accessor) in accesses[attr.type]: 388 accesses[attr.type].append((attribute, accessor)) 389 else: 390 if not attr in non_accesses: 391 non_accesses.append(attr) 392 393 # Revoke this type from any name involved. 394 395 self._prune_non_accesses(loadattr.expr, attr) 396 397 if not types: 398 print "No attribute found for", loadattr.name, "given", self.namespace.types 399 self.namespace.set_types(types) 400 loadattr.non_accesses = non_accesses 401 loadattr.accesses = accesses 402 self.annotate(loadattr) 403 return loadattr 404 405 def _prune_non_accesses(self, expr, attr): 406 if isinstance(expr, LoadName): 407 self.namespace.revoke(expr.name, attr) 408 elif isinstance(expr, LoadAttr): 409 for expr_attr in expr.expr.types: 410 if hasattr(expr_attr.type, "namespace"): 411 expr_attr.type.namespace.revoke(expr.name, attr) 412 elif isinstance(expr, LoadExc): 413 self.namespace.revoke_exception_type(attr) 414 415 def visitLoadExc(self, loadexc): 416 self.namespace.types = self.namespace.raises[:] 417 self.annotate(loadexc) 418 return loadexc 419 420 def visitLoadName(self, loadname): 421 self.namespace.set_types(self.namespace.load(loadname.name)) 422 result = loadname 423 self.annotate(result) 424 return result 425 426 def visitLoadRef(self, loadref): 427 self.namespace.set_types([Attribute(None, loadref.ref)]) 428 self.annotate(loadref) 429 return loadref 430 431 def visitLoadTemp(self, loadtemp): 432 index = getattr(loadtemp, "index", None) 433 try: 434 if getattr(loadtemp, "release", 0): 435 self.namespace.set_types(self.namespace.temp[index].pop()) 436 else: 437 self.namespace.set_types(self.namespace.temp[index][-1]) 438 except KeyError: 439 raise AnnotationMessage, "Temporary store index '%s' not defined." % index 440 self.annotate(loadtemp) 441 return loadtemp 442 443 def visitNot(self, not_): 444 not_.expr = self.dispatch(not_.expr) 445 return not_ 446 447 def visitRaise(self, raise_): 448 if getattr(raise_, "traceback", None) is not None: 449 raise_.traceback = self.dispatch(raise_.traceback) 450 raise_.expr = self.dispatch(raise_.expr) 451 452 # Handle bare name exceptions by converting any classes to instances. 453 454 if not isinstance(raise_.expr, InvokeFunction): 455 raise_.pos_args = [] 456 raise_.kw_args = {} 457 raise_.star = None 458 raise_.dstar = None 459 types = [] 460 for attr in self.namespace.types: 461 if isinstance(attr.type, Class): 462 self._visitInvoke(raise_, [attr], have_args=0) 463 types += self.namespace.types 464 else: 465 types = self.namespace.types 466 467 combine(self.namespace.raises, types) 468 return raise_ 469 470 def visitReleaseTemp(self, releasetemp): 471 index = getattr(releasetemp, "index", None) 472 try: 473 self.namespace.temp[index].pop() 474 except KeyError: 475 raise AnnotationMessage, "Temporary store index '%s' not defined." % index 476 except IndexError: 477 pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index 478 return releasetemp 479 480 def visitReturn(self, return_): 481 if hasattr(return_, "expr"): 482 return_.expr = self.dispatch(return_.expr) 483 combine(self.namespace.returns, self.namespace.types) 484 self.annotate(return_) 485 self.namespace.snapshot() 486 return return_ 487 488 def visitStoreAttr(self, storeattr): 489 storeattr.expr = self.dispatch(storeattr.expr) 490 expr = self.namespace.types 491 storeattr.lvalue = self.dispatch(storeattr.lvalue) 492 writes = {} 493 non_writes = [] 494 for attr in self.namespace.types: 495 if attr is None: 496 if not attr in non_writes: 497 non_writes.append(attr) 498 continue 499 attr.type.namespace.store(storeattr.name, expr) 500 writes[attr.type] = attr.type.namespace.load(storeattr.name) 501 if not writes: 502 print "Unable to store attribute", storeattr.name, "given", self.namespace.types 503 storeattr.writes = writes 504 storeattr.non_writes = non_writes 505 return storeattr 506 507 def visitStoreName(self, storename): 508 storename.expr = self.dispatch(storename.expr) 509 self.namespace.store(storename.name, self.namespace.types) 510 return storename 511 512 def visitStoreTemp(self, storetemp): 513 storetemp.expr = self.dispatch(storetemp.expr) 514 index = getattr(storetemp, "index", None) 515 if not self.namespace.temp.has_key(index): 516 self.namespace.temp[index] = [] 517 self.namespace.temp[index].append(self.namespace.types) 518 return storetemp 519 520 # Invocations are a chapter of their own. 521 522 def visitInvokeBlock(self, invoke): 523 524 # First find the callables. 525 526 invoke.expr = self.dispatch(invoke.expr) 527 invocation_types = self.namespace.types 528 return self._visitInvoke(invoke, invocation_types, have_args=0) 529 530 def visitInvokeFunction(self, invoke): 531 532 # First find the callables. 533 534 invoke.expr = self.dispatch(invoke.expr) 535 invocation_types = self.namespace.types 536 537 # Invocation processing starts with making sure that the arguments have 538 # been processed. 539 540 return self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke)) 541 542 def _visitInvoke(self, invoke, invocation_types, have_args): 543 544 # Now locate and invoke the subprogram. This can be complicated because 545 # the target may be a class or object, and there may be many different 546 # related subprograms. 547 548 invocations = [] 549 550 # Visit each callable in turn, finding subprograms. 551 552 for attr in invocation_types: 553 554 # Deal with class invocations by providing instance objects. 555 # Here, each class is queried for the __init__ method, which may 556 # exist for some combinations of classes in a hierarchy but not for 557 # others. 558 559 if isinstance(attr.type, Class): 560 attributes = get_attributes(attr.type, "__init__") 561 562 # Deal with object invocations by using __call__ methods. 563 564 elif isinstance(attr.type, Instance): 565 attributes = get_attributes(attr.type, "__call__") 566 567 # Normal functions or methods are more straightforward. 568 # Here, we model them using an attribute with no context and with 569 # no associated accessor. 570 571 else: 572 attributes = [(attr, None)] 573 574 # Inspect each attribute and extract the subprogram. 575 576 for attribute, accessor in attributes: 577 578 # If a class is involved, presume that it must create a new 579 # object. 580 581 if isinstance(attr.type, Class): 582 583 # Instantiate the class. 584 # NOTE: Should probably only allocate a single instance. 585 586 instance = self.new_instance(invoke, "new", attr.type.full_name(), attr.type) 587 588 # For instantiations, switch the context. 589 590 if attribute is not None: 591 attribute = Attribute(instance, attribute.type) 592 593 # Skip cases where no callable is found. 594 595 if attribute is not None: 596 597 # If a subprogram is defined, invoke it. 598 599 self.invoke_subprogram(invoke, attribute) 600 if attribute.type not in invocations: 601 invocations.append(attribute.type) 602 603 elif not isinstance(attr.type, Class): 604 print "Invocation type is None for", accessor 605 606 else: 607 608 # Test to see if no arguments were supplied in cases where no 609 # initialiser was found. 610 611 if have_args: 612 raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type 613 614 # Special case: initialisation. 615 616 if isinstance(attr.type, Class): 617 618 # Associate the instance with the result of this invocation. 619 620 self.namespace.set_types([Attribute(None, instance)]) 621 self.annotate(invoke) 622 623 # Remember the invocations that were found, along with the return type 624 # information. 625 626 invoke.invocations = invocations 627 self.namespace.set_types(getattr(invoke, "types", [])) 628 return invoke 629 630 # Utility methods. 631 632 def new_instance(self, node, reason, target, type): 633 634 "Create, on the given 'node', a new instance with the given 'type'." 635 636 if not hasattr(node, "instances"): 637 node.instances = {} 638 639 if not node.instances.has_key((reason, target, type)): 640 641 # Insist on a single instance per type. 642 # NOTE: Strategy-dependent instantiation. 643 644 if len(type.instances) == 0: 645 instance = Instance() 646 instance.namespace = Namespace() 647 instance.namespace.store("__class__", [Attribute(None, type)]) 648 type.instances.append(instance) 649 else: 650 instance = type.instances[0] 651 652 #instance = Instance() 653 #instance.namespace = Namespace() 654 #instance.namespace.store("__class__", [Attribute(None, type)]) 655 #type.instances.append(instance) 656 657 node.instances[(reason, target, type)] = instance 658 659 return node.instances[(reason, target, type)] 660 661 def invoke_subprogram(self, invoke, subprogram): 662 663 "Invoke using the given 'invoke' node the given 'subprogram'." 664 665 # Test to see if anything has changed. 666 667 if hasattr(invoke, "syscount") and invoke.syscount == self.system.count: 668 return 669 670 # Remember the state of the system. 671 672 else: 673 invoke.syscount = self.system.count 674 675 # Test for context information, making it into a real attribute. 676 677 if subprogram.context is not None: 678 context = Attribute(None, subprogram.context) 679 target = subprogram.type 680 else: 681 context = None 682 target = subprogram.type 683 684 # Provide the correct namespace for the invocation. 685 686 if getattr(invoke, "share_locals", 0): 687 namespace = Namespace() 688 namespace.merge_namespace(self.namespace, everything=0) 689 using_module_namespace = self.namespace is self.module.namespace 690 elif getattr(target, "structure", None): 691 namespace = Namespace() 692 using_module_namespace = 0 693 else: 694 items = self.make_items(invoke, target, context) 695 namespace = self.make_namespace(items) 696 using_module_namespace = 0 697 698 # Process the subprogram. 699 # In order to keep global accesses working, the module namespace must be 700 # adjusted. 701 702 if using_module_namespace: 703 self.module.namespace = namespace 704 705 self.process_node(target, namespace) 706 707 # NOTE: Improve and verify this. 708 # If the invocation returns a value, acquire the return types. 709 710 if getattr(target, "returns_value", 0): 711 self.namespace.set_types(self.last_returns) 712 self.annotate(invoke) 713 714 # If it is a normal block, merge the locals. 715 # This can happen in addition to the above because for things like 716 # logical expressions, the namespace can be modified whilst values are 717 # returned as results. 718 719 if getattr(invoke, "share_locals", 0): 720 self.namespace.reset() 721 for locals in self.returned_locals: 722 self.namespace.merge_namespace(locals, everything=0) 723 724 # Incorporate any raised exceptions. 725 726 combine(self.namespace.raises, self.last_raises) 727 728 # In order to keep global accesses working, the module namespace must be 729 # adjusted. 730 731 if using_module_namespace: 732 self.module.namespace = self.namespace 733 734 def process_args(self, invocation): 735 736 """ 737 Process the arguments associated with an 'invocation'. Return whether 738 any arguments were processed. 739 """ 740 741 invocation.pos_args = self.dispatches(invocation.pos_args) 742 invocation.kw_args = self.dispatch_dict(invocation.kw_args) 743 744 # Get type information for star and dstar arguments. 745 746 if invocation.star is not None: 747 param, default = invocation.star 748 default = self.dispatch(default) 749 invocation.star = param, default 750 751 if invocation.dstar is not None: 752 param, default = invocation.dstar 753 default = self.dispatch(default) 754 invocation.dstar = param, default 755 756 if invocation.pos_args or invocation.kw_args or invocation.star or invocation.dstar: 757 return 1 758 else: 759 return 0 760 761 def make_items(self, invocation, subprogram, context): 762 763 """ 764 Make an items mapping for the 'invocation' of the 'subprogram' using the 765 given 'context' (which may be None). 766 """ 767 768 if context is not None: 769 pos_args = [Self(context)] + invocation.pos_args 770 else: 771 pos_args = invocation.pos_args 772 kw_args = invocation.kw_args 773 774 # Sort the arguments into positional and keyword arguments. 775 776 params = subprogram.params 777 items = [] 778 star_args = [] 779 780 # Match each positional argument, taking excess arguments as star args. 781 782 for arg in pos_args: 783 if params: 784 param, default = params[0] 785 if arg is None: 786 arg = default 787 items.append((param, arg.types)) 788 params = params[1:] 789 else: 790 star_args.append(arg) 791 792 # Collect the remaining defaults. 793 794 while params: 795 param, default = params[0] 796 if kw_args.has_key(param): 797 arg = kw_args[param] 798 del kw_args[param] 799 elif default is not None: 800 arg = self.dispatch(default) 801 else: 802 raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param) 803 items.append((param, arg.types)) 804 params = params[1:] 805 806 dstar_args = kw_args.values() 807 808 # Construct temporary objects. 809 810 if star_args: 811 star_invocation = self.make_star_args(invocation, subprogram, star_args) 812 self.dispatch(star_invocation) 813 star_types = star_invocation.types 814 else: 815 star_types = None 816 817 if dstar_args: 818 dstar_invocation = self.make_dstar_args(invocation, subprogram, dstar_args) # NOTE: To be written! 819 self.dispatch(dstar_invocation) 820 dstar_types = dstar_invocation.types 821 else: 822 dstar_types = None 823 824 # NOTE: Merge the objects properly. 825 826 star_types = star_types or invocation.star and invocation.star.types 827 dstar_types = dstar_types or invocation.dstar and invocation.dstar.types 828 829 # Add star and dstar. 830 831 if star_types is not None: 832 if subprogram.star is not None: 833 param, default = subprogram.star 834 items.append((param, star_types)) 835 else: 836 raise AnnotationMessage, "Invocation provides unwanted *args." 837 elif subprogram.star is not None: 838 param, default = subprogram.star 839 arg = self.dispatch(default) # NOTE: Review reprocessing. 840 items.append((param, arg.types)) 841 842 if dstar_types is not None: 843 if subprogram.dstar is not None: 844 param, default = subprogram.dstar 845 items.append((param, dstar_types)) 846 else: 847 raise AnnotationMessage, "Invocation provides unwanted **args." 848 elif subprogram.dstar is not None: 849 param, default = subprogram.dstar 850 arg = self.dispatch(default) # NOTE: Review reprocessing. 851 items.append((param, arg.types)) 852 853 # Record the parameter types. 854 855 if not hasattr(subprogram, "paramtypes"): 856 subprogram.paramtypes = {} 857 858 for param, types in items: 859 if not subprogram.paramtypes.has_key(param): 860 subprogram.paramtypes[param] = [] 861 combine(subprogram.paramtypes[param], types) 862 863 return items 864 865 def make_star_args(self, invocation, subprogram, star_args): 866 867 "Make a subprogram which initialises a list containing 'star_args'." 868 869 if not hasattr(invocation, "stars"): 870 invocation.stars = {} 871 872 if not invocation.stars.has_key(subprogram.full_name()): 873 code=[ 874 StoreTemp( 875 expr=InvokeFunction( 876 expr=LoadAttr( 877 expr=LoadRef( 878 ref=self.builtins 879 ), 880 name="list", 881 nstype="module", 882 ), 883 args=[], 884 star=None, 885 dstar=None 886 ) 887 ) 888 ] 889 890 for arg in star_args: 891 code.append( 892 InvokeFunction( 893 expr=LoadAttr( 894 expr=LoadTemp(), 895 name="append" 896 ), 897 args=[arg], 898 star=None, 899 dstar=None 900 ) 901 ) 902 903 code += [ 904 Return(expr=LoadTemp(release=1)) 905 ] 906 907 invocation.stars[subprogram.full_name()] = InvokeBlock( 908 produces_result=1, 909 expr=LoadRef( 910 ref=Subprogram( 911 name=None, 912 returns_value=1, 913 params=[], 914 star=None, 915 dstar=None, 916 code=code 917 ) 918 ) 919 ) 920 921 return invocation.stars[subprogram.full_name()] 922 923 def make_namespace(self, items): 924 namespace = Namespace() 925 namespace.merge_items(items) 926 return namespace 927 928 # Namespace-related abstractions. 929 930 class Namespace: 931 932 """ 933 A local namespace which may either relate to a genuine set of function 934 locals or the initialisation of a structure or module. 935 """ 936 937 def __init__(self): 938 939 """ 940 Initialise the namespace with a mapping of local names to possible 941 types, a list of return values and of possible returned local 942 namespaces. The namespace also tracks the "current" types and a mapping 943 of temporary value names to types. 944 """ 945 946 self.names = {} 947 self.returns = [] 948 self.return_locals = [] 949 self.raises = [] 950 self.temp = {} 951 self.types = [] 952 953 def set_types(self, types): 954 self.types = types 955 956 def store(self, name, types): 957 self.names[name] = types 958 959 __setitem__ = store 960 961 def load(self, name): 962 return self.names[name] 963 964 __getitem__ = load 965 966 def revoke(self, name, type): 967 self.names[name].remove(type) 968 969 def revoke_exception_type(self, type): 970 self.raises.remove(type) 971 972 def merge_namespace(self, namespace, everything=1): 973 self.merge_items(namespace.names.items()) 974 if everything: 975 combine(self.returns, namespace.returns) 976 combine(self.return_locals, namespace.return_locals) 977 combine(self.raises, namespace.raises) 978 for name, values in namespace.temp.items(): 979 if values: 980 if not self.temp.has_key(name) or not self.temp[name]: 981 self.temp[name] = [[]] 982 combine(self.temp[name][-1], values[-1]) 983 984 def merge_items(self, items): 985 for name, types in items: 986 self.merge(name, types) 987 988 def merge(self, name, types): 989 if not self.names.has_key(name): 990 self.names[name] = types[:] 991 else: 992 existing = self.names[name] 993 combine(existing, types) 994 995 def snapshot(self): 996 997 "Make a snapshot of the locals and remember them." 998 999 namespace = Namespace() 1000 namespace.merge_namespace(self) 1001 self.return_locals.append(namespace) 1002 1003 def reset(self): 1004 1005 "Reset a namespace in preparation for merging with returned locals." 1006 1007 self.names = {} 1008 1009 def __repr__(self): 1010 return repr(self.names) 1011 1012 class Attribute: 1013 1014 """ 1015 An attribute abstraction, indicating the type of the attribute along with 1016 its context or origin. 1017 """ 1018 1019 def __init__(self, context, type): 1020 self.context = context 1021 self.type = type 1022 1023 def __eq__(self, other): 1024 return hasattr(other, "type") and other.type == self.type or other == self.type 1025 1026 def __repr__(self): 1027 return "Attribute(%s, %s)" % (repr(self.context), repr(self.type)) 1028 1029 class Self: 1030 1031 """ 1032 A program node encapsulating object/context information in an argument list. 1033 This is not particularly like Attribute, Class, Instance or other such 1034 things, since it actually appears in the program representation. 1035 """ 1036 1037 def __init__(self, attribute): 1038 self.types = [attribute] 1039 1040 def combine(target, additions): 1041 1042 """ 1043 Merge into the 'target' sequence the given 'additions', preventing duplicate 1044 items. 1045 """ 1046 1047 for addition in additions: 1048 if addition not in target: 1049 target.append(addition) 1050 1051 def find_attributes(structure, name): 1052 1053 """ 1054 Find for the given 'structure' all attributes for the given 'name', visiting 1055 base classes where appropriate and returning the attributes in order of 1056 descending precedence for all possible base classes. 1057 1058 The elements in the result list are 2-tuples which contain the attribute and 1059 the structure involved in accessing the attribute. 1060 """ 1061 1062 # First attempt to search the instance/class namespace. 1063 1064 try: 1065 l = structure.namespace.load(name) 1066 attributes = [] 1067 for attribute in l: 1068 attributes.append((attribute, structure)) 1069 1070 # If that does not work, attempt to investigate any class or base classes. 1071 1072 except KeyError: 1073 attributes = [] 1074 1075 # Investigate any instance's implementing class. 1076 1077 if isinstance(structure, Instance): 1078 for attr in structure.namespace.load("__class__"): 1079 cls = attr.type 1080 l = get_attributes(cls, name) 1081 combine(attributes, l) 1082 1083 # Investigate any class's base classes. 1084 1085 elif isinstance(structure, Class): 1086 1087 # If no base classes exist, return an indicator that no attribute 1088 # exists. 1089 1090 if not structure.base_refs: 1091 return [(None, structure)] 1092 1093 # Otherwise, find all possible base classes. 1094 1095 for base_refs in structure.base_refs: 1096 base_attributes = [] 1097 1098 # For each base class, find attributes either in the base 1099 # class or its own base classes. 1100 1101 for base_ref in base_refs: 1102 l = get_attributes(base_ref, name) 1103 combine(base_attributes, l) 1104 1105 combine(attributes, base_attributes) 1106 1107 return attributes 1108 1109 def get_attributes(structure, name): 1110 1111 """ 1112 Return all possible attributes for the given 'structure' having the given 1113 'name', wrapping each attribute in an Attribute object which includes 1114 context information for the attribute access. 1115 1116 The elements in the result list are 2-tuples which contain the attribute and 1117 the structure involved in accessing the attribute. 1118 """ 1119 1120 if isinstance(structure, Attribute): 1121 structure = structure.type 1122 results = [] 1123 for attribute, accessor in find_attributes(structure, name): 1124 if attribute is not None and isinstance(structure, Structure): 1125 results.append((Attribute(structure, attribute.type), accessor)) 1126 else: 1127 results.append((attribute, accessor)) 1128 return results 1129 1130 # Convenience functions. 1131 1132 def annotate(module, builtins=None): 1133 1134 """ 1135 Annotate the given 'module', also employing the optional 'builtins' module, 1136 if specified. 1137 """ 1138 1139 annotator = Annotator() 1140 if builtins is not None: 1141 annotator.process(module, builtins) 1142 else: 1143 annotator.process(module) 1144 1145 def annotate_all(modules, builtins): 1146 annotate(builtins) 1147 for module in modules: 1148 annotate(module, builtins) 1149 1150 # vim: tabstop=4 expandtab shiftwidth=4