Lichen

deducer.py

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