Lichen

deducer.py

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