Lichen

inspector.py

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