1 #!/usr/bin/env python 2 3 """ 4 Inspect and obtain module structure. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 7 2014, 2015, 2016 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 from branching import BranchTracker 24 from common import get_argnames, init_item, predefined_constants 25 from modules import BasicModule, CacheWritingModule 26 from referencing import Reference 27 from resolving import NameResolving 28 from results import AccessRef, InstanceRef, InvocationRef, LiteralSequenceRef, \ 29 LocalNameRef, NameRef, ResolvedNameRef 30 import compiler 31 import sys 32 33 class InspectedModule(BasicModule, CacheWritingModule, NameResolving): 34 35 "A module inspector." 36 37 def __init__(self, name, importer): 38 39 "Initialise the module with basic details." 40 41 BasicModule.__init__(self, name, importer) 42 43 self.in_class = False 44 self.in_conditional = False 45 self.global_attr_accesses = {} 46 47 # Nested scope handling. 48 49 self.parent_function = None 50 self.propagated_names = {} 51 52 # Usage tracking. 53 54 self.trackers = [] 55 self.attr_accessor_branches = {} 56 57 def __repr__(self): 58 return "InspectedModule(%r, %r)" % (self.name, self.importer) 59 60 def parse(self, filename): 61 62 "Parse the file having the given 'filename'." 63 64 self.parse_file(filename) 65 66 # Inspect the module. 67 68 self.start_tracking_in_module() 69 70 # Detect and record imports and globals declared in the module. 71 72 self.assign_general_local("__name__", self.get_constant("str", self.name)) 73 self.assign_general_local("__file__", self.get_constant("str", filename)) 74 self.process_structure(self.astnode) 75 76 # Set the class of the module after the definition has occurred. 77 78 ref = self.get_builtin("object") 79 self.set_name("__class__", ref) 80 81 # Get module-level attribute usage details. 82 83 self.stop_tracking_in_module() 84 85 def complete(self): 86 87 "Complete the module inspection." 88 89 # Resolve names not definitively mapped to objects. 90 91 self.resolve() 92 93 # Define the invocation requirements in each namespace. 94 95 self.set_invocation_usage() 96 97 # Propagate to the importer information needed in subsequent activities. 98 99 self.propagate() 100 101 def set_invocation_usage(self): 102 103 """ 104 Discard the current invocation storage figures, retaining the maximum 105 values. 106 """ 107 108 for path, (current, maximum) in self.function_targets.items(): 109 self.importer.function_targets[path] = self.function_targets[path] = maximum 110 111 for path, (current, maximum) in self.function_arguments.items(): 112 self.importer.function_arguments[path] = self.function_arguments[path] = maximum 113 114 # Module structure traversal. 115 116 def process_structure_node(self, n): 117 118 "Process the individual node 'n'." 119 120 # Module global detection. 121 122 if isinstance(n, compiler.ast.Global): 123 self.process_global_node(n) 124 125 # Module import declarations. 126 127 elif isinstance(n, compiler.ast.From): 128 self.process_from_node(n) 129 130 elif isinstance(n, compiler.ast.Import): 131 self.process_import_node(n) 132 133 # Nodes using operator module functions. 134 135 elif isinstance(n, compiler.ast.Operator): 136 return self.process_operator_node(n) 137 138 elif isinstance(n, compiler.ast.AugAssign): 139 self.process_augassign_node(n) 140 141 elif isinstance(n, compiler.ast.Compare): 142 return self.process_compare_node(n) 143 144 elif isinstance(n, compiler.ast.Slice): 145 return self.process_slice_node(n) 146 147 elif isinstance(n, compiler.ast.Sliceobj): 148 return self.process_sliceobj_node(n) 149 150 elif isinstance(n, compiler.ast.Subscript): 151 return self.process_subscript_node(n) 152 153 # Namespaces within modules. 154 155 elif isinstance(n, compiler.ast.Class): 156 self.process_class_node(n) 157 158 elif isinstance(n, compiler.ast.Function): 159 self.process_function_node(n, n.name) 160 161 elif isinstance(n, compiler.ast.Lambda): 162 return self.process_lambda_node(n) 163 164 # Assignments. 165 166 elif isinstance(n, compiler.ast.Assign): 167 168 # Handle each assignment node. 169 170 for node in n.nodes: 171 self.process_assignment_node(node, n.expr) 172 173 # Assignments within non-Assign nodes. 174 175 elif isinstance(n, compiler.ast.AssName): 176 self.process_assignment_node(n, None) 177 178 elif isinstance(n, compiler.ast.AssAttr): 179 self.process_attribute_access(n) 180 181 # Accesses. 182 183 elif isinstance(n, compiler.ast.Getattr): 184 return self.process_attribute_access(n) 185 186 # Name recording for later testing. 187 188 elif isinstance(n, compiler.ast.Name): 189 return self.process_name_node(n) 190 191 # Conditional statement tracking. 192 193 elif isinstance(n, compiler.ast.For): 194 self.process_for_node(n) 195 196 elif isinstance(n, compiler.ast.While): 197 self.process_while_node(n) 198 199 elif isinstance(n, compiler.ast.If): 200 self.process_if_node(n) 201 202 elif isinstance(n, (compiler.ast.And, compiler.ast.Or)): 203 return self.process_logical_node(n) 204 205 # Exception control-flow tracking. 206 207 elif isinstance(n, compiler.ast.TryExcept): 208 self.process_try_node(n) 209 210 elif isinstance(n, compiler.ast.TryFinally): 211 self.process_try_finally_node(n) 212 213 # Control-flow modification statements. 214 215 elif isinstance(n, compiler.ast.Break): 216 self.trackers[-1].suspend_broken_branch() 217 218 elif isinstance(n, compiler.ast.Continue): 219 self.trackers[-1].suspend_continuing_branch() 220 221 elif isinstance(n, compiler.ast.Raise): 222 self.process_structure(n) 223 self.trackers[-1].abandon_branch() 224 225 elif isinstance(n, compiler.ast.Return): 226 self.process_structure(n) 227 self.trackers[-1].abandon_returning_branch() 228 229 # Invocations. 230 231 elif isinstance(n, compiler.ast.CallFunc): 232 return self.process_invocation_node(n) 233 234 # Constant usage. 235 236 elif isinstance(n, compiler.ast.Const): 237 return self.get_literal_instance(n, n.value.__class__.__name__) 238 239 elif isinstance(n, compiler.ast.Dict): 240 return self.get_literal_instance(n, "dict") 241 242 elif isinstance(n, compiler.ast.List): 243 return self.get_literal_instance(n, "list") 244 245 elif isinstance(n, compiler.ast.Tuple): 246 return self.get_literal_instance(n, "tuple") 247 248 # List comprehensions and if expressions. 249 250 elif isinstance(n, compiler.ast.ListComp): 251 self.process_listcomp_node(n) 252 253 elif isinstance(n, compiler.ast.IfExp): 254 self.process_ifexp_node(n) 255 256 # All other nodes are processed depth-first. 257 258 else: 259 self.process_structure(n) 260 261 # By default, no expression details are returned. 262 263 return None 264 265 # Specific node handling. 266 267 def process_assignment_node(self, n, expr): 268 269 "Process the individual node 'n' to be assigned the contents of 'expr'." 270 271 # Names and attributes are assigned the entire expression. 272 273 if isinstance(n, compiler.ast.AssName): 274 275 name_ref = expr and self.process_structure_node(expr) 276 277 # Name assignments populate either function namespaces or the 278 # general namespace hierarchy. 279 280 self.assign_general_local(n.name, name_ref) 281 282 # Record usage of the name. 283 284 self.record_name(n.name) 285 286 elif isinstance(n, compiler.ast.AssAttr): 287 if expr: self.process_structure_node(expr) 288 self.process_attribute_access(n) 289 290 # Lists and tuples are matched against the expression and their 291 # items assigned to expression items. 292 293 elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)): 294 self.process_assignment_node_items(n, expr) 295 296 # Slices and subscripts are permitted within assignment nodes. 297 298 elif isinstance(n, compiler.ast.Slice): 299 self.process_slice_node(n, expr) 300 301 elif isinstance(n, compiler.ast.Subscript): 302 self.process_subscript_node(n, expr) 303 304 def process_attribute_access(self, n): 305 306 "Process the given attribute access node 'n'." 307 308 # Obtain any completed chain and return the reference to it. 309 310 name_ref = self.process_attribute_chain(n) 311 if self.have_access_expression(n): 312 return name_ref 313 314 # Where the start of the chain of attributes has been reached, determine 315 # the complete access. 316 317 # Given a non-access node, this chain can be handled in its entirety, 318 # either being name-based and thus an access rooted on a name, or being 319 # based on some other node and thus an anonymous access of some kind. 320 321 path = self.get_namespace_path() 322 323 # Start with the the full attribute chain. 324 325 remaining = self.attrs 326 attrnames = ".".join(remaining) 327 328 # If the accessor cannot be identified, or where attributes 329 # remain in an attribute chain, record the anonymous accesses. 330 331 if not isinstance(name_ref, NameRef): # includes ResolvedNameRef 332 333 assignment = isinstance(n, compiler.ast.AssAttr) 334 335 init_item(self.attr_accesses, path, set) 336 self.attr_accesses[path].add(attrnames) 337 338 self.record_access_details(None, attrnames, assignment) 339 del self.attrs[0] 340 return 341 342 # Name-based accesses will handle the first attribute in a 343 # chain. 344 345 else: 346 attrname = remaining[0] 347 348 # Attribute assignments are used to identify instance attributes. 349 350 if isinstance(n, compiler.ast.AssAttr) and \ 351 self.in_class and self.in_function and n.expr.name == "self": 352 353 self.set_instance_attr(attrname) 354 355 # Record attribute usage using any name local to this namespace, 356 # if assigned in the namespace, or using an external name 357 # (presently just globals within classes). 358 359 name = self.get_name_for_tracking(name_ref.name, name_ref.final()) 360 tracker = self.trackers[-1] 361 362 immediate_access = len(self.attrs) == 1 363 assignment = immediate_access and isinstance(n, compiler.ast.AssAttr) 364 365 del self.attrs[0] 366 367 # Record global-based chains for subsequent resolution. 368 369 is_global = self.in_function and not self.function_locals[path].has_key(name) or \ 370 not self.in_function 371 372 if is_global: 373 self.record_global_access_details(name, attrnames) 374 375 # Make sure the name is being tracked: global names will not 376 # already be initialised in a branch and must be added 377 # explicitly. 378 379 if not tracker.have_name(name): 380 tracker.assign_names([name]) 381 if self.in_function: 382 self.scope_globals[path].add(name) 383 384 # Record attribute usage in the tracker, and record the branch 385 # information for the access. 386 387 branches = tracker.use_attribute(name, attrname) 388 389 if not branches: 390 print >>sys.stderr, "In %s, name %s is accessed using %s before an assignment." % ( 391 path, name, attrname) 392 branches = tracker.use_attribute(name, attrname) 393 394 self.record_branches_for_access(branches, name, attrnames) 395 access_number = self.record_access_details(name, attrnames, assignment) 396 return AccessRef(name, attrnames, access_number) 397 398 def process_class_node(self, n): 399 400 "Process the given class node 'n'." 401 402 path = self.get_namespace_path() 403 404 # To avoid notions of class "versions" where the same definition 405 # might be parameterised with different state and be referenced 406 # elsewhere (as base classes, for example), classes in functions or 407 # conditions are forbidden. 408 409 if self.in_function or self.in_conditional: 410 print >>sys.stderr, "In %s, class %s in function or conditional statement ignored." % ( 411 path, n.name) 412 return 413 414 # Resolve base classes. 415 416 bases = [] 417 418 for base in n.bases: 419 base_class = self.get_class(base) 420 421 if not base_class: 422 print >>sys.stderr, "In %s, class %s has unidentifiable base class: %s" % ( 423 path, n.name, base) 424 return 425 else: 426 bases.append(base_class) 427 428 # Record bases for the class and retain the class name. 429 430 class_name = self.get_object_path(n.name) 431 432 if not bases and class_name != "__builtins__.core.object": 433 ref = self.get_object("__builtins__.object") 434 bases.append(ref) 435 436 self.importer.classes[class_name] = self.classes[class_name] = bases 437 self.importer.subclasses[class_name] = set() 438 self.scope_globals[class_name] = set() 439 440 # Set the definition before entering the namespace rather than 441 # afterwards because methods may reference it. In normal Python, 442 # a class is not accessible until the definition is complete, but 443 # methods can generally reference it since upon being called the 444 # class will already exist. 445 446 self.set_definition(n.name, "<class>") 447 448 in_class = self.in_class 449 self.in_class = class_name 450 self.set_instance_attr("__class__", Reference("<class>", class_name)) 451 self.enter_namespace(n.name) 452 self.set_name("__fn__") # special instantiator attribute 453 self.set_name("__args__") # special instantiator attribute 454 self.assign_general_local("__name__", self.get_constant("str", class_name)) 455 self.process_structure_node(n.code) 456 self.exit_namespace() 457 self.in_class = in_class 458 459 def process_from_node(self, n): 460 461 "Process the given node 'n', importing from another module." 462 463 path = self.get_namespace_path() 464 465 module_name, names = self.get_module_name(n) 466 if module_name == self.name: 467 raise InspectError("Cannot import from the current module.", path, n) 468 469 self.importer.queue_module(module_name, self) 470 471 # Attempt to obtain the referenced objects. 472 473 for name, alias in n.names: 474 if name == "*": 475 raise InspectError("Only explicitly specified names can be imported from modules.", path, n) 476 477 # Explicit names. 478 479 ref = self.import_name_from_module(name, module_name) 480 value = ResolvedNameRef(alias or name, ref) 481 self.set_general_local(alias or name, value) 482 483 def process_function_node(self, n, name): 484 485 """ 486 Process the given function or lambda node 'n' with the given 'name'. 487 """ 488 489 is_lambda = isinstance(n, compiler.ast.Lambda) 490 491 # Where a function is declared conditionally, use a separate name for 492 # the definition, and assign the definition to the stated name. 493 494 if (self.in_conditional or self.in_function) and not is_lambda: 495 original_name = name 496 name = self.get_lambda_name() 497 else: 498 original_name = None 499 500 # Initialise argument and local records. 501 502 function_name = self.get_object_path(name) 503 504 argnames = self.importer.function_parameters[function_name] = \ 505 self.function_parameters[function_name] = get_argnames(n.argnames) 506 locals = self.function_locals[function_name] = {} 507 508 for argname in argnames: 509 locals[argname] = Reference("<var>") 510 511 globals = self.scope_globals[function_name] = set() 512 513 # Process the defaults. 514 515 defaults = self.importer.function_defaults[function_name] = \ 516 self.function_defaults[function_name] = [] 517 518 for argname, default in compiler.ast.get_defaults(n): 519 if default: 520 521 # Obtain any reference for the default. 522 523 name_ref = self.process_structure_node(default) 524 defaults.append((argname, name_ref.is_name() and name_ref.reference() or Reference("<var>"))) 525 526 # Reset conditional tracking to focus on the function contents. 527 528 parent_function = self.parent_function 529 self.parent_function = self.in_function and self.get_namespace_path() or None 530 531 in_conditional = self.in_conditional 532 self.in_conditional = False 533 534 in_function = self.in_function 535 self.in_function = function_name 536 537 self.enter_namespace(name) 538 539 # Track attribute usage within the namespace. 540 541 path = self.get_namespace_path() 542 init_item(self.propagated_names, path, set) 543 544 self.start_tracking(locals) 545 self.process_structure_node(n.code) 546 self.stop_tracking() 547 548 # Propagate names from parent scopes. 549 550 for local in self.propagated_names[path]: 551 if not local in argnames and self.trackers[-1].have_name(local): 552 argnames.append(local) 553 defaults.append((local, Reference("<var>"))) 554 self.set_function_local(local) 555 556 # Exit to the parent and note propagated names. 557 558 self.exit_namespace() 559 560 parent = self.get_namespace_path() 561 if self.propagated_names.has_key(parent): 562 for local in self.propagated_names[path]: 563 self.propagated_names[parent].add(local) 564 565 # Update flags. 566 567 self.in_function = in_function 568 self.in_conditional = in_conditional 569 self.parent_function = parent_function 570 571 # Define the function using the appropriate name. 572 573 self.set_definition(name, "<function>") 574 575 # Where a function is set conditionally, assign the name. 576 577 if original_name: 578 self.process_assignment_for_function(original_name, name) 579 580 def process_global_node(self, n): 581 582 """ 583 Process the given "global" node 'n'. 584 """ 585 586 path = self.get_namespace_path() 587 588 if path != self.name: 589 self.scope_globals[path].update(n.names) 590 591 def process_if_node(self, n): 592 593 """ 594 Process the given "if" node 'n'. 595 """ 596 597 tracker = self.trackers[-1] 598 tracker.new_branchpoint() 599 600 for test, body in n.tests: 601 self.process_structure_node(test) 602 603 tracker.new_branch() 604 605 in_conditional = self.in_conditional 606 self.in_conditional = True 607 self.process_structure_node(body) 608 self.in_conditional = in_conditional 609 610 tracker.shelve_branch() 611 612 # Maintain a branch for the else clause. 613 614 tracker.new_branch() 615 if n.else_: 616 self.process_structure_node(n.else_) 617 tracker.shelve_branch() 618 619 tracker.merge_branches() 620 621 def process_ifexp_node(self, n): 622 623 "Process the given if expression node 'n'." 624 625 name_ref = self.process_structure_node(self.convert_ifexp_node(n)) 626 627 path = self.get_namespace_path() 628 self.allocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 629 self.deallocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 630 631 return InvocationRef(name_ref) 632 633 def process_import_node(self, n): 634 635 "Process the given import node 'n'." 636 637 path = self.get_namespace_path() 638 639 # Load the mentioned module. 640 641 for name, alias in n.names: 642 if name == self.name: 643 raise InspectError("Cannot import the current module.", path, n) 644 if not alias and len(n.names) > 1: 645 raise InspectError("Imported modules must be aliased unless a simple module is imported.", path, n) 646 647 self.set_module(alias or name.split(".")[-1], name) 648 self.importer.queue_module(name, self, True) 649 650 def process_invocation_node(self, n): 651 652 "Process the given invocation node 'n'." 653 654 path = self.get_namespace_path() 655 656 self.allocate_arguments(path, n.args) 657 658 try: 659 # Process the expression, obtaining any identified reference. 660 661 name_ref = self.process_structure_node(n.node) 662 663 # Process the arguments. 664 665 for arg in n.args: 666 self.process_structure_node(arg) 667 668 # Detect class invocations. 669 670 if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"): 671 return InstanceRef(name_ref.reference().instance_of()) 672 673 elif isinstance(name_ref, NameRef): 674 return InvocationRef(name_ref) 675 676 return None 677 678 finally: 679 self.deallocate_arguments(path, n.args) 680 681 def process_lambda_node(self, n): 682 683 "Process the given lambda node 'n'." 684 685 name = self.get_lambda_name() 686 self.process_function_node(n, name) 687 688 origin = self.get_object_path(name) 689 return ResolvedNameRef(name, Reference("<function>", origin)) 690 691 def process_listcomp_node(self, n): 692 693 "Process the given list comprehension node 'n'." 694 695 name_ref = self.process_structure_node(self.convert_listcomp_node(n)) 696 697 path = self.get_namespace_path() 698 self.allocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 699 self.deallocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 700 701 return InvocationRef(name_ref) 702 703 def process_logical_node(self, n): 704 705 "Process the given operator node 'n'." 706 707 self.process_operator_chain(n.nodes, self.process_structure_node) 708 709 def process_name_node(self, n): 710 711 "Process the given name node 'n'." 712 713 path = self.get_namespace_path() 714 715 # Special names. 716 717 if n.name.startswith("$"): 718 value = self.get_special(n.name) 719 if value: 720 return value 721 722 # Special case for operator functions introduced through code 723 # transformations. 724 725 if n.name.startswith("$op"): 726 727 # Obtain the location of the actual function defined in the operator 728 # package. 729 730 op = n.name[len("$op"):] 731 732 # Attempt to get a reference. 733 734 ref = self.import_name_from_module(op, "operator") 735 736 # Record the imported name and provide the resolved name reference. 737 # NOTE: Maybe use a different class. 738 739 value = ResolvedNameRef(n.name, ref) 740 self.set_special(n.name, value) 741 return value 742 743 # Record usage of the name. 744 745 self.record_name(n.name) 746 747 # Search for unknown names in non-function scopes immediately. 748 # External names in functions are resolved later. 749 750 ref = self.find_name(n.name) 751 if ref: 752 return ResolvedNameRef(n.name, ref) 753 754 # Global name. 755 756 elif self.in_function and n.name in self.scope_globals[path]: 757 return NameRef(n.name) 758 759 # Examine other names. 760 761 else: 762 tracker = self.trackers[-1] 763 764 # Check local names. 765 766 branches = tracker.tracking_name(n.name) 767 768 # Find names inherited from a parent scope. 769 770 if not branches and self.parent_function: 771 branches = tracker.have_name(n.name) 772 if branches: 773 self.propagate_name(n.name) 774 775 # Local or inherited name. 776 777 if branches: 778 self.record_branches_for_access(branches, n.name, None) 779 access_number = self.record_access_details(n.name, None, False) 780 return LocalNameRef(n.name, access_number) 781 782 # Possible global name. 783 784 else: 785 return NameRef(n.name) 786 787 def process_operator_chain(self, nodes, fn): 788 789 """ 790 Process the given chain of 'nodes', applying 'fn' to each node or item. 791 Each node starts a new conditional region, effectively making a deeply- 792 nested collection of if-like statements. 793 """ 794 795 tracker = self.trackers[-1] 796 797 for item in nodes: 798 tracker.new_branchpoint() 799 tracker.new_branch() 800 fn(item) 801 802 for item in nodes[:-1]: 803 tracker.shelve_branch() 804 tracker.new_branch() 805 tracker.shelve_branch() 806 tracker.merge_branches() 807 808 tracker.shelve_branch() 809 tracker.merge_branches() 810 811 def process_try_node(self, n): 812 813 """ 814 Process the given "try...except" node 'n'. 815 """ 816 817 tracker = self.trackers[-1] 818 tracker.new_branchpoint() 819 820 self.process_structure_node(n.body) 821 822 for name, var, handler in n.handlers: 823 if name is not None: 824 self.process_structure_node(name) 825 826 # Any abandoned branches from the body can now be resumed in a new 827 # branch. 828 829 tracker.resume_abandoned_branches() 830 831 # Establish the local for the handler. 832 833 if var is not None: 834 self.process_structure_node(var) 835 if handler is not None: 836 self.process_structure_node(handler) 837 838 tracker.shelve_branch() 839 840 # The else clause maintains the usage from the body but without the 841 # abandoned branches since they would never lead to the else clause 842 # being executed. 843 844 if n.else_: 845 tracker.new_branch() 846 self.process_structure_node(n.else_) 847 tracker.shelve_branch() 848 849 # Without an else clause, a null branch propagates the successful 850 # outcome. 851 852 else: 853 tracker.new_branch() 854 tracker.shelve_branch() 855 856 tracker.merge_branches() 857 858 def process_try_finally_node(self, n): 859 860 """ 861 Process the given "try...finally" node 'n'. 862 """ 863 864 tracker = self.trackers[-1] 865 self.process_structure_node(n.body) 866 867 # Any abandoned branches from the body can now be resumed. 868 869 branches = tracker.resume_all_abandoned_branches() 870 self.process_structure_node(n.final) 871 872 # At the end of the finally clause, abandoned branches are discarded. 873 874 tracker.restore_active_branches(branches) 875 876 def process_while_node(self, n): 877 878 "Process the given while node 'n'." 879 880 tracker = self.trackers[-1] 881 tracker.new_branchpoint(loop_node=True) 882 883 # Evaluate any test or iterator outside the loop. 884 885 self.process_structure_node(n.test) 886 887 # Propagate attribute usage to branches. 888 889 tracker.new_branch(loop_node=True) 890 891 # Enter the loop. 892 893 in_conditional = self.in_conditional 894 self.in_conditional = True 895 self.process_structure_node(n.body) 896 self.in_conditional = in_conditional 897 898 # Continuing branches are resumed before any test. 899 900 tracker.resume_continuing_branches() 901 902 # Evaluate any continuation test within the body. 903 904 self.process_structure_node(n.test) 905 906 tracker.shelve_branch(loop_node=True) 907 908 # Support the non-looping condition. 909 910 tracker.new_branch() 911 tracker.shelve_branch() 912 913 tracker.merge_branches() 914 915 # Evaluate any else clause outside branches. 916 917 if n.else_: 918 self.process_structure_node(n.else_) 919 920 # Connect broken branches to the code after any loop. 921 922 tracker.resume_broken_branches() 923 924 # Branch tracking methods. 925 926 def start_tracking(self, names): 927 928 """ 929 Start tracking attribute usage for names in the current namespace, 930 immediately registering the given 'names'. 931 """ 932 933 path = self.get_namespace_path() 934 parent = self.trackers[-1] 935 tracker = BranchTracker() 936 self.trackers.append(tracker) 937 938 # For functions created from expressions or for functions within 939 # functions, propagate usage to the new namespace. 940 941 if self.parent_function: 942 tracker.inherit_branches(parent, names) 943 944 # Record the given names established as new branches. 945 946 tracker.assign_names(names) 947 948 def assign_name(self, name, name_ref): 949 950 "Assign to 'name' the given 'name_ref' in the current namespace." 951 952 name = self.get_name_for_tracking(name) 953 self.trackers[-1].assign_names([name], [name_ref]) 954 955 def stop_tracking(self): 956 957 """ 958 Stop tracking attribute usage, recording computed usage for the current 959 namespace. 960 """ 961 962 path = self.get_namespace_path() 963 tracker = self.trackers.pop() 964 self.record_assignments_for_access(tracker) 965 966 self.attr_usage[path] = tracker.get_all_usage() 967 self.name_initialisers[path] = tracker.get_all_values() 968 969 def start_tracking_in_module(self): 970 971 "Start tracking attribute usage in the module." 972 973 tracker = BranchTracker() 974 self.trackers.append(tracker) 975 976 def stop_tracking_in_module(self): 977 978 "Stop tracking attribute usage in the module." 979 980 tracker = self.trackers[0] 981 self.record_assignments_for_access(tracker) 982 self.attr_usage[self.name] = tracker.get_all_usage() 983 self.name_initialisers[self.name] = tracker.get_all_values() 984 985 def propagate_name(self, name): 986 987 "Propagate the given 'name' into the current namespace." 988 989 path = self.get_namespace_path() 990 self.propagated_names[path].add(name) 991 992 def record_assignments_for_access(self, tracker): 993 994 """ 995 For the current path, use the given 'tracker' to record assignment 996 version information for attribute accesses. 997 """ 998 999 path = self.get_path_for_access() 1000 1001 if not self.attr_accessor_branches.has_key(path): 1002 return 1003 1004 init_item(self.attr_accessors, path, dict) 1005 attr_accessors = self.attr_accessors[path] 1006 1007 # Obtain the branches applying during each access. 1008 1009 for access, all_branches in self.attr_accessor_branches[path].items(): 1010 name, attrnames = access 1011 init_item(attr_accessors, access, list) 1012 1013 # Obtain the assignments applying to each branch. 1014 1015 for branches in all_branches: 1016 positions = tracker.get_assignment_positions_for_branches(name, branches) 1017 1018 # Detect missing name information. 1019 1020 if None in positions: 1021 globals = self.global_attr_accesses.get(path) 1022 accesses = globals and globals.get(name) 1023 if not accesses: 1024 print >>sys.stderr, "In %s, %s may not be defined when used." % ( 1025 self.get_namespace_path(), name) 1026 positions.remove(None) 1027 1028 attr_accessors[access].append(positions) 1029 1030 def record_branches_for_access(self, branches, name, attrnames): 1031 1032 """ 1033 Record the given 'branches' for an access involving the given 'name' and 1034 'attrnames'. 1035 """ 1036 1037 access = name, attrnames 1038 path = self.get_path_for_access() 1039 1040 init_item(self.attr_accessor_branches, path, dict) 1041 attr_accessor_branches = self.attr_accessor_branches[path] 1042 1043 init_item(attr_accessor_branches, access, list) 1044 attr_accessor_branches[access].append(branches) 1045 1046 def record_access_details(self, name, attrnames, assignment): 1047 1048 """ 1049 For the given 'name' and 'attrnames', record an access indicating 1050 whether 'assignment' is occurring. 1051 1052 These details correspond to accesses otherwise recorded by the attribute 1053 accessor and attribute access dictionaries. 1054 """ 1055 1056 access = name, attrnames 1057 path = self.get_path_for_access() 1058 1059 init_item(self.attr_access_modifiers, path, dict) 1060 init_item(self.attr_access_modifiers[path], access, list) 1061 1062 access_number = len(self.attr_access_modifiers[path][access]) 1063 self.attr_access_modifiers[path][access].append(assignment) 1064 return access_number 1065 1066 def record_global_access_details(self, name, attrnames): 1067 1068 """ 1069 Record details of a global access via the given 'name' involving the 1070 indicated 'attrnames'. 1071 """ 1072 1073 path = self.get_namespace_path() 1074 1075 init_item(self.global_attr_accesses, path, dict) 1076 init_item(self.global_attr_accesses[path], name, set) 1077 self.global_attr_accesses[path][name].add(attrnames) 1078 1079 # Namespace modification. 1080 1081 def record_name(self, name): 1082 1083 "Record the use of 'name' in a namespace." 1084 1085 path = self.get_namespace_path() 1086 init_item(self.names_used, path, set) 1087 self.names_used[path].add(name) 1088 1089 def set_module(self, name, module_name): 1090 1091 """ 1092 Set a module in the current namespace using the given 'name' associated 1093 with the corresponding 'module_name'. 1094 """ 1095 1096 if name: 1097 self.set_general_local(name, Reference("<module>", module_name)) 1098 1099 def set_definition(self, name, kind): 1100 1101 """ 1102 Set the definition having the given 'name' and 'kind'. 1103 1104 Definitions are set in the static namespace hierarchy, but they can also 1105 be recorded for function locals. 1106 """ 1107 1108 if self.is_global(name): 1109 print >>sys.stderr, "In %s, %s is defined as being global." % ( 1110 self.get_namespace_path(), name) 1111 1112 path = self.get_object_path(name) 1113 self.set_object(path, kind) 1114 1115 ref = self.get_object(path) 1116 if ref.get_kind() == "<var>": 1117 print >>sys.stderr, "In %s, %s is defined more than once." % ( 1118 self.get_namespace_path(), name) 1119 1120 if not self.is_global(name) and self.in_function: 1121 self.set_function_local(name, ref) 1122 1123 def set_function_local(self, name, ref=None): 1124 1125 "Set the local with the given 'name' and optional 'ref'." 1126 1127 locals = self.function_locals[self.get_namespace_path()] 1128 multiple = not ref or locals.has_key(name) and locals[name] != ref 1129 locals[name] = multiple and Reference("<var>") or ref 1130 1131 def assign_general_local(self, name, name_ref): 1132 1133 """ 1134 Set for 'name' the given 'name_ref', recording the name for attribute 1135 usage tracking. 1136 """ 1137 1138 self.set_general_local(name, name_ref) 1139 self.assign_name(name, name_ref) 1140 1141 def set_general_local(self, name, value=None): 1142 1143 """ 1144 Set the 'name' with optional 'value' in any kind of local namespace, 1145 where the 'value' should be a reference if specified. 1146 """ 1147 1148 init_value = self.get_initialising_value(value) 1149 1150 # Module global names. 1151 1152 if self.is_global(name): 1153 path = self.get_global_path(name) 1154 self.set_object(path, init_value) 1155 1156 # Function local names. 1157 1158 elif self.in_function: 1159 path = self.get_object_path(name) 1160 self.set_function_local(name, init_value) 1161 1162 # Other namespaces (classes). 1163 1164 else: 1165 path = self.get_object_path(name) 1166 self.set_name(name, init_value) 1167 1168 def set_name(self, name, ref=None): 1169 1170 "Attach the 'name' with optional 'ref' to the current namespace." 1171 1172 self.set_object(self.get_object_path(name), ref) 1173 1174 def set_instance_attr(self, name, ref=None): 1175 1176 """ 1177 Add an instance attribute of the given 'name' to the current class, 1178 using the optional 'ref'. 1179 """ 1180 1181 init_item(self.instance_attrs, self.in_class, set) 1182 self.instance_attrs[self.in_class].add(name) 1183 1184 if ref: 1185 init_item(self.instance_attr_constants, self.in_class, dict) 1186 self.instance_attr_constants[self.in_class][name] = ref 1187 1188 def get_initialising_value(self, value): 1189 1190 "Return a suitable initialiser reference for 'value'." 1191 1192 if isinstance(value, (NameRef, AccessRef, InstanceRef)): # includes LiteralSequenceRef, ResolvedNameRef 1193 return value.reference() 1194 1195 # In general, invocations do not produce known results. However, the 1196 # name initialisers are resolved once a module has been inspected. 1197 1198 elif isinstance(value, InvocationRef): 1199 return None 1200 1201 else: 1202 return value 1203 1204 # Static, program-relative naming. 1205 1206 def find_name(self, name): 1207 1208 """ 1209 Return the qualified name for the given 'name' used in the current 1210 non-function namespace. 1211 """ 1212 1213 path = self.get_namespace_path() 1214 ref = None 1215 1216 if not self.in_function and name not in predefined_constants: 1217 if self.in_class: 1218 ref = self.get_object(self.get_object_path(name)) 1219 if not ref: 1220 ref = self.get_global_or_builtin(name) 1221 1222 return ref 1223 1224 def get_class(self, node): 1225 1226 """ 1227 Use the given 'node' to obtain the identity of a class. Return a 1228 reference for the class. Unresolved dependencies are permitted and must 1229 be resolved later. 1230 """ 1231 1232 ref = self._get_class(node) 1233 return ref.has_kind(["<class>", "<depends>"]) and ref or None 1234 1235 def _get_class(self, node): 1236 1237 """ 1238 Use the given 'node' to find a class definition. Return a reference to 1239 the class. 1240 """ 1241 1242 if isinstance(node, compiler.ast.Getattr): 1243 1244 # Obtain the identity of the access target. 1245 1246 ref = self._get_class(node.expr) 1247 1248 # Where the target is a class or module, obtain the identity of the 1249 # attribute. 1250 1251 if ref.has_kind(["<function>", "<var>"]): 1252 return None 1253 else: 1254 attrname = "%s.%s" % (ref.get_origin(), node.attrname) 1255 return self.get_object(attrname) 1256 1257 # Names can be module-level or built-in. 1258 1259 elif isinstance(node, compiler.ast.Name): 1260 1261 # Record usage of the name and attempt to identify it. 1262 1263 self.record_name(node.name) 1264 return self.get_global_or_builtin(node.name) 1265 else: 1266 return None 1267 1268 def get_constant(self, name, value): 1269 1270 "Return a constant reference for the given type 'name' and 'value'." 1271 1272 ref = self.get_builtin_class(name) 1273 return self.get_constant_reference(ref, value) 1274 1275 def get_literal_instance(self, n, name): 1276 1277 "For node 'n', return a reference to an instance of 'name'." 1278 1279 # Get a reference to the built-in class. 1280 1281 ref = self.get_builtin_class(name) 1282 1283 # Obtain the details of the literal itself. 1284 # An alias to the type is generated for sequences. 1285 1286 if name in ("dict", "list", "tuple"): 1287 self.set_special_literal(name, ref) 1288 return self.process_literal_sequence_node(n, name, ref, LiteralSequenceRef) 1289 1290 # Constant values are independently recorded. 1291 1292 else: 1293 return self.get_constant_reference(ref, n.value) 1294 1295 # Functions and invocations. 1296 1297 def allocate_arguments(self, path, args): 1298 1299 """ 1300 Allocate temporary argument storage using current and maximum 1301 requirements for the given 'path' and 'args'. 1302 """ 1303 1304 init_item(self.function_targets, path, lambda: [0, 0]) 1305 t = self.function_targets[path] 1306 t[0] += 1 1307 t[1] = max(t[0], t[1]) 1308 1309 init_item(self.function_arguments, path, lambda: [0, 0]) 1310 t = self.function_arguments[path] 1311 t[0] += len(args) + 1 1312 t[1] = max(t[0], t[1]) 1313 1314 def deallocate_arguments(self, path, args): 1315 1316 "Deallocate temporary argument storage for the given 'path' and 'args'." 1317 1318 self.function_targets[path][0] -= 1 1319 self.function_arguments[path][0] -= len(args) + 1 1320 1321 # vim: tabstop=4 expandtab shiftwidth=4