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 * 25 from modules import * 26 from os import listdir 27 from os.path import extsep, split, splitext 28 from referencing import Reference 29 import compiler 30 import sys 31 32 class AccessRef(Result): 33 34 """ 35 A reference to an attribute access that is generally only returned from a 36 processed access for possible initialiser resolution for assignments. 37 """ 38 39 def __init__(self, original_name, attrnames, number): 40 self.original_name = original_name 41 self.attrnames = attrnames 42 self.number = number 43 44 def reference(self): 45 return None 46 47 def __repr__(self): 48 return "AccessRef(%r, %r, %r)" % (self.original_name, self.attrnames, self.number) 49 50 class InvocationRef(Result): 51 52 "An invocation of a name reference." 53 54 def __init__(self, name_ref): 55 self.name_ref = name_ref 56 57 def __repr__(self): 58 return "InvocationRef(%r)" % self.name_ref 59 60 class InspectedModule(BasicModule, CacheWritingModule): 61 62 "A module inspector." 63 64 def __init__(self, name, importer): 65 BasicModule.__init__(self, name, importer) 66 self.in_class = False 67 self.in_conditional = False 68 self.global_attr_accesses = {} 69 70 # Nested scope handling. 71 72 self.parent_function = None 73 self.propagated_names = {} 74 75 # Usage tracking. 76 77 self.trackers = [] 78 self.attr_accessor_branches = {} 79 80 def __repr__(self): 81 return "InspectedModule(%r, %r)" % (self.name, self.importer) 82 83 def parse(self, filename): 84 85 "Parse the file having the given 'filename'." 86 87 self.parse_file(filename) 88 89 # Inspect the module. 90 91 self.start_tracking_in_module() 92 93 # Detect and record imports and globals declared in the module. 94 95 self.assign_general_local("__name__", self.get_constant("str", self.name)) 96 self.assign_general_local("__file__", self.get_constant("str", filename)) 97 self.process_structure(self.astnode) 98 99 # Set the class of the module after the definition has occurred. 100 101 ref = self.get_builtin("object") 102 self.set_name("__class__", ref) 103 104 # Get module-level attribute usage details. 105 106 self.stop_tracking_in_module() 107 108 # Check names used and resolve them. 109 110 self.register_submodules() 111 self.loaded = True 112 113 def register_submodules(self): 114 115 "For package modules add submodule details." 116 117 if splitext(split(self.filename)[1])[0] == "__init__": 118 for subname in listdir(split(self.filename)[0]): 119 name, ext = splitext(subname) 120 121 # Add modules whose names are not already defined as globals. 122 123 if ext == ("%spy" % extsep) and name != "__init__" and not self.get_global(name): 124 module_name = self.get_global_path(name) 125 top, submodule = self.get_module(module_name, True) 126 self.set_module(name, submodule, hidden=True) 127 128 def check_special(self): 129 130 "Check special names." 131 132 for name, value in self.special.items(): 133 if value.has_kind("<depends>"): 134 self.find_imported_name(name, self.name) 135 self.special[name] = self.get_object(value.get_origin()) 136 137 def check_names_used(self): 138 139 "Check the names used by each function." 140 141 for path in self.names_used.keys(): 142 self.check_names_used_for_path(path) 143 144 def check_names_used_for_path(self, path): 145 146 "Check the names used by the given 'path'." 147 148 names = self.names_used.get(path) 149 if not names: 150 return 151 152 in_function = self.function_locals.has_key(path) 153 154 for name in names: 155 if name in predefined_constants or in_function and name in self.function_locals[path]: 156 continue 157 158 # Resolve names that have been imported locally. 159 160 self.find_imported_name(name, path) 161 162 # Find local definitions. 163 164 key = "%s.%s" % (path, name) 165 ref = self.get_object(key) 166 if ref: 167 self.name_references[key] = ref.final() or key 168 self.resolve_accesses(path, name, ref) 169 continue 170 171 # Resolve names that have been imported globally. 172 173 self.find_imported_name(name, self.name) 174 175 # Find global or built-in definitions. 176 177 ref = self.get_global_or_builtin(name) 178 objpath = ref and (ref.final() or ref.get_name()) 179 if objpath: 180 self.name_references[key] = objpath 181 self.resolve_accesses(path, name, ref) 182 continue 183 184 print >>sys.stderr, "Name not recognised: %s in %s" % (name, path) 185 init_item(self.names_missing, path, set) 186 self.names_missing[path].add(name) 187 188 def resolve_members(self): 189 190 "Resolve any members referring to deferred references." 191 192 for name, ref in self.objects.items(): 193 if ref.has_kind("<depends>"): 194 ref = self.get_object(ref.get_origin()) 195 ref = ref.alias(name) 196 self.importer.objects[name] = self.objects[name] = ref 197 198 def resolve_accesses(self, path, name, ref): 199 200 """ 201 Resolve any unresolved accesses in the function at the given 'path' 202 for the given 'name' corresponding to the indicated 'ref'. Note that 203 this mechanism cannot resolve things like inherited methods because 204 they are not recorded as program objects in their inherited locations. 205 """ 206 207 attr_accesses = self.global_attr_accesses.get(path) 208 all_attrnames = attr_accesses and attr_accesses.get(name) 209 210 if not all_attrnames: 211 return 212 213 # Insist on constant accessors. 214 215 if not ref.has_kind(["<class>", "<module>"]): 216 return 217 218 found_attrnames = set() 219 220 for attrnames in all_attrnames: 221 222 # Start with the resolved name, adding attributes. 223 224 attrs = ref.get_path() 225 remaining = attrnames.split(".") 226 last_ref = ref 227 228 # Add each component, testing for a constant object. 229 230 while remaining: 231 attrname = remaining[0] 232 attrs.append(attrname) 233 del remaining[0] 234 235 # Find any constant object reference. 236 237 attr_ref = self.get_object(".".join(attrs)) 238 239 # Non-constant accessors terminate the traversal. 240 241 if not attr_ref.has_kind(["<class>", "<module>", "<function>"]): 242 243 # Provide the furthest constant accessor unless the final 244 # access can be resolved. 245 246 if remaining: 247 remaining.insert(0, attrs.pop()) 248 else: 249 last_ref = attr_ref 250 break 251 252 # A module may expose an attribute imported from a hidden 253 # module. 254 255 elif last_ref.has_kind("<module>"): 256 module, leaf_module = self.get_module(last_ref.get_origin()) 257 self.find_imported_name(attrname, module.name, module) 258 259 # Follow any reference to a constant object. 260 # Where the given name refers to an object in another location, 261 # switch to the other location in order to be able to test its 262 # attributes. 263 264 last_ref = attr_ref 265 attrs = attr_ref.get_path() 266 267 # Record a constant accessor only if an object was found 268 # that is different from the namespace involved. 269 270 if last_ref: 271 objpath = ".".join(attrs) 272 if objpath != path: 273 274 # Establish a constant access. 275 276 init_item(self.const_accesses, path, dict) 277 self.const_accesses[path][(name, attrnames)] = (objpath, last_ref, ".".join(remaining)) 278 279 if len(attrs) > 1: 280 found_attrnames.add(attrs[1]) 281 282 # Remove any usage records for the name. 283 284 if found_attrnames: 285 286 # NOTE: Should be only one name version. 287 288 versions = [] 289 for version in self.attr_usage[path][name]: 290 new_usage = set() 291 for usage in version: 292 if found_attrnames.intersection(usage): 293 new_usage.add(tuple(set(usage).difference(found_attrnames))) 294 else: 295 new_usage.add(usage) 296 versions.append(new_usage) 297 298 self.attr_usage[path][name] = versions 299 300 def resolve_initialisers(self): 301 302 "Resolve initialiser values for names." 303 304 # Get the initialisers in each namespace. 305 306 for path, name_initialisers in self.name_initialisers.items(): 307 const_accesses = self.const_accesses.get(path) 308 309 # Resolve values for each name in a scope. 310 311 for name, values in name_initialisers.items(): 312 if path == self.name: 313 assigned_path = name 314 else: 315 assigned_path = "%s.%s" % (path, name) 316 317 initialised_names = {} 318 aliased_names = {} 319 320 for i, name_ref in enumerate(values): 321 322 # Unwrap invocations. 323 324 if isinstance(name_ref, InvocationRef): 325 invocation = True 326 name_ref = name_ref.name_ref 327 else: 328 invocation = False 329 330 # Obtain a usable reference from names or constants. 331 332 if isinstance(name_ref, ResolvedNameRef): 333 if not name_ref.reference(): 334 continue 335 ref = name_ref.reference() 336 337 # Obtain a reference from instances. 338 339 elif isinstance(name_ref, InstanceRef): 340 if not name_ref.reference(): 341 continue 342 ref = name_ref.reference() 343 344 # Resolve accesses that employ constants. 345 346 elif isinstance(name_ref, AccessRef): 347 ref = None 348 349 if const_accesses: 350 resolved_access = const_accesses.get((name_ref.original_name, name_ref.attrnames)) 351 if resolved_access: 352 objpath, ref, remaining_attrnames = resolved_access 353 if remaining_attrnames: 354 ref = None 355 356 # Accesses that do not employ constants cannot be resolved, 357 # but they may be resolvable later. 358 359 if not ref: 360 if not invocation: 361 aliased_names[i] = name_ref.original_name, name_ref.attrnames, name_ref.number 362 continue 363 364 # Attempt to resolve a plain name reference. 365 366 elif isinstance(name_ref, LocalNameRef): 367 key = "%s.%s" % (path, name_ref.name) 368 origin = self.name_references.get(key) 369 370 # Accesses that do not refer to known static objects 371 # cannot be resolved, but they may be resolvable later. 372 373 if not origin: 374 if not invocation: 375 aliased_names[i] = name_ref.name, None, name_ref.number 376 continue 377 378 ref = self.get_object(origin) 379 if not ref: 380 continue 381 382 elif isinstance(name_ref, NameRef): 383 key = "%s.%s" % (path, name_ref.name) 384 origin = self.name_references.get(key) 385 if not origin: 386 continue 387 388 ref = self.get_object(origin) 389 if not ref: 390 continue 391 392 else: 393 continue 394 395 # Convert class invocations to instances. 396 397 if invocation: 398 ref = ref.has_kind("<class>") and ref.instance_of() or None 399 400 if ref: 401 initialised_names[i] = ref 402 403 if initialised_names: 404 self.initialised_names[assigned_path] = initialised_names 405 if aliased_names: 406 self.aliased_names[assigned_path] = aliased_names 407 408 def resolve_literals(self): 409 410 "Resolve constant value types." 411 412 # Get the constants defined in each namespace. 413 414 for path, constants in self.constants.items(): 415 for constant, n in constants.items(): 416 objpath = "%s.$c%d" % (path, n) 417 _constant, value_type = self.constant_values[objpath] 418 self.initialised_names[objpath] = {0 : Reference("<instance>", value_type)} 419 420 # Get the literals defined in each namespace. 421 422 for path, literals in self.literals.items(): 423 for n in range(0, literals): 424 objpath = "%s.$C%d" % (path, n) 425 value_type = self.literal_types[objpath] 426 self.initialised_names[objpath] = {0 : Reference("<instance>", value_type)} 427 428 def remove_redundant_accessors(self): 429 430 "Remove now-redundant modifier and accessor information." 431 432 for path, const_accesses in self.const_accesses.items(): 433 accesses = self.attr_accessors.get(path) 434 modifiers = self.attr_access_modifiers.get(path) 435 if not accesses: 436 continue 437 for access in const_accesses.keys(): 438 if accesses.has_key(access): 439 del accesses[access] 440 if modifiers and modifiers.has_key(access): 441 del modifiers[access] 442 443 def set_invocation_usage(self): 444 445 """ 446 Discard the current invocation storage figures, retaining the maximum 447 values. 448 """ 449 450 for path, (current, maximum) in self.function_targets.items(): 451 self.importer.function_targets[path] = self.function_targets[path] = maximum 452 453 for path, (current, maximum) in self.function_arguments.items(): 454 self.importer.function_arguments[path] = self.function_arguments[path] = maximum 455 456 # Module structure traversal. 457 458 def process_structure_node(self, n): 459 460 "Process the individual node 'n'." 461 462 # Module global detection. 463 464 if isinstance(n, compiler.ast.Global): 465 self.process_global_node(n) 466 467 # Module import declarations. 468 469 elif isinstance(n, compiler.ast.From): 470 self.process_from_node(n) 471 472 elif isinstance(n, compiler.ast.Import): 473 self.process_import_node(n) 474 475 # Nodes using operator module functions. 476 477 elif isinstance(n, compiler.ast.Operator): 478 return self.process_operator_node(n) 479 480 elif isinstance(n, compiler.ast.AugAssign): 481 self.process_augassign_node(n) 482 483 elif isinstance(n, compiler.ast.Compare): 484 return self.process_compare_node(n) 485 486 elif isinstance(n, compiler.ast.Slice): 487 return self.process_slice_node(n) 488 489 elif isinstance(n, compiler.ast.Sliceobj): 490 return self.process_sliceobj_node(n) 491 492 elif isinstance(n, compiler.ast.Subscript): 493 return self.process_subscript_node(n) 494 495 # Namespaces within modules. 496 497 elif isinstance(n, compiler.ast.Class): 498 self.process_class_node(n) 499 500 elif isinstance(n, compiler.ast.Function): 501 self.process_function_node(n, n.name) 502 503 elif isinstance(n, compiler.ast.Lambda): 504 return self.process_lambda_node(n) 505 506 # Assignments. 507 508 elif isinstance(n, compiler.ast.Assign): 509 510 # Handle each assignment node. 511 512 for node in n.nodes: 513 self.process_assignment_node(node, n.expr) 514 515 # Assignments within non-Assign nodes. 516 517 elif isinstance(n, compiler.ast.AssName): 518 self.process_assignment_node(n, None) 519 520 elif isinstance(n, compiler.ast.AssAttr): 521 self.process_attribute_access(n) 522 523 # Accesses. 524 525 elif isinstance(n, compiler.ast.Getattr): 526 return self.process_attribute_access(n) 527 528 # Name recording for later testing. 529 530 elif isinstance(n, compiler.ast.Name): 531 return self.process_name_node(n) 532 533 # Conditional statement tracking. 534 535 elif isinstance(n, compiler.ast.For): 536 self.process_for_node(n) 537 538 elif isinstance(n, compiler.ast.While): 539 self.process_while_node(n) 540 541 elif isinstance(n, compiler.ast.If): 542 self.process_if_node(n) 543 544 elif isinstance(n, (compiler.ast.And, compiler.ast.Or)): 545 return self.process_logical_node(n) 546 547 # Exception control-flow tracking. 548 549 elif isinstance(n, compiler.ast.TryExcept): 550 self.process_try_node(n) 551 552 elif isinstance(n, compiler.ast.TryFinally): 553 self.process_try_finally_node(n) 554 555 # Control-flow modification statements. 556 557 elif isinstance(n, compiler.ast.Break): 558 self.trackers[-1].suspend_broken_branch() 559 560 elif isinstance(n, compiler.ast.Continue): 561 self.trackers[-1].suspend_continuing_branch() 562 563 elif isinstance(n, compiler.ast.Raise): 564 self.process_structure(n) 565 self.trackers[-1].abandon_branch() 566 567 elif isinstance(n, compiler.ast.Return): 568 self.process_structure(n) 569 self.trackers[-1].abandon_returning_branch() 570 571 # Invocations. 572 573 elif isinstance(n, compiler.ast.CallFunc): 574 return self.process_invocation_node(n) 575 576 # Constant usage. 577 578 elif isinstance(n, compiler.ast.Const): 579 return self.get_literal_instance(n, n.value.__class__.__name__) 580 581 elif isinstance(n, compiler.ast.Dict): 582 return self.get_literal_instance(n, "dict") 583 584 elif isinstance(n, compiler.ast.List): 585 return self.get_literal_instance(n, "list") 586 587 elif isinstance(n, compiler.ast.Tuple): 588 return self.get_literal_instance(n, "tuple") 589 590 # List comprehensions and if expressions. 591 592 elif isinstance(n, compiler.ast.ListComp): 593 self.process_listcomp_node(n) 594 595 elif isinstance(n, compiler.ast.IfExp): 596 self.process_ifexp_node(n) 597 598 # All other nodes are processed depth-first. 599 600 else: 601 self.process_structure(n) 602 603 # By default, no expression details are returned. 604 605 return None 606 607 # Specific node handling. 608 609 def process_assignment_node(self, n, expr): 610 611 "Process the individual node 'n' to be assigned the contents of 'expr'." 612 613 # Names and attributes are assigned the entire expression. 614 615 if isinstance(n, compiler.ast.AssName): 616 617 name_ref = expr and self.process_structure_node(expr) 618 619 # Name assignments populate either function namespaces or the 620 # general namespace hierarchy. 621 622 self.assign_general_local(n.name, name_ref) 623 624 # Record usage of the name. 625 626 self.record_name(n.name) 627 628 elif isinstance(n, compiler.ast.AssAttr): 629 if expr: self.process_structure_node(expr) 630 self.process_attribute_access(n) 631 632 # Lists and tuples are matched against the expression and their 633 # items assigned to expression items. 634 635 elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)): 636 self.process_assignment_node_items(n, expr) 637 638 # Slices and subscripts are permitted within assignment nodes. 639 640 elif isinstance(n, compiler.ast.Slice): 641 self.process_slice_node(n, expr) 642 643 elif isinstance(n, compiler.ast.Subscript): 644 self.process_subscript_node(n, expr) 645 646 def process_attribute_access(self, n): 647 648 "Process the given attribute access node 'n'." 649 650 # Obtain any completed chain and return the reference to it. 651 652 name_ref = self.process_attribute_chain(n) 653 if self.have_access_expression(n): 654 return name_ref 655 656 # Where the start of the chain of attributes has been reached, determine 657 # the complete access. 658 659 # Given a non-access node, this chain can be handled in its entirety, 660 # either being name-based and thus an access rooted on a name, or being 661 # based on some other node and thus an anonymous access of some kind. 662 663 path = self.get_namespace_path() 664 665 # Start with the the full attribute chain. 666 667 remaining = self.attrs 668 attrnames = ".".join(remaining) 669 670 # If the accessor cannot be identified, or where attributes 671 # remain in an attribute chain, record the anonymous accesses. 672 673 if not isinstance(name_ref, NameRef): # includes ResolvedNameRef 674 675 assignment = isinstance(n, compiler.ast.AssAttr) 676 677 init_item(self.attr_accesses, path, set) 678 self.attr_accesses[path].add(attrnames) 679 680 self.record_access_details(None, attrnames, assignment) 681 del self.attrs[0] 682 return 683 684 # Name-based accesses will handle the first attribute in a 685 # chain. 686 687 else: 688 attrname = remaining[0] 689 690 # Attribute assignments are used to identify instance attributes. 691 692 if isinstance(n, compiler.ast.AssAttr) and \ 693 self.in_class and self.in_function and n.expr.name == "self": 694 695 self.set_instance_attr(attrname) 696 697 # Record attribute usage using any name local to this namespace, 698 # if assigned in the namespace, or using an external name 699 # (presently just globals within classes). 700 701 name = self.get_name_for_tracking(name_ref.name, name_ref.final()) 702 tracker = self.trackers[-1] 703 704 immediate_access = len(self.attrs) == 1 705 assignment = immediate_access and isinstance(n, compiler.ast.AssAttr) 706 707 del self.attrs[0] 708 709 # Record global-based chains for subsequent resolution. 710 711 is_global = self.in_function and not self.function_locals[path].has_key(name) or \ 712 not self.in_function 713 714 if is_global: 715 self.record_global_access_details(name, attrnames) 716 717 # Make sure the name is being tracked: global names will not 718 # already be initialised in a branch and must be added 719 # explicitly. 720 721 if not tracker.have_name(name): 722 tracker.assign_names([name]) 723 if self.in_function: 724 self.scope_globals[path].add(name) 725 726 # Record attribute usage in the tracker, and record the branch 727 # information for the access. 728 729 branches = tracker.use_attribute(name, attrname) 730 731 if not branches: 732 print >>sys.stderr, "In %s, name %s is accessed using %s before an assignment." % ( 733 path, name, attrname) 734 branches = tracker.use_attribute(name, attrname) 735 736 self.record_branches_for_access(branches, name, attrnames) 737 access_number = self.record_access_details(name, attrnames, assignment) 738 return AccessRef(name, attrnames, access_number) 739 740 def process_class_node(self, n): 741 742 "Process the given class node 'n'." 743 744 path = self.get_namespace_path() 745 746 # To avoid notions of class "versions" where the same definition 747 # might be parameterised with different state and be referenced 748 # elsewhere (as base classes, for example), classes in functions or 749 # conditions are forbidden. 750 751 if self.in_function or self.in_conditional: 752 print >>sys.stderr, "In %s, class %s in function or conditional statement ignored." % ( 753 path, n.name) 754 return 755 756 # Resolve base classes. 757 758 bases = [] 759 760 for base in n.bases: 761 base_class = self.get_class(base) 762 763 if not base_class: 764 print >>sys.stderr, "In %s, class %s has unidentifiable base classes." % ( 765 path, n.name) 766 return 767 else: 768 bases.append(base_class) 769 770 # Record bases for the class and retain the class name. 771 772 class_name = self.get_object_path(n.name) 773 774 if not bases and class_name != "__builtins__.core.object": 775 ref = self.get_object("__builtins__.object") 776 bases.append(ref) 777 778 self.importer.classes[class_name] = self.classes[class_name] = bases 779 self.importer.subclasses[class_name] = set() 780 self.scope_globals[class_name] = set() 781 782 # Set the definition before entering the namespace rather than 783 # afterwards because methods may reference it. In normal Python, 784 # a class is not accessible until the definition is complete, but 785 # methods can generally reference it since upon being called the 786 # class will already exist. 787 788 self.set_definition(n.name, "<class>") 789 790 in_class = self.in_class 791 self.in_class = class_name 792 self.set_instance_attr("__class__", Reference("<class>", class_name)) 793 self.enter_namespace(n.name) 794 self.set_name("__fn__") # special instantiator attribute 795 self.set_name("__args__") # special instantiator attribute 796 self.assign_general_local("__name__", self.get_constant("str", class_name)) 797 self.process_structure_node(n.code) 798 self.exit_namespace() 799 self.in_class = in_class 800 801 def process_from_node(self, n): 802 803 "Process the given node 'n', importing from another module." 804 805 path = self.get_namespace_path() 806 807 modname, names = self.get_module_name(n) 808 809 # Load the mentioned module. 810 811 top, module = self.get_module(modname, True) 812 self.set_module(None, module, hidden=True) 813 814 if not module: 815 print >>sys.stderr, "In %s, from statement importing from %s failed." % ( 816 path, modname) 817 818 # Attempt to obtain the referenced objects. 819 820 for name, alias in n.names: 821 822 # NOTE: Package submodules are not implicitly imported. 823 824 if name == "*": 825 if module: 826 827 # Warn about a circular import that probably doesn't find 828 # the names. 829 830 if not module.loaded: 831 print >>sys.stderr, "In %s, from statement performs circular import %s of %s." % ( 832 path, modname) 833 834 for name, value in module.get_globals().items(): 835 if name != "__name__": 836 value = ResolvedNameRef(name, value) 837 self.set_general_local(name, value) 838 self.set_imported_name(name, modname) 839 break 840 841 # Explicit names. 842 843 ref = self.import_name_from_module(name, modname, module, alias) 844 value = ResolvedNameRef(alias or name, ref) 845 self.set_general_local(alias or name, value) 846 self.set_imported_name(name, modname, alias) 847 848 def import_name_from_module(self, name, modname, module, alias=None): 849 850 """ 851 Import 'name' from the module having the given 'modname', with 'module' 852 having been obtained for the module name, using 'alias' for the imported 853 name in the current namespace. 854 """ 855 856 path = self.get_namespace_path() 857 858 if module and module.get_global(name): 859 value = module.get_global(name) 860 861 # Warn about an import that fails to provide a name, perhaps due 862 # to a circular import. 863 864 if not value: 865 print >>sys.stderr, "In %s, from statement cannot import %s from %s%s." % ( 866 path, name, modname, not module.loaded and "(circular import)") 867 868 return value 869 870 # Record the name as a dependency. 871 872 else: 873 return Reference("<depends>", "%s.%s" % (modname, name)) 874 875 def process_function_node(self, n, name): 876 877 """ 878 Process the given function or lambda node 'n' with the given 'name'. 879 """ 880 881 is_lambda = isinstance(n, compiler.ast.Lambda) 882 883 # Where a function is declared conditionally, use a separate name for 884 # the definition, and assign the definition to the stated name. 885 886 if (self.in_conditional or self.in_function) and not is_lambda: 887 original_name = name 888 name = self.get_lambda_name() 889 else: 890 original_name = None 891 892 # Initialise argument and local records. 893 894 function_name = self.get_object_path(name) 895 896 argnames = self.importer.function_parameters[function_name] = \ 897 self.function_parameters[function_name] = get_argnames(n.argnames) 898 locals = self.function_locals[function_name] = {} 899 900 for argname in argnames: 901 locals[argname] = Reference("<var>") 902 903 globals = self.scope_globals[function_name] = set() 904 905 # Process the defaults. 906 907 defaults = self.importer.function_defaults[function_name] = \ 908 self.function_defaults[function_name] = [] 909 910 for argname, default in compiler.ast.get_defaults(n): 911 if default: 912 913 # Obtain any reference for the default. 914 915 name_ref = self.process_structure_node(default) 916 defaults.append((argname, name_ref.is_name() and name_ref.reference() or Reference("<var>"))) 917 918 # Reset conditional tracking to focus on the function contents. 919 920 parent_function = self.parent_function 921 self.parent_function = self.in_function and self.get_namespace_path() or None 922 923 in_conditional = self.in_conditional 924 self.in_conditional = False 925 926 in_function = self.in_function 927 self.in_function = function_name 928 929 self.enter_namespace(name) 930 931 # Track attribute usage within the namespace. 932 933 path = self.get_namespace_path() 934 init_item(self.propagated_names, path, set) 935 936 self.start_tracking(locals) 937 self.process_structure_node(n.code) 938 self.stop_tracking() 939 940 # Propagate names from parent scopes. 941 942 for local in self.propagated_names[path]: 943 if not local in argnames and self.trackers[-1].have_name(local): 944 argnames.append(local) 945 defaults.append((local, Reference("<var>"))) 946 self.set_function_local(local) 947 948 # Exit to the parent and note propagated names. 949 950 self.exit_namespace() 951 952 parent = self.get_namespace_path() 953 if self.propagated_names.has_key(parent): 954 for local in self.propagated_names[path]: 955 self.propagated_names[parent].add(local) 956 957 # Update flags. 958 959 self.in_function = in_function 960 self.in_conditional = in_conditional 961 self.parent_function = parent_function 962 963 # Define the function using the appropriate name. 964 965 self.set_definition(name, "<function>") 966 967 # Where a function is set conditionally, assign the name. 968 969 if original_name: 970 self.process_assignment_for_function(original_name, name) 971 972 def process_global_node(self, n): 973 974 """ 975 Process the given "global" node 'n'. 976 """ 977 978 path = self.get_namespace_path() 979 980 if path != self.name: 981 self.scope_globals[path].update(n.names) 982 983 def process_if_node(self, n): 984 985 """ 986 Process the given "if" node 'n'. 987 """ 988 989 tracker = self.trackers[-1] 990 tracker.new_branchpoint() 991 992 for test, body in n.tests: 993 self.process_structure_node(test) 994 995 tracker.new_branch() 996 997 in_conditional = self.in_conditional 998 self.in_conditional = True 999 self.process_structure_node(body) 1000 self.in_conditional = in_conditional 1001 1002 tracker.shelve_branch() 1003 1004 # Maintain a branch for the else clause. 1005 1006 tracker.new_branch() 1007 if n.else_: 1008 self.process_structure_node(n.else_) 1009 tracker.shelve_branch() 1010 1011 tracker.merge_branches() 1012 1013 def process_ifexp_node(self, n): 1014 1015 "Process the given if expression node 'n'." 1016 1017 name_ref = self.process_structure_node(self.convert_ifexp_node(n)) 1018 1019 path = self.get_namespace_path() 1020 self.allocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 1021 self.deallocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 1022 1023 return InvocationRef(name_ref) 1024 1025 def process_import_node(self, n): 1026 1027 "Process the given import node 'n'." 1028 1029 path = self.get_namespace_path() 1030 1031 # Load the mentioned module. 1032 1033 for name, alias in n.names: 1034 module, leaf_module = self.get_module(name, alias) 1035 1036 if not module: 1037 print >>sys.stderr, "In %s, import statement importing from %s failed." % ( 1038 path, name) 1039 if module and not module.loaded: 1040 print >>sys.stderr, "In %s, import statement performs circular import of %s." % ( 1041 path, name) 1042 1043 self.set_module(alias or name.split(".")[0], module, leaf_module) 1044 1045 def process_invocation_node(self, n): 1046 1047 "Process the given invocation node 'n'." 1048 1049 path = self.get_namespace_path() 1050 1051 self.allocate_arguments(path, n.args) 1052 1053 try: 1054 # Process the expression, obtaining any identified reference. 1055 1056 name_ref = self.process_structure_node(n.node) 1057 1058 # Process the arguments. 1059 1060 for arg in n.args: 1061 self.process_structure_node(arg) 1062 1063 # Detect class invocations. 1064 1065 if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"): 1066 return InstanceRef(name_ref.reference().instance_of()) 1067 1068 elif isinstance(name_ref, NameRef): 1069 return InvocationRef(name_ref) 1070 1071 return None 1072 1073 finally: 1074 self.deallocate_arguments(path, n.args) 1075 1076 def process_lambda_node(self, n): 1077 1078 "Process the given lambda node 'n'." 1079 1080 name = self.get_lambda_name() 1081 self.process_function_node(n, name) 1082 1083 origin = self.get_object_path(name) 1084 return ResolvedNameRef(name, Reference("<function>", origin)) 1085 1086 def process_listcomp_node(self, n): 1087 1088 "Process the given list comprehension node 'n'." 1089 1090 name_ref = self.process_structure_node(self.convert_listcomp_node(n)) 1091 1092 path = self.get_namespace_path() 1093 self.allocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 1094 self.deallocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 1095 1096 return InvocationRef(name_ref) 1097 1098 def process_logical_node(self, n): 1099 1100 "Process the given operator node 'n'." 1101 1102 self.process_operator_chain(n.nodes, self.process_structure_node) 1103 1104 def process_name_node(self, n): 1105 1106 "Process the given name node 'n'." 1107 1108 path = self.get_namespace_path() 1109 1110 # Special names. 1111 1112 if n.name.startswith("$"): 1113 value = self.get_special(n.name) 1114 if value: 1115 return value 1116 1117 # Special case for operator functions introduced through code 1118 # transformations. 1119 1120 if n.name.startswith("$op"): 1121 1122 # Obtain the location of the actual function defined in the operator 1123 # package. 1124 1125 op = n.name[len("$op"):] 1126 1127 # Access the operator module. 1128 1129 top, module = self.get_module("operator", True) 1130 self.set_module(None, module, hidden=True) 1131 1132 # Link the operation to the operator module definition in this 1133 # module. 1134 1135 self.set_imported_name(op, "operator", n.name, self.name) 1136 1137 # Attempt to get a reference. 1138 1139 ref = self.import_name_from_module(op, "operator", module) 1140 ref = self.get_object("operator.%s" % op) or ref 1141 1142 # Record the imported name and provide the resolved name reference. 1143 1144 value = ResolvedNameRef(n.name, ref) 1145 self.set_special(n.name, value) 1146 return value 1147 1148 # Record usage of the name. 1149 1150 self.record_name(n.name) 1151 1152 # Search for unknown names in non-function scopes immediately. 1153 # External names in functions are resolved later. 1154 1155 ref = self.find_name(n.name) 1156 if ref: 1157 return ResolvedNameRef(n.name, ref) 1158 1159 # Global name. 1160 1161 elif self.in_function and n.name in self.scope_globals[path]: 1162 return NameRef(n.name) 1163 1164 # Examine other names. 1165 1166 else: 1167 tracker = self.trackers[-1] 1168 1169 # Check local names. 1170 1171 branches = tracker.tracking_name(n.name) 1172 1173 # Find names inherited from a parent scope. 1174 1175 if not branches and self.parent_function: 1176 branches = tracker.have_name(n.name) 1177 if branches: 1178 self.propagate_name(n.name) 1179 1180 # Local or inherited name. 1181 1182 if branches: 1183 self.record_branches_for_access(branches, n.name, None) 1184 access_number = self.record_access_details(n.name, None, False) 1185 return LocalNameRef(n.name, access_number) 1186 1187 # Possible global name. 1188 1189 else: 1190 return NameRef(n.name) 1191 1192 def process_operator_chain(self, nodes, fn): 1193 1194 """ 1195 Process the given chain of 'nodes', applying 'fn' to each node or item. 1196 Each node starts a new conditional region, effectively making a deeply- 1197 nested collection of if-like statements. 1198 """ 1199 1200 tracker = self.trackers[-1] 1201 1202 for item in nodes: 1203 tracker.new_branchpoint() 1204 tracker.new_branch() 1205 fn(item) 1206 1207 for item in nodes[:-1]: 1208 tracker.shelve_branch() 1209 tracker.new_branch() 1210 tracker.shelve_branch() 1211 tracker.merge_branches() 1212 1213 tracker.shelve_branch() 1214 tracker.merge_branches() 1215 1216 def process_try_node(self, n): 1217 1218 """ 1219 Process the given "try...except" node 'n'. 1220 """ 1221 1222 tracker = self.trackers[-1] 1223 tracker.new_branchpoint() 1224 1225 self.process_structure_node(n.body) 1226 1227 for name, var, handler in n.handlers: 1228 if name is not None: 1229 self.process_structure_node(name) 1230 1231 # Any abandoned branches from the body can now be resumed in a new 1232 # branch. 1233 1234 tracker.resume_abandoned_branches() 1235 1236 # Establish the local for the handler. 1237 1238 if var is not None: 1239 self.process_structure_node(var) 1240 if handler is not None: 1241 self.process_structure_node(handler) 1242 1243 tracker.shelve_branch() 1244 1245 # The else clause maintains the usage from the body but without the 1246 # abandoned branches since they would never lead to the else clause 1247 # being executed. 1248 1249 if n.else_: 1250 tracker.new_branch() 1251 self.process_structure_node(n.else_) 1252 tracker.shelve_branch() 1253 1254 # Without an else clause, a null branch propagates the successful 1255 # outcome. 1256 1257 else: 1258 tracker.new_branch() 1259 tracker.shelve_branch() 1260 1261 tracker.merge_branches() 1262 1263 def process_try_finally_node(self, n): 1264 1265 """ 1266 Process the given "try...finally" node 'n'. 1267 """ 1268 1269 tracker = self.trackers[-1] 1270 self.process_structure_node(n.body) 1271 1272 # Any abandoned branches from the body can now be resumed. 1273 1274 branches = tracker.resume_all_abandoned_branches() 1275 self.process_structure_node(n.final) 1276 1277 # At the end of the finally clause, abandoned branches are discarded. 1278 1279 tracker.restore_active_branches(branches) 1280 1281 def process_while_node(self, n): 1282 1283 "Process the given while node 'n'." 1284 1285 tracker = self.trackers[-1] 1286 tracker.new_branchpoint(loop_node=True) 1287 1288 # Evaluate any test or iterator outside the loop. 1289 1290 self.process_structure_node(n.test) 1291 1292 # Propagate attribute usage to branches. 1293 1294 tracker.new_branch(loop_node=True) 1295 1296 # Enter the loop. 1297 1298 in_conditional = self.in_conditional 1299 self.in_conditional = True 1300 self.process_structure_node(n.body) 1301 self.in_conditional = in_conditional 1302 1303 # Continuing branches are resumed before any test. 1304 1305 tracker.resume_continuing_branches() 1306 1307 # Evaluate any continuation test within the body. 1308 1309 self.process_structure_node(n.test) 1310 1311 tracker.shelve_branch(loop_node=True) 1312 1313 # Support the non-looping condition. 1314 1315 tracker.new_branch() 1316 tracker.shelve_branch() 1317 1318 tracker.merge_branches() 1319 1320 # Evaluate any else clause outside branches. 1321 1322 if n.else_: 1323 self.process_structure_node(n.else_) 1324 1325 # Connect broken branches to the code after any loop. 1326 1327 tracker.resume_broken_branches() 1328 1329 # Branch tracking methods. 1330 1331 def start_tracking(self, names): 1332 1333 """ 1334 Start tracking attribute usage for names in the current namespace, 1335 immediately registering the given 'names'. 1336 """ 1337 1338 path = self.get_namespace_path() 1339 parent = self.trackers[-1] 1340 tracker = BranchTracker() 1341 self.trackers.append(tracker) 1342 1343 # For functions created from expressions or for functions within 1344 # functions, propagate usage to the new namespace. 1345 1346 if self.parent_function: 1347 tracker.inherit_branches(parent, names) 1348 1349 # Record the given names established as new branches. 1350 1351 tracker.assign_names(names) 1352 1353 def assign_name(self, name, name_ref): 1354 1355 "Assign to 'name' the given 'name_ref' in the current namespace." 1356 1357 name = self.get_name_for_tracking(name) 1358 self.trackers[-1].assign_names([name], [name_ref]) 1359 1360 def stop_tracking(self): 1361 1362 """ 1363 Stop tracking attribute usage, recording computed usage for the current 1364 namespace. 1365 """ 1366 1367 path = self.get_namespace_path() 1368 tracker = self.trackers.pop() 1369 self.record_assignments_for_access(tracker) 1370 1371 self.attr_usage[path] = tracker.get_all_usage() 1372 self.name_initialisers[path] = tracker.get_all_values() 1373 1374 def start_tracking_in_module(self): 1375 1376 "Start tracking attribute usage in the module." 1377 1378 tracker = BranchTracker() 1379 self.trackers.append(tracker) 1380 1381 def stop_tracking_in_module(self): 1382 1383 "Stop tracking attribute usage in the module." 1384 1385 tracker = self.trackers[0] 1386 self.record_assignments_for_access(tracker) 1387 self.attr_usage[self.name] = tracker.get_all_usage() 1388 self.name_initialisers[self.name] = tracker.get_all_values() 1389 1390 def propagate_name(self, name): 1391 1392 "Propagate the given 'name' into the current namespace." 1393 1394 path = self.get_namespace_path() 1395 self.propagated_names[path].add(name) 1396 1397 def record_assignments_for_access(self, tracker): 1398 1399 """ 1400 For the current path, use the given 'tracker' to record assignment 1401 version information for attribute accesses. 1402 """ 1403 1404 path = self.get_path_for_access() 1405 1406 if not self.attr_accessor_branches.has_key(path): 1407 return 1408 1409 init_item(self.attr_accessors, path, dict) 1410 attr_accessors = self.attr_accessors[path] 1411 1412 # Obtain the branches applying during each access. 1413 1414 for access, all_branches in self.attr_accessor_branches[path].items(): 1415 name, attrnames = access 1416 init_item(attr_accessors, access, list) 1417 1418 # Obtain the assignments applying to each branch. 1419 1420 for branches in all_branches: 1421 positions = tracker.get_assignment_positions_for_branches(name, branches) 1422 1423 # Detect missing name information. 1424 1425 if None in positions: 1426 globals = self.global_attr_accesses.get(path) 1427 accesses = globals and globals.get(name) 1428 if not accesses: 1429 print >>sys.stderr, "In %s, %s may not be defined when used." % ( 1430 self.get_namespace_path(), name) 1431 positions.remove(None) 1432 1433 attr_accessors[access].append(positions) 1434 1435 def record_branches_for_access(self, branches, name, attrnames): 1436 1437 """ 1438 Record the given 'branches' for an access involving the given 'name' and 1439 'attrnames'. 1440 """ 1441 1442 access = name, attrnames 1443 path = self.get_path_for_access() 1444 1445 init_item(self.attr_accessor_branches, path, dict) 1446 attr_accessor_branches = self.attr_accessor_branches[path] 1447 1448 init_item(attr_accessor_branches, access, list) 1449 attr_accessor_branches[access].append(branches) 1450 1451 def record_access_details(self, name, attrnames, assignment): 1452 1453 """ 1454 For the given 'name' and 'attrnames', record an access indicating 1455 whether 'assignment' is occurring. 1456 1457 These details correspond to accesses otherwise recorded by the attribute 1458 accessor and attribute access dictionaries. 1459 """ 1460 1461 access = name, attrnames 1462 path = self.get_path_for_access() 1463 1464 init_item(self.attr_access_modifiers, path, dict) 1465 init_item(self.attr_access_modifiers[path], access, list) 1466 1467 access_number = len(self.attr_access_modifiers[path][access]) 1468 self.attr_access_modifiers[path][access].append(assignment) 1469 return access_number 1470 1471 def record_global_access_details(self, name, attrnames): 1472 1473 """ 1474 Record details of a global access via the given 'name' involving the 1475 indicated 'attrnames'. 1476 """ 1477 1478 path = self.get_namespace_path() 1479 1480 init_item(self.global_attr_accesses, path, dict) 1481 init_item(self.global_attr_accesses[path], name, set) 1482 self.global_attr_accesses[path][name].add(attrnames) 1483 1484 # Namespace modification. 1485 1486 def record_name(self, name): 1487 1488 "Record the use of 'name' in a namespace." 1489 1490 path = self.get_namespace_path() 1491 init_item(self.names_used, path, set) 1492 self.names_used[path].add(name) 1493 1494 def set_module(self, name, module, leaf_module=None, hidden=False): 1495 1496 """ 1497 Set a module in the current namespace using the given 'name' and 1498 corresponding 'module' object, with the 'leaf_module' being recorded 1499 if different. If 'hidden' is a true value, the modules are recorded as 1500 not necessarily being exposed by this module. This module is, however, 1501 recorded as accessing the given modules and is thus dependent on them. 1502 """ 1503 1504 if name: 1505 self.set_general_local(name, module and Reference("<module>", module.name) or None) 1506 if module: 1507 if hidden: 1508 self.imported_hidden.add(module) 1509 if leaf_module and leaf_module is not module: 1510 self.imported_hidden.add(leaf_module) 1511 else: 1512 self.imported.add(module) 1513 module.accessing_modules.add(self.name) 1514 if leaf_module and leaf_module is not module: 1515 self.imported.add(leaf_module) 1516 leaf_module.accessing_modules.add(self.name) 1517 1518 def set_definition(self, name, kind): 1519 1520 """ 1521 Set the definition having the given 'name' and 'kind'. 1522 1523 Definitions are set in the static namespace hierarchy, but they can also 1524 be recorded for function locals. 1525 """ 1526 1527 if self.is_global(name): 1528 print >>sys.stderr, "In %s, %s is defined as being global." % ( 1529 self.get_namespace_path(), name) 1530 1531 path = self.get_object_path(name) 1532 self.set_object(path, kind) 1533 1534 ref = self.get_object(path) 1535 if ref.get_kind() == "<var>": 1536 print >>sys.stderr, "In %s, %s is defined more than once." % ( 1537 self.get_namespace_path(), name) 1538 1539 if not self.is_global(name) and self.in_function: 1540 self.set_function_local(name, ref) 1541 1542 def set_function_local(self, name, ref=None): 1543 1544 "Set the local with the given 'name' and optional 'ref'." 1545 1546 locals = self.function_locals[self.get_namespace_path()] 1547 multiple = not ref or locals.has_key(name) and locals[name] != ref 1548 locals[name] = multiple and Reference("<var>") or ref 1549 1550 def assign_general_local(self, name, name_ref): 1551 1552 """ 1553 Set for 'name' the given 'name_ref', recording the name for attribute 1554 usage tracking. 1555 """ 1556 1557 self.set_general_local(name, name_ref) 1558 self.assign_name(name, name_ref) 1559 1560 def set_general_local(self, name, value=None): 1561 1562 """ 1563 Set the 'name' with optional 'value' in any kind of local namespace, 1564 where the 'value' should be a reference if specified. 1565 """ 1566 1567 init_value = self.get_initialising_value(value) 1568 1569 # Module global names. 1570 1571 if self.is_global(name): 1572 path = self.get_global_path(name) 1573 self.set_object(path, init_value) 1574 1575 # Function local names. 1576 1577 elif self.in_function: 1578 path = self.get_object_path(name) 1579 self.set_function_local(name, init_value) 1580 1581 # Other namespaces (classes). 1582 1583 else: 1584 path = self.get_object_path(name) 1585 self.set_name(name, init_value) 1586 1587 def set_name(self, name, ref=None): 1588 1589 "Attach the 'name' with optional 'ref' to the current namespace." 1590 1591 self.set_object(self.get_object_path(name), ref) 1592 1593 def set_instance_attr(self, name, ref=None): 1594 1595 """ 1596 Add an instance attribute of the given 'name' to the current class, 1597 using the optional 'ref'. 1598 """ 1599 1600 init_item(self.instance_attrs, self.in_class, set) 1601 self.instance_attrs[self.in_class].add(name) 1602 1603 if ref: 1604 init_item(self.instance_attr_constants, self.in_class, dict) 1605 self.instance_attr_constants[self.in_class][name] = ref 1606 1607 def get_initialising_value(self, value): 1608 1609 "Return a suitable initialiser reference for 'value'." 1610 1611 if isinstance(value, (NameRef, AccessRef, InstanceRef)): # includes LiteralSequenceRef, ResolvedNameRef 1612 return value.reference() 1613 1614 # In general, invocations do not produce known results. However, the 1615 # name initialisers are resolved once a module has been inspected. 1616 1617 elif isinstance(value, InvocationRef): 1618 return None 1619 1620 else: 1621 return value 1622 1623 # Static, program-relative naming. 1624 1625 def find_name(self, name): 1626 1627 """ 1628 Return the qualified name for the given 'name' used in the current 1629 non-function namespace. 1630 """ 1631 1632 path = self.get_namespace_path() 1633 ref = None 1634 1635 if not self.in_function and name not in predefined_constants: 1636 if self.in_class: 1637 ref = self.get_object(self.get_object_path(name)) 1638 if not ref: 1639 ref = self.get_global_or_builtin(name) 1640 1641 return ref 1642 1643 def get_class(self, node): 1644 1645 """ 1646 Use the given 'node' to obtain the identity of a class. Return a 1647 reference for the class. Unresolved dependencies are permitted and must 1648 be resolved later. 1649 """ 1650 1651 ref = self._get_class(node) 1652 return ref.has_kind(["<class>", "<depends>"]) and ref or None 1653 1654 def _get_class(self, node): 1655 1656 """ 1657 Use the given 'node' to find a class definition. Return a reference to 1658 the class. 1659 """ 1660 1661 if isinstance(node, compiler.ast.Getattr): 1662 1663 # Obtain the identity of the access target. 1664 1665 ref = self._get_class(node.expr) 1666 1667 # Where the target is a class or module, obtain the identity of the 1668 # attribute. 1669 1670 if ref.has_kind(["<function>", "<var>"]): 1671 return None 1672 else: 1673 attrname = "%s.%s" % (ref.get_origin(), node.attrname) 1674 return self.get_object(attrname) 1675 1676 # Names can be module-level or built-in. 1677 1678 elif isinstance(node, compiler.ast.Name): 1679 1680 # Record usage of the name and attempt to identify it. 1681 1682 self.record_name(node.name) 1683 return self.get_global_or_builtin(node.name) 1684 else: 1685 return None 1686 1687 def get_constant(self, name, value): 1688 1689 "Return a constant reference for the given type 'name' and 'value'." 1690 1691 ref = self.get_literal_builtin(name) 1692 return self.get_constant_reference(ref, value) 1693 1694 def get_literal_instance(self, n, name): 1695 1696 "For node 'n', return a reference to an instance of 'name'." 1697 1698 # Get a class reference. 1699 1700 ref = self.get_literal_builtin(name) 1701 1702 # Obtain the details of the literal itself. 1703 # An alias to the type is generated for sequences. 1704 1705 if name in ("dict", "list", "tuple"): 1706 self.set_special_literal(name, ref) 1707 return self.process_literal_sequence_node(n, name, ref, LiteralSequenceRef) 1708 1709 # Constant values are independently recorded. 1710 1711 else: 1712 return self.get_constant_reference(ref, n.value) 1713 1714 def get_literal_builtin(self, name): 1715 1716 "Return a reference for a built-in literal type of the given 'name'." 1717 1718 ref = self.get_builtin(name) 1719 true_origin = "__builtins__.%s.%s" % (name, name) 1720 exposed_origin = "__builtins__.%s" % name 1721 1722 # Obtain fully-imported built-in class references. 1723 1724 if ref and ref.has_kind("<class>"): 1725 pass 1726 1727 # Early-stage references need explicit references. 1728 1729 elif ref: 1730 ref = Reference("<class>", true_origin) 1731 1732 # Otherwise, the normal locations can be used. 1733 1734 else: 1735 ref = Reference("<class>", true_origin, exposed_origin) 1736 1737 return ref 1738 1739 # Functions and invocations. 1740 1741 def allocate_arguments(self, path, args): 1742 1743 """ 1744 Allocate temporary argument storage using current and maximum 1745 requirements for the given 'path' and 'args'. 1746 """ 1747 1748 init_item(self.function_targets, path, lambda: [0, 0]) 1749 t = self.function_targets[path] 1750 t[0] += 1 1751 t[1] = max(t[0], t[1]) 1752 1753 init_item(self.function_arguments, path, lambda: [0, 0]) 1754 t = self.function_arguments[path] 1755 t[0] += len(args) + 1 1756 t[1] = max(t[0], t[1]) 1757 1758 def deallocate_arguments(self, path, args): 1759 1760 "Deallocate temporary argument storage for the given 'path' and 'args'." 1761 1762 self.function_targets[path][0] -= 1 1763 self.function_arguments[path][0] -= len(args) + 1 1764 1765 # vim: tabstop=4 expandtab shiftwidth=4