Lichen

deducer.py

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