Lichen

inspector.py

16:7a0fcff254e8
2016-09-04 Paul Boddie Track required modules, either explicitly imported or providing dependencies, as opposed to those providing objects defined elsewhere.
     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