Lichen

deducer.py

511:b99c11afb6f5
2017-01-27 Paul Boddie Configure the Makefile using generated secondary Makefiles, replacing the debug-specific Makefile and permitting the selection of included source files.
     1 #!/usr/bin/env python     2      3 """     4 Deduce types for usage observations.     5      6 Copyright (C) 2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 from common import first, get_assigned_attributes, \    23                    get_attrname_from_location, get_attrnames, \    24                    get_invoked_attributes, get_name_path, init_item, \    25                    sorted_output, CommonOutput    26 from encoders import encode_access_location, \    27                      encode_constrained, encode_location, encode_usage, \    28                      get_kinds, test_label_for_kind, test_label_for_type    29 from errors import DeduceError    30 from os.path import join    31 from referencing import combine_types, is_single_class_type, separate_types, \    32                         Reference    33     34 class Deducer(CommonOutput):    35     36     "Deduce types in a program."    37     38     def __init__(self, importer, output):    39     40         """    41         Initialise an instance using the given 'importer' that will perform    42         deductions on the program information, writing the results to the given    43         'output' directory.    44         """    45     46         self.importer = importer    47         self.output = output    48     49         # Descendants of classes.    50     51         self.descendants = {}    52         self.init_descendants()    53         self.init_special_attributes()    54     55         # Map locations to usage in order to determine specific types.    56     57         self.location_index = {}    58     59         # Map access locations to definition locations.    60     61         self.access_index = {}    62     63         # Map aliases to accesses that define them.    64     65         self.alias_index = {}    66     67         # Map constant accesses to redefined accesses.    68     69         self.const_accesses = {}    70         self.const_accesses_rev = {}    71     72         # Map usage observations to assigned attributes.    73     74         self.assigned_attrs = {}    75     76         # Map usage observations to objects.    77     78         self.attr_class_types = {}    79         self.attr_instance_types = {}    80         self.attr_module_types = {}    81     82         # All known attribute names.    83     84         self.all_attrnames = set()    85     86         # Modified attributes from usage observations.    87     88         self.modified_attributes = {}    89     90         # Accesses that are assignments or invocations.    91     92         self.reference_assignments = set()    93         self.reference_invocations = set()    94     95         # Map locations to types, constrained indicators and attributes.    96     97         self.accessor_class_types = {}    98         self.accessor_instance_types = {}    99         self.accessor_module_types = {}   100         self.provider_class_types = {}   101         self.provider_instance_types = {}   102         self.provider_module_types = {}   103         self.accessor_constrained = set()   104         self.access_constrained = set()   105         self.referenced_attrs = {}   106         self.referenced_objects = {}   107    108         # Details of access operations.   109    110         self.access_plans = {}   111    112         # Accumulated information about accessors and providers.   113    114         self.accessor_general_class_types = {}   115         self.accessor_general_instance_types = {}   116         self.accessor_general_module_types = {}   117         self.accessor_all_types = {}   118         self.accessor_all_general_types = {}   119         self.provider_all_types = {}   120         self.accessor_guard_tests = {}   121    122         # Accumulated information about accessed attributes and   123         # access/attribute-specific accessor tests.   124    125         self.reference_all_attrs = {}   126         self.reference_all_attrtypes = {}   127         self.reference_all_accessor_types = {}   128         self.reference_all_accessor_general_types = {}   129         self.reference_test_types = {}   130         self.reference_test_accessor_type = {}   131    132         # The processing workflow itself.   133    134         self.init_usage_index()   135         self.init_attr_type_indexes()   136         self.init_combined_attribute_index()   137         self.init_accessors()   138         self.init_accesses()   139         self.init_aliases()   140         self.modify_mutated_attributes()   141         self.identify_references()   142         self.classify_accessors()   143         self.classify_accesses()   144         self.initialise_access_plans()   145         self.identify_dependencies()   146    147     def to_output(self):   148    149         "Write the output files using deduction information."   150    151         self.check_output()   152    153         self.write_mutations()   154         self.write_accessors()   155         self.write_accesses()   156         self.write_access_plans()   157    158     def write_mutations(self):   159    160         """   161         Write mutation-related output in the following format:   162    163         qualified name " " original object type   164    165         Object type can be "<class>", "<function>" or "<var>".   166         """   167    168         f = open(join(self.output, "mutations"), "w")   169         try:   170             attrs = self.modified_attributes.items()   171             attrs.sort()   172    173             for attr, value in attrs:   174                 print >>f, attr, value   175         finally:   176             f.close()   177    178     def write_accessors(self):   179    180         """   181         Write reference-related output in the following format for types:   182    183         location " " ( "constrained" | "deduced" ) " " attribute type " " most general type names " " number of specific types   184    185         Note that multiple lines can be given for each location, one for each   186         attribute type.   187    188         Locations have the following format:   189    190         qualified name of scope "." local name ":" name version   191    192         The attribute type can be "<class>", "<instance>", "<module>" or "<>",   193         where the latter indicates an absence of suitable references.   194    195         Type names indicate the type providing the attributes, being either a   196         class or module qualified name.   197    198         ----   199    200         A summary of accessor types is formatted as follows:   201    202         location " " ( "constrained" | "deduced" ) " " ( "specific" | "common" | "unguarded" ) " " most general type names " " number of specific types   203    204         This summary groups all attribute types (class, instance, module) into a   205         single line in order to determine the complexity of identifying an   206         accessor.   207    208         ----   209    210         References that cannot be supported by any types are written to a   211         warnings file in the following format:   212    213         location   214    215         ----   216    217         For each location where a guard would be asserted to guarantee the   218         nature of an object, the following format is employed:   219    220         location " " ( "specific" | "common" ) " " object kind " " object types   221    222         Object kind can be "<class>", "<instance>" or "<module>".   223         """   224    225         f_type_summary = open(join(self.output, "type_summary"), "w")   226         f_types = open(join(self.output, "types"), "w")   227         f_warnings = open(join(self.output, "type_warnings"), "w")   228         f_guards = open(join(self.output, "guards"), "w")   229    230         try:   231             locations = self.accessor_class_types.keys()   232             locations.sort()   233    234             for location in locations:   235                 constrained = location in self.accessor_constrained   236    237                 # Accessor information.   238    239                 class_types = self.accessor_class_types[location]   240                 instance_types = self.accessor_instance_types[location]   241                 module_types = self.accessor_module_types[location]   242    243                 general_class_types = self.accessor_general_class_types[location]   244                 general_instance_types = self.accessor_general_instance_types[location]   245                 general_module_types = self.accessor_general_module_types[location]   246    247                 all_types = self.accessor_all_types[location]   248                 all_general_types = self.accessor_all_general_types[location]   249    250                 if class_types:   251                     print >>f_types, encode_location(location), encode_constrained(constrained), "<class>", \   252                         sorted_output(general_class_types), len(class_types)   253    254                 if instance_types:   255                     print >>f_types, encode_location(location), encode_constrained(constrained), "<instance>", \   256                         sorted_output(general_instance_types), len(instance_types)   257    258                 if module_types:   259                     print >>f_types, encode_location(location), encode_constrained(constrained), "<module>", \   260                         sorted_output(general_module_types), len(module_types)   261    262                 if not all_types:   263                     print >>f_types, encode_location(location), "deduced", "<>", 0   264                     attrnames = list(self.location_index[location])   265                     attrnames.sort()   266                     print >>f_warnings, encode_location(location), "; ".join(map(encode_usage, attrnames))   267    268                 guard_test = self.accessor_guard_tests.get(location)   269                 if guard_test:   270                     guard_test_type, guard_test_arg = guard_test   271    272                 # Write specific type guard details.   273    274                 if guard_test and guard_test_type == "specific":   275                     print >>f_guards, encode_location(location), "-".join(guard_test), \   276                         first(get_kinds(all_types)), \   277                         sorted_output(all_types)   278    279                 # Write common type guard details.   280    281                 elif guard_test and guard_test_type == "common":   282                     print >>f_guards, encode_location(location), "-".join(guard_test), \   283                         first(get_kinds(all_general_types)), \   284                         sorted_output(all_general_types)   285    286                 print >>f_type_summary, encode_location(location), encode_constrained(constrained), \   287                     guard_test and "-".join(guard_test) or "unguarded", sorted_output(all_general_types), len(all_types)   288    289         finally:   290             f_type_summary.close()   291             f_types.close()   292             f_warnings.close()   293             f_guards.close()   294    295     def write_accesses(self):   296    297         """   298         Specific attribute output is produced in the following format:   299    300         location " " ( "constrained" | "deduced" ) " " attribute type " " attribute references   301    302         Note that multiple lines can be given for each location and attribute   303         name, one for each attribute type.   304    305         Locations have the following format:   306    307         qualified name of scope "." local name " " attribute name ":" access number   308    309         The attribute type can be "<class>", "<instance>", "<module>" or "<>",   310         where the latter indicates an absence of suitable references.   311    312         Attribute references have the following format:   313    314         object type ":" qualified name   315    316         Object type can be "<class>", "<function>" or "<var>".   317    318         ----   319    320         A summary of attributes is formatted as follows:   321    322         location " " attribute name " " ( "constrained" | "deduced" ) " " test " " attribute references   323    324         This summary groups all attribute types (class, instance, module) into a   325         single line in order to determine the complexity of each access.   326    327         Tests can be "validate", "specific", "untested", "guarded-validate" or "guarded-specific".   328    329         ----   330    331         For each access where a test would be asserted to guarantee the   332         nature of an attribute, the following formats are employed:   333    334         location " " attribute name " " "validate"   335         location " " attribute name " " "specific" " " attribute type " " object type   336    337         ----   338    339         References that cannot be supported by any types are written to a   340         warnings file in the following format:   341    342         location   343         """   344    345         f_attr_summary = open(join(self.output, "attribute_summary"), "w")   346         f_attrs = open(join(self.output, "attributes"), "w")   347         f_tests = open(join(self.output, "tests"), "w")   348         f_warnings = open(join(self.output, "attribute_warnings"), "w")   349    350         try:   351             locations = self.referenced_attrs.keys()   352             locations.sort()   353    354             for location in locations:   355                 constrained = location in self.access_constrained   356    357                 # Attribute information, both name-based and anonymous.   358    359                 referenced_attrs = self.referenced_attrs[location]   360    361                 if referenced_attrs:   362                     attrname = get_attrname_from_location(location)   363    364                     all_accessed_attrs = self.reference_all_attrs[location]   365    366                     for attrtype, attrs in self.get_referenced_attrs(location):   367                         print >>f_attrs, encode_access_location(location), encode_constrained(constrained), attrtype, sorted_output(attrs)   368    369                     test_type = self.reference_test_types.get(location)   370    371                     # Write the need to test at run time.   372    373                     if test_type[0] == "validate":   374                         print >>f_tests, encode_access_location(location), "-".join(test_type)   375    376                     # Write any type checks for anonymous accesses.   377    378                     elif test_type and self.reference_test_accessor_type.get(location):   379                         print >>f_tests, encode_access_location(location), "-".join(test_type), \   380                             sorted_output(all_accessed_attrs), \   381                             self.reference_test_accessor_type[location]   382    383                     print >>f_attr_summary, encode_access_location(location), encode_constrained(constrained), \   384                         test_type and "-".join(test_type) or "untested", sorted_output(all_accessed_attrs)   385    386                 else:   387                     print >>f_warnings, encode_access_location(location)   388    389         finally:   390             f_attr_summary.close()   391             f_attrs.close()   392             f_tests.close()   393             f_warnings.close()   394    395     def write_access_plans(self):   396    397         """   398         Each attribute access is written out as a plan of the following form:   399    400         location " " name " " test " " test type " " base " " traversed attributes   401                  " " attributes to traverse " " context " " access method   402                  " " static attribute " " accessor kinds   403         """   404    405         f_attrs = open(join(self.output, "attribute_plans"), "w")   406    407         try:   408             locations = self.access_plans.keys()   409             locations.sort()   410    411             for location in locations:   412                 name, test, test_type, base, \   413                     traversed, traversal_modes, attrnames, \   414                     context, context_test, \   415                     first_method, final_method, \   416                     attr, accessor_kinds = self.access_plans[location]   417    418                 print >>f_attrs, encode_access_location(location), \   419                                  name or "{}", \   420                                  test and "-".join(test) or "{}", \   421                                  test_type or "{}", \   422                                  base or "{}", \   423                                  ".".join(traversed) or "{}", \   424                                  ".".join(traversal_modes) or "{}", \   425                                  ".".join(attrnames) or "{}", \   426                                  context, context_test, \   427                                  first_method, final_method, attr or "{}", \   428                                  ",".join(accessor_kinds)   429    430         finally:   431             f_attrs.close()   432    433     def classify_accessors(self):   434    435         "For each program location, classify accessors."   436    437         # Where instance and module types are defined, class types are also   438         # defined. See: init_definition_details   439    440         locations = self.accessor_class_types.keys()   441    442         for location in locations:   443             constrained = location in self.accessor_constrained   444    445             # Provider information.   446    447             class_types = self.provider_class_types[location]   448             instance_types = self.provider_instance_types[location]   449             module_types = self.provider_module_types[location]   450    451             # Collect specific and general type information.   452    453             self.provider_all_types[location] = \   454                 combine_types(class_types, instance_types, module_types)   455    456             # Accessor information.   457    458             class_types = self.accessor_class_types[location]   459             self.accessor_general_class_types[location] = \   460                 general_class_types = self.get_most_general_class_types(class_types)   461    462             instance_types = self.accessor_instance_types[location]   463             self.accessor_general_instance_types[location] = \   464                 general_instance_types = self.get_most_general_class_types(instance_types)   465    466             module_types = self.accessor_module_types[location]   467             self.accessor_general_module_types[location] = \   468                 general_module_types = self.get_most_general_module_types(module_types)   469    470             # Collect specific and general type information.   471    472             self.accessor_all_types[location] = all_types = \   473                 combine_types(class_types, instance_types, module_types)   474    475             self.accessor_all_general_types[location] = all_general_types = \   476                 combine_types(general_class_types, general_instance_types, general_module_types)   477    478             # Record guard information.   479    480             if not constrained:   481    482                 # Record specific type guard details.   483    484                 if len(all_types) == 1:   485                     self.accessor_guard_tests[location] = ("specific", test_label_for_type(first(all_types)))   486                 elif is_single_class_type(all_types):   487                     self.accessor_guard_tests[location] = ("specific", "object")   488    489                 # Record common type guard details.   490    491                 elif len(all_general_types) == 1:   492                     self.accessor_guard_tests[location] = ("common", test_label_for_type(first(all_types)))   493                 elif is_single_class_type(all_general_types):   494                     self.accessor_guard_tests[location] = ("common", "object")   495    496                 # Otherwise, no convenient guard can be defined.   497    498     def classify_accesses(self):   499    500         "For each program location, classify accesses."   501    502         # Attribute accesses use potentially different locations to those of   503         # accessors.   504    505         locations = self.referenced_attrs.keys()   506    507         for location in locations:   508             constrained = location in self.access_constrained   509    510             # Combine type information from all accessors supplying the access.   511    512             accessor_locations = self.get_accessors_for_access(location)   513    514             all_provider_types = set()   515             all_accessor_types = set()   516             all_accessor_general_types = set()   517    518             for accessor_location in accessor_locations:   519    520                 # Obtain the provider types for guard-related attribute access   521                 # checks.   522    523                 all_provider_types.update(self.provider_all_types.get(accessor_location))   524    525                 # Obtain the accessor guard types (specific and general).   526    527                 all_accessor_types.update(self.accessor_all_types.get(accessor_location))   528                 all_accessor_general_types.update(self.accessor_all_general_types.get(accessor_location))   529    530             # Obtain basic properties of the types involved in the access.   531    532             single_accessor_type = len(all_accessor_types) == 1   533             single_accessor_class_type = is_single_class_type(all_accessor_types)   534             single_accessor_general_type = len(all_accessor_general_types) == 1   535             single_accessor_general_class_type = is_single_class_type(all_accessor_general_types)   536    537             # Determine whether the attribute access is guarded or not.   538    539             guarded = (   540                 single_accessor_type or single_accessor_class_type or   541                 single_accessor_general_type or single_accessor_general_class_type   542                 )   543    544             if guarded:   545                 (guard_class_types, guard_instance_types, guard_module_types,   546                     _function_types, _var_types) = separate_types(all_provider_types)   547    548             self.reference_all_accessor_types[location] = all_accessor_types   549             self.reference_all_accessor_general_types[location] = all_accessor_general_types   550    551             # Attribute information, both name-based and anonymous.   552    553             referenced_attrs = self.referenced_attrs[location]   554    555             if not referenced_attrs:   556                 raise DeduceError("In %s, access via %s to attribute %s (occurrence %d) cannot be identified." % location)   557    558             # Record attribute information for each name used on the   559             # accessor.   560    561             attrname = get_attrname_from_location(location)   562    563             all_accessed_attrs = set()   564             all_providers = set()   565    566             # Obtain provider and attribute details for this kind of   567             # object.   568    569             for attrtype, object_type, attr in referenced_attrs:   570                 all_accessed_attrs.add(attr)   571                 all_providers.add(object_type)   572    573             all_general_providers = self.get_most_general_types(all_providers)   574    575             # Determine which attributes would be provided by the   576             # accessor types upheld by a guard.   577    578             if guarded:   579                 guard_attrs = set()   580                 for _attrtype, object_type, attr in \   581                     self._identify_reference_attribute(attrname, guard_class_types, guard_instance_types, guard_module_types):   582                     guard_attrs.add(attr)   583             else:   584                 guard_attrs = None   585    586             self.reference_all_attrs[location] = all_accessed_attrs   587    588             # Constrained accesses guarantee the nature of the accessor.   589             # However, there may still be many types involved.   590    591             if constrained:   592                 if single_accessor_type:   593                     self.reference_test_types[location] = ("constrained", "specific", test_label_for_type(first(all_accessor_types)))   594                 elif single_accessor_class_type:   595                     self.reference_test_types[location] = ("constrained", "specific", "object")   596                 elif single_accessor_general_type:   597                     self.reference_test_types[location] = ("constrained", "common", test_label_for_type(first(all_accessor_general_types)))   598                 elif single_accessor_general_class_type:   599                     self.reference_test_types[location] = ("constrained", "common", "object")   600                 else:   601                     self.reference_test_types[location] = ("constrained", "many")   602    603             # Suitably guarded accesses, where the nature of the   604             # accessor can be guaranteed, do not require the attribute   605             # involved to be validated. Otherwise, for unguarded   606             # accesses, access-level tests are required.   607    608             elif guarded and all_accessed_attrs.issubset(guard_attrs):   609                 if single_accessor_type:   610                     self.reference_test_types[location] = ("guarded", "specific", test_label_for_type(first(all_accessor_types)))   611                 elif single_accessor_class_type:   612                     self.reference_test_types[location] = ("guarded", "specific", "object")   613                 elif single_accessor_general_type:   614                     self.reference_test_types[location] = ("guarded", "common", test_label_for_type(first(all_accessor_general_types)))   615                 elif single_accessor_general_class_type:   616                     self.reference_test_types[location] = ("guarded", "common", "object")   617    618             # Record the need to test the type of anonymous and   619             # unconstrained accessors.   620    621             elif len(all_providers) == 1:   622                 provider = first(all_providers)   623                 if provider != '__builtins__.object':   624                     all_accessor_kinds = set(get_kinds(all_accessor_types))   625                     if len(all_accessor_kinds) == 1:   626                         test_type = ("test", "specific", test_label_for_kind(first(all_accessor_kinds)))   627                     else:   628                         test_type = ("test", "specific", "object")   629                     self.reference_test_types[location] = test_type   630                     self.reference_test_accessor_type[location] = provider   631    632             elif len(all_general_providers) == 1:   633                 provider = first(all_general_providers)   634                 if provider != '__builtins__.object':   635                     all_accessor_kinds = set(get_kinds(all_accessor_general_types))   636                     if len(all_accessor_kinds) == 1:   637                         test_type = ("test", "common", test_label_for_kind(first(all_accessor_kinds)))   638                     else:   639                         test_type = ("test", "common", "object")   640                     self.reference_test_types[location] = test_type   641                     self.reference_test_accessor_type[location] = provider   642    643             # Record the need to test the identity of the attribute.   644    645             else:   646                 self.reference_test_types[location] = ("validate",)   647    648     def initialise_access_plans(self):   649    650         "Define attribute access plans."   651    652         for location in self.referenced_attrs.keys():   653             original_location = self.const_accesses_rev.get(location)   654             self.access_plans[original_location or location] = self.get_access_plan(location)   655    656     def identify_dependencies(self):   657    658         "Introduce more module dependencies to the importer."   659    660         for location, referenced_attrs in self.referenced_attrs.items():   661             path, name, attrnames, version = location   662    663             # Identify module-level paths.   664    665             if self.importer.modules.has_key(path):   666                 module_name = path   667    668             # Identify the module containing other paths.   669    670             else:   671                 ref = self.importer.identify(path)   672                 for objpath in ref.ancestors():   673                     if self.importer.modules.has_key(objpath):   674                         module_name = objpath   675                         break   676                 else:   677                     raise DeduceError("Cannot find module for path %s." % path)   678    679             # Identify references providing dependencies.   680    681             for attrtype, objtype, attr in referenced_attrs:   682                 self.importer.add_dependency(path, attr.get_origin())   683    684     def get_referenced_attrs(self, location):   685    686         """   687         Return attributes referenced at the given access 'location' by the given   688         'attrname' as a list of (attribute type, attribute set) tuples.   689         """   690    691         d = {}   692         for attrtype, objtype, attr in self.referenced_attrs[location]:   693             init_item(d, attrtype, set)   694             d[attrtype].add(attr.unaliased())   695         l = d.items()   696         l.sort() # class, module, instance   697         return l   698    699     # Initialisation methods.   700    701     def init_descendants(self):   702    703         "Identify descendants of each class."   704    705         for name in self.importer.classes.keys():   706             self.get_descendants_for_class(name)   707    708     def get_descendants_for_class(self, name):   709    710         """   711         Use subclass information to deduce the descendants for the class of the   712         given 'name'.   713         """   714    715         if not self.descendants.has_key(name):   716             descendants = set()   717    718             for subclass in self.importer.subclasses[name]:   719                 descendants.update(self.get_descendants_for_class(subclass))   720                 descendants.add(subclass)   721    722             self.descendants[name] = descendants   723    724         return self.descendants[name]   725    726     def init_special_attributes(self):   727    728         "Add special attributes to the classes for inheritance-related tests."   729    730         all_class_attrs = self.importer.all_class_attrs   731    732         for name, descendants in self.descendants.items():   733             for descendant in descendants:   734                 all_class_attrs[descendant]["#%s" % name] = name   735    736         for name in all_class_attrs.keys():   737             all_class_attrs[name]["#%s" % name] = name   738    739     def init_usage_index(self):   740    741         """   742         Create indexes for module and function attribute usage and for anonymous   743         accesses.   744         """   745    746         for module in self.importer.get_modules():   747             for path, assignments in module.attr_usage.items():   748                 self.add_usage(assignments, path)   749    750         for location, all_attrnames in self.importer.all_attr_accesses.items():   751             for attrnames in all_attrnames:   752                 attrname = get_attrnames(attrnames)[-1]   753                 access_location = (location, None, attrnames, 0)   754                 self.add_usage_term(access_location, ((attrname, False, False),))   755    756     def add_usage(self, assignments, path):   757    758         """   759         Collect usage from the given 'assignments', adding 'path' details to   760         each record if specified. Add the usage to an index mapping to location   761         information, as well as to an index mapping locations to usages.   762         """   763    764         for name, versions in assignments.items():   765             for i, usages in enumerate(versions):   766                 location = (path, name, None, i)   767    768                 for usage in usages:   769                     self.add_usage_term(location, usage)   770    771     def add_usage_term(self, location, usage):   772    773         """   774         For 'location' and using 'usage' as a description of usage, record   775         in the usage index a mapping from the usage to 'location', and record in   776         the location index a mapping from 'location' to the usage.   777         """   778    779         init_item(self.location_index, location, set)   780         self.location_index[location].add(usage)   781    782     def init_accessors(self):   783    784         "Create indexes for module and function accessor information."   785    786         for module in self.importer.get_modules():   787             for path, all_accesses in module.attr_accessors.items():   788                 self.add_accessors(all_accesses, path)   789    790     def add_accessors(self, all_accesses, path):   791    792         """   793         For attribute accesses described by the mapping of 'all_accesses' from   794         name details to accessor details, record the locations of the accessors   795         for each access.   796         """   797    798         # Get details for each access combining the given name and attribute.   799    800         for (name, attrnames), accesses in all_accesses.items():   801    802             # Obtain the usage details using the access information.   803    804             for access_number, versions in enumerate(accesses):   805                 access_location = (path, name, attrnames, access_number)   806                 locations = []   807    808                 for version in versions:   809                     location = (path, name, None, version)   810                     locations.append(location)   811    812                 self.access_index[access_location] = locations   813    814     def get_accessors_for_access(self, access_location):   815    816         "Find a definition providing accessor details, if necessary."   817    818         try:   819             return self.access_index[access_location]   820         except KeyError:   821             return [access_location]   822    823     def init_accesses(self):   824    825         """   826         Check that attributes used in accesses are actually defined on some   827         object. This can be overlooked if unknown attributes are employed in   828         attribute chains.   829    830         Initialise collections for accesses involving assignments.   831         """   832    833         # For each scope, obtain access details.   834    835         for path, all_accesses in self.importer.all_attr_access_modifiers.items():   836    837             # For each combination of name and attribute names, obtain   838             # applicable modifiers.   839    840             for (name, attrname_str), modifiers in all_accesses.items():   841    842                 # For each access, determine the name versions affected by   843                 # assignments.   844    845                 for access_number, (assignment, invocation) in enumerate(modifiers):   846    847                     if name:   848                         access_location = (path, name, attrname_str, access_number)   849                     else:   850                         access_location = (path, None, attrname_str, 0)   851    852                     # Plain name accesses do not employ attributes and are   853                     # ignored.   854    855                     if not attrname_str:   856                         continue   857    858                     attrnames = get_attrnames(attrname_str)   859    860                     # Check the attribute names.   861    862                     for attrname in attrnames:   863                         if not attrname in self.all_attrnames:   864                             raise DeduceError("In %s, attribute %s is not defined in the program." % (path, attrname))   865    866                     # Now only process assignments and invocations.   867    868                     if invocation:   869                         self.reference_invocations.add(access_location)   870                         continue   871                     elif not assignment:   872                         continue   873    874                     # Associate assignments with usage.   875    876                     self.reference_assignments.add(access_location)   877    878                     # Assignment occurs for the only attribute.   879    880                     if len(attrnames) == 1:   881                         accessor_locations = self.get_accessors_for_access(access_location)   882    883                         for location in accessor_locations:   884                             for usage in self.location_index[location]:   885                                 init_item(self.assigned_attrs, usage, set)   886                                 self.assigned_attrs[usage].add((path, name, attrnames[0]))   887    888                     # Assignment occurs for the final attribute.   889    890                     else:   891                         usage = ((attrnames[-1], False, False),)   892                         init_item(self.assigned_attrs, usage, set)   893                         self.assigned_attrs[usage].add((path, name, attrnames[-1]))   894    895     def init_aliases(self):   896    897         "Expand aliases so that alias-based accesses can be resolved."   898    899         # Get aliased names with details of their accesses.   900    901         for name_path, all_aliases in self.importer.all_aliased_names.items():   902             path, name = name_path.rsplit(".", 1)   903    904             # For each version of the name, obtain the access location.   905    906             for version, (original_name, attrnames, access_number) in all_aliases.items():   907                 accessor_location = (path, name, None, version)   908                 access_location = (path, original_name, attrnames, access_number)   909                 init_item(self.alias_index, accessor_location, list)   910                 self.alias_index[accessor_location].append(access_location)   911    912         # Get aliases in terms of non-aliases and accesses.   913    914         for accessor_location, access_locations in self.alias_index.items():   915             self.update_aliases(accessor_location, access_locations)   916    917     def update_aliases(self, accessor_location, access_locations, visited=None):   918    919         """   920         Update the given 'accessor_location' defining an alias, update   921         'access_locations' to refer to non-aliases, following name references   922         via the access index.   923    924         If 'visited' is specified, it contains a set of accessor locations (and   925         thus keys to the alias index) that are currently being defined.   926         """   927    928         if visited is None:   929             visited = set()   930    931         updated_locations = set()   932    933         for access_location in access_locations:   934             (path, original_name, attrnames, access_number) = access_location   935    936             # Where an alias refers to a name access, obtain the original name   937             # version details.   938    939             if attrnames is None:   940    941                 # For each name version, attempt to determine any accesses that   942                 # initialise the name.   943    944                 for name_accessor_location in self.access_index[access_location]:   945    946                     # Already-visited aliases do not contribute details.   947    948                     if name_accessor_location in visited:   949                         continue   950    951                     visited.add(name_accessor_location)   952    953                     name_access_locations = self.alias_index.get(name_accessor_location)   954                     if name_access_locations:   955                         updated_locations.update(self.update_aliases(name_accessor_location, name_access_locations, visited))   956                     else:   957                         updated_locations.add(name_accessor_location)   958    959             # Otherwise, record the access details.   960    961             else:   962                 updated_locations.add(access_location)   963    964         self.alias_index[accessor_location] = updated_locations   965         return updated_locations   966    967     # Attribute mutation for types.   968    969     def modify_mutated_attributes(self):   970    971         "Identify known, mutated attributes and change their state."   972    973         # Usage-based accesses.   974    975         for usage, all_attrnames in self.assigned_attrs.items():   976             if not usage:   977                 continue   978    979             for path, name, attrname in all_attrnames:   980                 class_types = self.get_class_types_for_usage(usage)   981                 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)   982                 module_types = self.get_module_types_for_usage(usage)   983    984                 # Detect self usage within methods in order to narrow the scope   985                 # of the mutation.   986    987                 t = name == "self" and self.constrain_self_reference(path, class_types, only_instance_types)   988                 if t:   989                     class_types, only_instance_types, module_types, constrained = t   990                 objects = set(class_types).union(only_instance_types).union(module_types)   991    992                 self.mutate_attribute(objects, attrname)   993    994     def mutate_attribute(self, objects, attrname):   995    996         "Mutate static 'objects' with the given 'attrname'."   997    998         for name in objects:   999             attr = "%s.%s" % (name, attrname)  1000             value = self.importer.get_object(attr)  1001   1002             # If the value is None, the attribute is  1003             # inherited and need not be set explicitly on  1004             # the class concerned.  1005   1006             if value:  1007                 self.modified_attributes[attr] = value  1008                 self.importer.set_object(attr, value.as_var())  1009   1010     # Simplification of types.  1011   1012     def get_most_general_types(self, types):  1013   1014         "Return the most general types for the given 'types'."  1015   1016         module_types = set()  1017         class_types = set()  1018   1019         for type in types:  1020             ref = self.importer.identify(type)  1021             if ref.has_kind("<module>"):  1022                 module_types.add(type)  1023             else:  1024                 class_types.add(type)  1025   1026         types = set(self.get_most_general_module_types(module_types))  1027         types.update(self.get_most_general_class_types(class_types))  1028         return types  1029   1030     def get_most_general_class_types(self, class_types):  1031   1032         "Return the most general types for the given 'class_types'."  1033   1034         class_types = set(class_types)  1035         to_remove = set()  1036   1037         for class_type in class_types:  1038             for base in self.importer.classes[class_type]:  1039                 base = base.get_origin()  1040                 descendants = self.descendants[base]  1041                 if base in class_types and descendants.issubset(class_types):  1042                     to_remove.update(descendants)  1043   1044         class_types.difference_update(to_remove)  1045         return class_types  1046   1047     def get_most_general_module_types(self, module_types):  1048   1049         "Return the most general type for the given 'module_types'."  1050   1051         # Where all modules are provided, an object would provide the same  1052         # attributes.  1053   1054         if len(module_types) == len(self.importer.modules):  1055             return ["__builtins__.object"]  1056         else:  1057             return module_types  1058   1059     # More efficient usage-to-type indexing and retrieval.  1060   1061     def init_attr_type_indexes(self):  1062   1063         "Identify the types that can support each attribute name."  1064   1065         self._init_attr_type_index(self.attr_class_types, self.importer.all_class_attrs)  1066         self._init_attr_type_index(self.attr_instance_types, self.importer.all_instance_attrs, True)  1067         self._init_attr_type_index(self.attr_instance_types, self.importer.all_combined_attrs, False)  1068         self._init_attr_type_index(self.attr_module_types, self.importer.all_module_attrs)  1069   1070     def _init_attr_type_index(self, attr_types, attrs, assignment=None):  1071   1072         """  1073         Initialise the 'attr_types' attribute-to-types mapping using the given  1074         'attrs' type-to-attributes mapping.  1075         """  1076   1077         for name, attrnames in attrs.items():  1078             for attrname in attrnames:  1079   1080                 # Permit general access for certain kinds of object.  1081   1082                 if assignment is None:  1083                     init_item(attr_types, (attrname, False), set)  1084                     init_item(attr_types, (attrname, True), set)  1085                     attr_types[(attrname, False)].add(name)  1086                     attr_types[(attrname, True)].add(name)  1087   1088                 # Restrict attribute assignment for instances.  1089   1090                 else:  1091                     init_item(attr_types, (attrname, assignment), set)  1092                     attr_types[(attrname, assignment)].add(name)  1093   1094     def get_class_types_for_usage(self, usage):  1095   1096         "Return names of classes supporting the given 'usage'."  1097   1098         return self._get_types_for_usage(usage, self.attr_class_types, self.importer.all_class_attrs)  1099   1100     def get_instance_types_for_usage(self, usage):  1101   1102         """  1103         Return names of classes whose instances support the given 'usage'  1104         (as either class or instance attributes).  1105         """  1106   1107         return self._get_types_for_usage(usage, self.attr_instance_types, self.importer.all_combined_attrs)  1108   1109     def get_module_types_for_usage(self, usage):  1110   1111         "Return names of modules supporting the given 'usage'."  1112   1113         return self._get_types_for_usage(usage, self.attr_module_types, self.importer.all_module_attrs)  1114   1115     def _get_types_for_usage(self, usage, attr_types, attrs):  1116   1117         """  1118         For the given 'usage' representing attribute usage, return types  1119         recorded in the 'attr_types' attribute-to-types mapping that support  1120         such usage, with the given 'attrs' type-to-attributes mapping used to  1121         quickly assess whether a type supports all of the stated attributes.  1122         """  1123   1124         # Where no attributes are used, any type would be acceptable.  1125   1126         if not usage:  1127             return attrs.keys()  1128   1129         keys = []  1130         for attrname, invocation, assignment in usage:  1131             keys.append((attrname, assignment))  1132   1133         # Obtain types supporting the first (attribute name, assignment) key...  1134   1135         types = set(attr_types.get(keys[0]) or [])  1136   1137         for key in keys[1:]:  1138               1139             # Record types that support all of the other attributes as well.  1140   1141             types.intersection_update(attr_types.get(key) or [])  1142   1143         return types  1144   1145     def init_combined_attribute_index(self):  1146   1147         "Initialise a combined index for the detection of invalid attributes."  1148   1149         self.all_attrnames = set()  1150         for attrs in (self.importer.all_combined_attrs, self.importer.all_module_attrs):  1151             for name, attrnames in attrs.items():  1152                 self.all_attrnames.update(attrnames)  1153   1154     # Reference identification.  1155   1156     def identify_references(self):  1157   1158         "Identify references using usage and name reference information."  1159   1160         # Names with associated attribute usage.  1161   1162         for location, usages in self.location_index.items():  1163   1164             # Obtain attribute usage associated with a name, deducing the nature  1165             # of the name. Obtain types only for branches involving attribute  1166             # usage. (In the absence of usage, any type could be involved, but  1167             # then no accesses exist to require knowledge of the type.)  1168   1169             have_usage = False  1170             have_no_usage_branch = False  1171   1172             for usage in usages:  1173                 if not usage:  1174                     have_no_usage_branch = True  1175                     continue  1176                 elif not have_usage:  1177                     self.init_definition_details(location)  1178                     have_usage = True  1179                 self.record_types_for_usage(location, usage)  1180   1181             # Where some usage occurs, but where branches without usage also  1182             # occur, record the types for those branches anyway.  1183   1184             if have_usage and have_no_usage_branch:  1185                 self.init_definition_details(location)  1186                 self.record_types_for_usage(location, None)  1187   1188         # Specific name-based attribute accesses.  1189   1190         alias_accesses = set()  1191   1192         for access_location, accessor_locations in self.access_index.items():  1193             self.record_types_for_access(access_location, accessor_locations, alias_accesses)  1194   1195         # Anonymous references with attribute chains.  1196   1197         for location, accesses in self.importer.all_attr_accesses.items():  1198   1199             # Get distinct attribute names.  1200   1201             all_attrnames = set()  1202   1203             for attrnames in accesses:  1204                 all_attrnames.update(get_attrnames(attrnames))  1205   1206             # Get attribute and accessor details for each attribute name.  1207   1208             for attrname in all_attrnames:  1209                 access_location = (location, None, attrname, 0)  1210                 self.record_types_for_attribute(access_location, attrname)  1211   1212         # References via constant/identified objects.  1213   1214         for location, name_accesses in self.importer.all_const_accesses.items():  1215   1216             # A mapping from the original name and attributes to resolved access  1217             # details.  1218   1219             for original_access, access in name_accesses.items():  1220                 original_name, original_attrnames = original_access  1221                 objpath, ref, attrnames = access  1222   1223                 # Build an accessor combining the name and attribute names used.  1224   1225                 original_accessor = tuple([original_name] + original_attrnames.split("."))  1226   1227                 # Direct accesses to attributes.  1228   1229                 if not attrnames:  1230   1231                     # Build a descriptive location based on the original  1232                     # details, exposing the final attribute name.  1233   1234                     oa, attrname = original_accessor[:-1], original_accessor[-1]  1235                     oa = ".".join(oa)  1236   1237                     access_location = (location, oa, attrname, 0)  1238                     accessor_location = (location, oa, None, 0)  1239                     self.access_index[access_location] = [accessor_location]  1240   1241                     self.init_access_details(access_location)  1242                     self.init_definition_details(accessor_location)  1243   1244                     # Obtain a reference for the accessor in order to properly  1245                     # determine its type.  1246   1247                     if ref.get_kind() != "<instance>":  1248                         objpath = ref.get_origin()  1249   1250                     objpath = objpath.rsplit(".", 1)[0]  1251   1252                     # Where the object name conflicts with the module  1253                     # providing it, obtain the module details.  1254   1255                     if objpath in self.importer.modules:  1256                         accessor = Reference("<module>", objpath)  1257                     else:  1258                         accessor = self.importer.get_object(objpath)  1259   1260                     self.referenced_attrs[access_location] = [(accessor.get_kind(), accessor.get_origin(), ref)]  1261                     self.access_constrained.add(access_location)  1262   1263                     class_types, instance_types, module_types = accessor.get_types()  1264                     self.record_reference_types(accessor_location, class_types, instance_types, module_types, True, True)  1265   1266                 else:  1267   1268                     # Build a descriptive location based on the original  1269                     # details, employing the first remaining attribute name.  1270   1271                     l = get_attrnames(attrnames)  1272                     attrname = l[0]  1273   1274                     oa = original_accessor[:-len(l)]  1275                     oa = ".".join(oa)  1276   1277                     access_location = (location, oa, attrnames, 0)  1278                     accessor_location = (location, oa, None, 0)  1279                     self.access_index[access_location] = [accessor_location]  1280   1281                     self.init_access_details(access_location)  1282                     self.init_definition_details(accessor_location)  1283   1284                     class_types, instance_types, module_types = ref.get_types()  1285   1286                     self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, True)  1287                     self.record_reference_types(accessor_location, class_types, instance_types, module_types, True, True)  1288   1289                 original_location = (location, original_name, original_attrnames, 0)  1290   1291                 if original_location != access_location:  1292                     self.const_accesses[original_location] = access_location  1293                     self.const_accesses_rev[access_location] = original_location  1294   1295         # Aliased name definitions. All aliases with usage will have been  1296         # defined, but they may be refined according to referenced accesses.  1297   1298         for accessor_location in self.alias_index.keys():  1299             self.record_types_for_alias(accessor_location)  1300   1301         # Update accesses employing aliases.  1302   1303         for access_location in alias_accesses:  1304             self.record_types_for_access(access_location, self.access_index[access_location])  1305   1306     def constrain_types(self, path, class_types, instance_types, module_types):  1307   1308         """  1309         Using the given 'path' to an object, constrain the given 'class_types',  1310         'instance_types' and 'module_types'.  1311   1312         Return the class, instance, module types plus whether the types are  1313         constrained to a specific kind of type.  1314         """  1315   1316         ref = self.importer.identify(path)  1317         if ref:  1318   1319             # Constrain usage suggestions using the identified object.  1320   1321             if ref.has_kind("<class>"):  1322                 return (  1323                     set(class_types).intersection([ref.get_origin()]), [], [], True  1324                     )  1325             elif ref.has_kind("<module>"):  1326                 return (  1327                     [], [], set(module_types).intersection([ref.get_origin()]), True  1328                     )  1329   1330         return class_types, instance_types, module_types, False  1331   1332     def get_target_types(self, location, usage):  1333   1334         """  1335         Return the class, instance and module types constrained for the name at  1336         the given 'location' exhibiting the given 'usage'. Whether the types  1337         have been constrained using contextual information is also indicated,  1338         plus whether the types have been constrained to a specific kind of type.  1339         """  1340   1341         unit_path, name, attrnames, version = location  1342         have_assignments = get_assigned_attributes(usage)  1343   1344         # Detect any initialised name for the location.  1345   1346         if name:  1347             ref = self.get_initialised_name(location)  1348             if ref:  1349                 (class_types, only_instance_types, module_types,  1350                     _function_types, _var_types) = separate_types([ref])  1351                 return class_types, only_instance_types, module_types, True, have_assignments  1352   1353         # Retrieve the recorded types for the usage.  1354   1355         class_types = self.get_class_types_for_usage(usage)  1356         only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)  1357         module_types = self.get_module_types_for_usage(usage)  1358   1359         # Merge usage deductions with observations to obtain reference types  1360         # for names involved with attribute accesses.  1361   1362         if not name:  1363             return class_types, only_instance_types, module_types, False, have_assignments  1364   1365         # Obtain references to known objects.  1366   1367         path = get_name_path(unit_path, name)  1368   1369         class_types, only_instance_types, module_types, constrained_specific = \  1370             self.constrain_types(path, class_types, only_instance_types, module_types)  1371   1372         if constrained_specific:  1373             return class_types, only_instance_types, module_types, constrained_specific, \  1374                 constrained_specific or have_assignments  1375   1376         # Constrain "self" references.  1377   1378         if name == "self":  1379   1380             # Test for the class of the method in the deduced types.  1381   1382             class_name = self.in_method(unit_path)  1383   1384             if class_name and class_name not in class_types and class_name not in only_instance_types:  1385                 raise DeduceError("In %s, usage {%s} is not directly supported by class %s or its instances." %  1386                                   (unit_path, encode_usage(usage), class_name))  1387   1388             # Constrain the types to the class's hierarchy.  1389   1390             t = self.constrain_self_reference(unit_path, class_types, only_instance_types)  1391             if t:  1392                 class_types, only_instance_types, module_types, constrained = t  1393                 return class_types, only_instance_types, module_types, constrained, have_assignments  1394   1395         return class_types, only_instance_types, module_types, False, have_assignments  1396   1397     def constrain_self_reference(self, unit_path, class_types, only_instance_types):  1398   1399         """  1400         Where the name "self" appears in a method, attempt to constrain the  1401         classes involved.  1402   1403         Return the class, instance, module types plus whether the types are  1404         constrained.  1405         """  1406   1407         class_name = self.in_method(unit_path)  1408   1409         if not class_name:  1410             return None  1411   1412         classes = set([class_name])  1413         classes.update(self.get_descendants_for_class(class_name))  1414   1415         # Note that only instances will be expected for these references but  1416         # either classes or instances may provide the attributes.  1417   1418         return (  1419             set(class_types).intersection(classes),  1420             set(only_instance_types).intersection(classes),  1421             [], True  1422             )  1423   1424     def in_method(self, path):  1425   1426         "Return whether 'path' refers to a method."  1427   1428         class_name, method_name = path.rsplit(".", 1)  1429         return class_name != "__builtins__.core.type" and self.importer.classes.has_key(class_name) and class_name  1430   1431     def init_reference_details(self, location):  1432   1433         "Initialise reference-related details for 'location'."  1434   1435         self.init_definition_details(location)  1436         self.init_access_details(location)  1437   1438     def init_definition_details(self, location):  1439   1440         "Initialise name definition details for 'location'."  1441   1442         self.accessor_class_types[location] = set()  1443         self.accessor_instance_types[location] = set()  1444         self.accessor_module_types[location] = set()  1445         self.provider_class_types[location] = set()  1446         self.provider_instance_types[location] = set()  1447         self.provider_module_types[location] = set()  1448   1449     def init_access_details(self, location):  1450   1451         "Initialise access details at 'location'."  1452   1453         self.referenced_attrs[location] = {}  1454   1455     def record_types_for_access(self, access_location, accessor_locations, alias_accesses=None):  1456   1457         """  1458         Define types for the 'access_location' associated with the given  1459         'accessor_locations'.  1460         """  1461   1462         attrname = get_attrname_from_location(access_location)  1463         if not attrname:  1464             return  1465   1466         # Collect all suggested types for the accessors. Accesses may  1467         # require accessors from of a subset of the complete set of types.  1468   1469         class_types = set()  1470         module_types = set()  1471         instance_types = set()  1472   1473         constrained = True  1474   1475         for location in accessor_locations:  1476   1477             # Remember accesses employing aliases.  1478   1479             if alias_accesses is not None and self.alias_index.has_key(location):  1480                 alias_accesses.add(access_location)  1481   1482             # Use the type information deduced for names from above.  1483   1484             if self.accessor_class_types.has_key(location):  1485                 class_types.update(self.accessor_class_types[location])  1486                 module_types.update(self.accessor_module_types[location])  1487                 instance_types.update(self.accessor_instance_types[location])  1488   1489             # Where accesses are associated with assignments but where no  1490             # attribute usage observations have caused such an association,  1491             # the attribute name is considered by itself.  1492   1493             else:  1494                 self.init_definition_details(location)  1495                 self.record_types_for_usage(location, [(attrname, False, False)])  1496   1497             constrained = location in self.accessor_constrained and constrained  1498   1499         self.init_access_details(access_location)  1500         self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained)  1501   1502     def record_types_for_usage(self, accessor_location, usage):  1503   1504         """  1505         Record types for the given 'accessor_location' according to the given  1506         'usage' observations which may be None to indicate an absence of usage.  1507         """  1508   1509         (class_types,  1510          instance_types,  1511          module_types,  1512          constrained,  1513          constrained_specific) = self.get_target_types(accessor_location, usage)  1514   1515         invocations = get_invoked_attributes(usage)  1516   1517         self.record_reference_types(accessor_location, class_types, instance_types,  1518             module_types, constrained, constrained_specific, invocations)  1519   1520     def record_types_for_attribute(self, access_location, attrname):  1521   1522         """  1523         Record types for the 'access_location' employing only the given  1524         'attrname' for type deduction.  1525         """  1526   1527         (class_types,  1528          only_instance_types,  1529          module_types) = self.get_types_for_attribute(attrname)  1530   1531         self.init_reference_details(access_location)  1532   1533         self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False)  1534         self.record_reference_types(access_location, class_types, only_instance_types, module_types, False)  1535   1536     def get_types_for_attribute(self, attrname):  1537   1538         "Return class, instance-only and module types supporting 'attrname'."  1539   1540         usage = ((attrname, False, False),)  1541   1542         class_types = self.get_class_types_for_usage(usage)  1543         only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)  1544         module_types = self.get_module_types_for_usage(usage)  1545   1546         return class_types, only_instance_types, module_types  1547   1548     def record_types_for_alias(self, accessor_location):  1549   1550         """  1551         Define types for the 'accessor_location' not having associated usage.  1552         """  1553   1554         have_access = self.provider_class_types.has_key(accessor_location)  1555   1556         # With an access, attempt to narrow the existing selection of provider  1557         # types.  1558   1559         if have_access:  1560             provider_class_types = self.provider_class_types[accessor_location]  1561             provider_instance_types = self.provider_instance_types[accessor_location]  1562             provider_module_types = self.provider_module_types[accessor_location]  1563   1564             # Find details for any corresponding access.  1565   1566             all_class_types = set()  1567             all_instance_types = set()  1568             all_module_types = set()  1569   1570             for access_location in self.alias_index[accessor_location]:  1571                 location, name, attrnames, access_number = access_location  1572   1573                 # Alias references an attribute access.  1574   1575                 if attrnames:  1576   1577                     # Obtain attribute references for the access.  1578   1579                     attrs = [attr for _attrtype, object_type, attr in self.referenced_attrs[access_location]]  1580   1581                     # Separate the different attribute types.  1582   1583                     (class_types, instance_types, module_types,  1584                         function_types, var_types) = separate_types(attrs)  1585   1586                     # Where non-accessor types are found, do not attempt to refine  1587                     # the defined accessor types.  1588   1589                     if function_types or var_types:  1590                         return  1591   1592                     class_types = set(provider_class_types).intersection(class_types)  1593                     instance_types = set(provider_instance_types).intersection(instance_types)  1594                     module_types = set(provider_module_types).intersection(module_types)  1595   1596                 # Alias references a name, not an access.  1597   1598                 else:  1599                     # Attempt to refine the types using initialised names.  1600   1601                     attr = self.get_initialised_name(access_location)  1602                     if attr:  1603                         (class_types, instance_types, module_types,  1604                             _function_types, _var_types) = separate_types([attr])  1605   1606                     # Where no further information is found, do not attempt to  1607                     # refine the defined accessor types.  1608   1609                     else:  1610                         return  1611   1612                 all_class_types.update(class_types)  1613                 all_instance_types.update(instance_types)  1614                 all_module_types.update(module_types)  1615   1616             # Record refined type details for the alias as an accessor.  1617   1618             self.init_definition_details(accessor_location)  1619             self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False)  1620   1621         # Without an access, attempt to identify references for the alias.  1622   1623         else:  1624             refs = set()  1625   1626             for access_location in self.alias_index[accessor_location]:  1627   1628                 # Obtain any redefined constant access location.  1629   1630                 if self.const_accesses.has_key(access_location):  1631                     access_location = self.const_accesses[access_location]  1632   1633                 location, name, attrnames, access_number = access_location  1634   1635                 # Alias references an attribute access.  1636   1637                 if attrnames:  1638                     attrs = [attr for attrtype, object_type, attr in self.referenced_attrs[access_location]]  1639                     refs.update(attrs)  1640   1641                 # Alias references a name, not an access.  1642   1643                 else:  1644                     attr = self.get_initialised_name(access_location)  1645                     attrs = attr and [attr] or []  1646                     if not attrs and self.provider_class_types.has_key(access_location):  1647                         class_types = self.provider_class_types[access_location]  1648                         instance_types = self.provider_instance_types[access_location]  1649                         module_types = self.provider_module_types[access_location]  1650                         attrs = combine_types(class_types, instance_types, module_types)  1651                     if attrs:  1652                         refs.update(attrs)  1653   1654             # Record reference details for the alias separately from accessors.  1655   1656             self.referenced_objects[accessor_location] = refs  1657   1658     def get_initialised_name(self, access_location):  1659   1660         """  1661         Return references for any initialised names at 'access_location', or  1662         None if no such references exist.  1663         """  1664   1665         location, name, attrnames, version = access_location  1666         path = get_name_path(location, name)  1667   1668         # Use initialiser information, if available.  1669   1670         refs = self.importer.all_initialised_names.get(path)  1671         if refs and refs.has_key(version):  1672             return refs[version]  1673         else:  1674             return None  1675   1676     def record_reference_types(self, location, class_types, instance_types,  1677         module_types, constrained, constrained_specific=False, invocations=None):  1678   1679         """  1680         Associate attribute provider types with the given 'location', consisting  1681         of the given 'class_types', 'instance_types' and 'module_types'.  1682   1683         If 'constrained' is indicated, the constrained nature of the accessor is  1684         recorded for the location.  1685   1686         If 'constrained_specific' is indicated using a true value, instance types  1687         will not be added to class types to permit access via instances at the  1688         given location. This is only useful where a specific accessor is known  1689         to be a class.  1690   1691         If 'invocations' is given, the given attribute names indicate those  1692         which are involved in invocations. Such invocations, if involving  1693         functions, will employ those functions as bound methods and will  1694         therefore not support classes as accessors, only instances of such  1695         classes.  1696   1697         Note that the specified types only indicate the provider types for  1698         attributes, whereas the recorded accessor types indicate the possible  1699         types of the actual objects used to access attributes.  1700         """  1701   1702         # Update the type details for the location.  1703   1704         self.provider_class_types[location].update(class_types)  1705         self.provider_instance_types[location].update(instance_types)  1706         self.provider_module_types[location].update(module_types)  1707   1708         # Class types support classes and instances as accessors.  1709         # Instance-only and module types support only their own kinds as  1710         # accessors.  1711   1712         path, name, version, attrnames = location  1713   1714         if invocations:  1715             class_only_types = self.filter_for_invocations(class_types, invocations)  1716         else:  1717             class_only_types = class_types  1718   1719         # However, the nature of accessors can be further determined.  1720         # Any self variable may only refer to an instance.  1721   1722         if name != "self" or not self.in_method(path):  1723             self.accessor_class_types[location].update(class_only_types)  1724   1725         if not constrained_specific:  1726             self.accessor_instance_types[location].update(class_types)  1727   1728         self.accessor_instance_types[location].update(instance_types)  1729   1730         if name != "self" or not self.in_method(path):  1731             self.accessor_module_types[location].update(module_types)  1732   1733         if constrained:  1734             self.accessor_constrained.add(location)  1735   1736     def filter_for_invocations(self, class_types, attrnames):  1737   1738         """  1739         From the given 'class_types', identify methods for the given  1740         'attrnames' that are being invoked, returning a filtered collection of  1741         class types.  1742         """  1743   1744         to_filter = set()  1745   1746         for class_type in class_types:  1747             for attrname in attrnames:  1748                 ref = self.importer.get_class_attribute(class_type, attrname)  1749                 parent_class = ref and ref.parent()  1750   1751                 if ref and ref.has_kind("<function>") and (  1752                    parent_class == class_type or  1753                    class_type in self.descendants[parent_class]):  1754   1755                     to_filter.add(class_type)  1756                     break  1757   1758         return set(class_types).difference(to_filter)  1759   1760     def identify_reference_attributes(self, location, attrname, class_types, instance_types, module_types, constrained):  1761   1762         """  1763         Identify reference attributes, associating them with the given  1764         'location', identifying the given 'attrname', employing the given  1765         'class_types', 'instance_types' and 'module_types'.  1766   1767         If 'constrained' is indicated, the constrained nature of the access is  1768         recorded for the location.  1769         """  1770   1771         # Record the referenced objects.  1772   1773         self.referenced_attrs[location] = \  1774             self._identify_reference_attribute(attrname, class_types, instance_types, module_types)  1775   1776         if constrained:  1777             self.access_constrained.add(location)  1778   1779     def _identify_reference_attribute(self, attrname, class_types, instance_types, module_types):  1780   1781         """  1782         Identify the reference attribute with the given 'attrname', employing  1783         the given 'class_types', 'instance_types' and 'module_types'.  1784         """  1785   1786         attrs = set()  1787   1788         # The class types expose class attributes either directly or via  1789         # instances.  1790   1791         for object_type in class_types:  1792             ref = self.importer.get_class_attribute(object_type, attrname)  1793             if ref:  1794                 attrs.add(("<class>", object_type, ref))  1795   1796             # Add any distinct instance attributes that would be provided  1797             # by instances also providing indirect class attribute access.  1798   1799             for ref in self.importer.get_instance_attributes(object_type, attrname):  1800                 attrs.add(("<instance>", object_type, ref))  1801   1802         # The instance-only types expose instance attributes, but although  1803         # classes are excluded as potential accessors (since they do not provide  1804         # the instance attributes), the class types may still provide some  1805         # attributes.  1806   1807         for object_type in instance_types:  1808             instance_attrs = self.importer.get_instance_attributes(object_type, attrname)  1809   1810             if instance_attrs:  1811                 for ref in instance_attrs:  1812                     attrs.add(("<instance>", object_type, ref))  1813             else:  1814                 ref = self.importer.get_class_attribute(object_type, attrname)  1815                 if ref:  1816                     attrs.add(("<class>", object_type, ref))  1817   1818         # Module types expose module attributes for module accessors.  1819   1820         for object_type in module_types:  1821             ref = self.importer.get_module_attribute(object_type, attrname)  1822             if ref:  1823                 attrs.add(("<module>", object_type, ref))  1824   1825         return attrs  1826   1827     class_tests = (  1828         ("guarded", "specific", "type"),  1829         ("guarded", "common", "type"),  1830         ("test", "specific", "type"),  1831         ("test", "common", "type"),  1832         )  1833   1834     def get_access_plan(self, location):  1835   1836         """  1837         Return details of the access at the given 'location'. The details are as  1838         follows:  1839   1840          * the initial accessor (from which accesses will be performed if no  1841            computed static accessor is found)  1842          * details of any test required on the initial accessor  1843          * details of any type employed by the test  1844          * any static accessor (from which accesses will be performed in  1845            preference to the initial accessor)  1846          * attributes needing to be traversed from the base that yield  1847            unambiguous objects  1848          * access modes for each of the unambiguously-traversed attributes  1849          * remaining attributes needing to be tested and traversed  1850          * details of the context  1851          * any test to apply to the context  1852          * the method of obtaining the final attribute  1853          * any static final attribute  1854          * the kinds of objects providing the final attribute  1855         """  1856   1857         const_access = self.const_accesses_rev.get(location)  1858   1859         path, name, attrnames, version = location  1860         remaining = attrnames.split(".")  1861         attrname = remaining[0]  1862   1863         # Obtain reference and accessor information, retaining also distinct  1864         # provider kind details.  1865   1866         attrs = []  1867         objtypes = []  1868         provider_kinds = set()  1869   1870         for attrtype, objtype, attr in self.referenced_attrs[location]:  1871             attrs.append(attr)  1872             objtypes.append(objtype)  1873             provider_kinds.add(attrtype)  1874   1875         # Obtain accessor type and kind information.  1876   1877         accessor_types = self.reference_all_accessor_types[location]  1878         accessor_general_types = self.reference_all_accessor_general_types[location]  1879         accessor_kinds = get_kinds(accessor_general_types)  1880   1881         # Determine any guard or test requirements.  1882   1883         constrained = location in self.access_constrained  1884         test = self.reference_test_types[location]  1885         test_type = self.reference_test_accessor_type.get(location)  1886   1887         # Determine the accessor and provider properties.  1888   1889         class_accessor = "<class>" in accessor_kinds  1890         module_accessor = "<module>" in accessor_kinds  1891         instance_accessor = "<instance>" in accessor_kinds  1892         provided_by_class = "<class>" in provider_kinds  1893         provided_by_instance = "<instance>" in provider_kinds  1894   1895         # Determine how attributes may be accessed relative to the accessor.  1896   1897         object_relative = class_accessor or module_accessor or provided_by_instance  1898         class_relative = instance_accessor and provided_by_class  1899   1900         # Identify the last static attribute for context acquisition.  1901   1902         base = None  1903         dynamic_base = None  1904   1905         # Constant accesses have static accessors.  1906   1907         if const_access:  1908             base = len(objtypes) == 1 and first(objtypes)  1909   1910         # Name-based accesses.  1911   1912         elif name:  1913             ref = self.importer.identify("%s.%s" % (path, name))  1914   1915             # Constant accessors are static.  1916   1917             if ref and ref.static():  1918                 base = ref.get_origin()  1919   1920             # Usage of previously-generated guard and test details.  1921   1922             elif test[:2] == ("constrained", "specific"):  1923                 ref = first(accessor_types)  1924   1925             elif test[:2] == ("constrained", "common"):  1926                 ref = first(accessor_general_types)  1927   1928             elif test[:2] == ("guarded", "specific"):  1929                 ref = first(accessor_types)  1930   1931             elif test[:2] == ("guarded", "common"):  1932                 ref = first(accessor_general_types)  1933   1934             # For attribute-based tests, tentatively identify a dynamic base.  1935             # Such tests allow single or multiple kinds of a type.  1936   1937             elif test[0] == "test" and test[1] in ("common", "specific"):  1938                 dynamic_base = test_type  1939   1940             # Static accessors.  1941   1942             if not base and test in self.class_tests:  1943                 base = ref and ref.get_origin() or dynamic_base  1944   1945             # Accessors that are not static but whose nature is determined.  1946   1947             elif not base and ref:  1948                 dynamic_base = ref.get_origin()  1949   1950         # Determine initial accessor details.  1951   1952         accessor = base or dynamic_base  1953         accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None  1954         provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None  1955   1956         # Traverse remaining attributes.  1957   1958         traversed = []  1959         traversal_modes = []  1960   1961         while len(attrs) == 1 and not first(attrs).has_kind("<var>"):  1962             attr = first(attrs)  1963   1964             traversed.append(attrname)  1965             traversal_modes.append(accessor_kind == provider_kind and "object" or "class")  1966   1967             # Consume attribute names providing unambiguous attributes.  1968   1969             del remaining[0]  1970   1971             if not remaining:  1972                 break  1973   1974             # Update the last static attribute.  1975   1976             if attr.static():  1977                 base = attr.get_origin()  1978                 traversed = []  1979                 traversal_modes = []  1980   1981             # Get the access details.  1982   1983             attrname = remaining[0]  1984             accessor = attr.get_origin()  1985             accessor_kind = attr.get_kind()  1986             provider_kind = self.importer.get_attribute_provider(attr, attrname)  1987             accessor_kinds = [accessor_kind]  1988             provider_kinds = [provider_kind]  1989   1990             # Get the next attribute.  1991   1992             attrs = self.importer.get_attributes(attr, attrname)  1993   1994         # Where many attributes are suggested, no single attribute identity can  1995         # be loaded.  1996   1997         else:  1998             attr = None  1999   2000         # Determine the method of access.  2001   2002         is_assignment = location in self.reference_assignments or const_access in self.reference_assignments  2003         is_invocation = location in self.reference_invocations or const_access in self.reference_invocations  2004   2005         # Identified attribute that must be accessed via its parent.  2006   2007         if attr and attr.get_name() and is_assignment:  2008             final_method = "static-assign"; origin = attr.get_name()  2009   2010         # Static, identified attribute.  2011   2012         elif attr and attr.static():  2013             final_method = is_assignment and "static-assign" or \  2014                            is_invocation and "static-invoke" or \  2015                            "static"  2016             origin = attr.final()  2017   2018         # All other methods of access involve traversal.  2019   2020         else:  2021             final_method = is_assignment and "assign" or "access"  2022             origin = None  2023   2024         # First attribute accessed at a known position via the accessor.  2025   2026         # Static bases support object-relative accesses only.  2027   2028         if base:  2029             first_method = "relative-object"  2030   2031         # Dynamic bases support either object- or class-relative accesses.  2032   2033         elif dynamic_base:  2034             first_method = "relative" + (object_relative and "-object" or "") + \  2035                                         (class_relative and "-class" or "")  2036   2037         # The fallback case is always run-time testing and access.  2038   2039         else:  2040             first_method = "check" + (object_relative and "-object" or "") + \  2041                                      (class_relative and "-class" or "")  2042   2043         # Determine whether an unbound method is being accessed via an instance,  2044         # requiring a context test.  2045   2046         context_test = "ignore"  2047   2048         # Assignments do not employ the context.  2049   2050         if is_assignment:  2051             pass  2052   2053         # Obtain a selection of possible attributes if no unambiguous attribute  2054         # was identified.  2055   2056         elif not attr:  2057   2058             # Use previously-deduced attributes for a simple ambiguous access.  2059             # Otherwise, use the final attribute name to obtain possible  2060             # attributes.  2061   2062             if len(remaining) > 1:  2063                 attrname = remaining[-1]  2064   2065                 (class_types,  2066                  only_instance_types,  2067                  module_types) = self.get_types_for_attribute(attrname)  2068   2069                 accessor_kinds = set()  2070                 provider_kinds = set()  2071   2072                 if class_types:  2073                     accessor_kinds.add("<class>")  2074                     accessor_kinds.add("<instance>")  2075                     provider_kinds.add("<class>")  2076                 if only_instance_types:  2077                     accessor_kinds.add("<instance>")  2078                     provider_kinds.add("<instance>")  2079                 if module_types:  2080                     accessor_kinds.add("<module>")  2081                     provider_kinds.add("<module>")  2082   2083                 attrs = set()  2084                 for type in combine_types(class_types, only_instance_types, module_types):  2085                     attrs.update(self.importer.get_attributes(type, attrname))  2086   2087             always_unbound = True  2088             have_function = False  2089             have_var = False  2090   2091             # Determine whether all attributes are unbound methods and whether  2092             # functions or unidentified attributes occur.  2093   2094             for attr in attrs:  2095                 always_unbound = always_unbound and attr.has_kind("<function>") and attr.name_parent() == attr.parent()  2096                 have_function = have_function or attr.has_kind("<function>")  2097                 have_var = have_var or attr.has_kind("<var>")  2098   2099             # Test for class-via-instance accesses.  2100   2101             if accessor_kind == "<instance>" and \  2102                provider_kind == "<class>":  2103   2104                 if always_unbound:  2105                     context_test = "replace"  2106                 else:  2107                     context_test = "test"  2108   2109             # Test for the presence of class-via-instance accesses.  2110   2111             elif "<instance>" in accessor_kinds and \  2112                  "<class>" in provider_kinds and \  2113                  (have_function or have_var):  2114   2115                 context_test = "test"  2116   2117         # With an unambiguous attribute, determine whether a test is needed.  2118   2119         elif accessor_kind == "<instance>" and \  2120              provider_kind == "<class>" and \  2121              (attr.has_kind("<var>") or  2122               attr.has_kind("<function>") and  2123               attr.name_parent() == attr.parent()):  2124   2125             if attr.has_kind("<var>"):  2126                 context_test = "test"  2127             else:  2128                 context_test = "replace"  2129   2130         # With an unambiguous attribute with ambiguity in the access method,  2131         # generate a test.  2132   2133         elif "<instance>" in accessor_kinds and \  2134              "<class>" in provider_kinds and \  2135              (attr.has_kind("<var>") or  2136               attr.has_kind("<function>") and  2137               attr.name_parent() == attr.parent()):  2138   2139             context_test = "test"  2140   2141         # Determine the nature of the context.  2142   2143         context = context_test == "ignore" and "unset" or \  2144                   len(traversed + remaining) == 1 and \  2145                       (base and "base" or "original-accessor") or \  2146                   "final-accessor"  2147   2148         return name, test, test_type, base, \  2149                traversed, traversal_modes, remaining, \  2150                context, context_test, \  2151                first_method, final_method, \  2152                origin, accessor_kinds  2153   2154 # vim: tabstop=4 expandtab shiftwidth=4