Lichen

deducer.py

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