Lichen

deducer.py

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