Lichen

deducer.py

657:196988ae83cb
2017-03-05 Paul Boddie Targets should not need unwrapping again when obtaining functions.
     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 name_path, all_aliases in self.importer.all_aliased_names.items():   947             path, name = name_path.rsplit(".", 1)   948    949             # For each version of the name, obtain the access location.   950    951             for version, (original_path, original_name, attrnames, access_number) in all_aliases.items():   952                 accessor_location = (path, name, None, version)   953                 access_location = (original_path, original_name, attrnames, access_number)   954                 init_item(self.alias_index, accessor_location, list)   955                 self.alias_index[accessor_location].append(access_location)   956    957         # Get aliases in terms of non-aliases and accesses.   958    959         for accessor_location, access_locations in self.alias_index.items():   960             self.update_aliases(accessor_location, access_locations)   961    962     def update_aliases(self, accessor_location, access_locations, visited=None):   963    964         """   965         Update the given 'accessor_location' defining an alias, update   966         'access_locations' to refer to non-aliases, following name references   967         via the access index.   968    969         If 'visited' is specified, it contains a set of accessor locations (and   970         thus keys to the alias index) that are currently being defined.   971         """   972    973         if visited is None:   974             visited = set()   975    976         updated_locations = set()   977    978         for access_location in access_locations:   979             (path, original_name, attrnames, access_number) = access_location   980    981             # Where an alias refers to a name access, obtain the original name   982             # version details.   983    984             if attrnames is None:   985    986                 # For each name version, attempt to determine any accesses that   987                 # initialise the name.   988    989                 for name_accessor_location in self.access_index[access_location]:   990    991                     # Already-visited aliases do not contribute details.   992    993                     if name_accessor_location in visited:   994                         continue   995    996                     visited.add(name_accessor_location)   997    998                     name_access_locations = self.alias_index.get(name_accessor_location)   999                     if name_access_locations:  1000                         updated_locations.update(self.update_aliases(name_accessor_location, name_access_locations, visited))  1001                     else:  1002                         updated_locations.add(name_accessor_location)  1003   1004             # Otherwise, record the access details.  1005   1006             else:  1007                 updated_locations.add(access_location)  1008   1009         self.alias_index[accessor_location] = updated_locations  1010         return updated_locations  1011   1012     # Attribute mutation for types.  1013   1014     def modify_mutated_attributes(self):  1015   1016         "Identify known, mutated attributes and change their state."  1017   1018         # Usage-based accesses.  1019   1020         for usage, all_attrnames in self.assigned_attrs.items():  1021             if not usage:  1022                 continue  1023   1024             for path, name, attrname in all_attrnames:  1025                 class_types = self.get_class_types_for_usage(usage)  1026                 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)  1027                 module_types = self.get_module_types_for_usage(usage)  1028   1029                 # Detect self usage within methods in order to narrow the scope  1030                 # of the mutation.  1031   1032                 t = name == "self" and self.constrain_self_reference(path, class_types, only_instance_types)  1033                 if t:  1034                     class_types, only_instance_types, module_types, constrained = t  1035                 objects = set(class_types).union(only_instance_types).union(module_types)  1036   1037                 self.mutate_attribute(objects, attrname)  1038   1039     def mutate_attribute(self, objects, attrname):  1040   1041         "Mutate static 'objects' with the given 'attrname'."  1042   1043         for name in objects:  1044             attr = "%s.%s" % (name, attrname)  1045             value = self.importer.get_object(attr)  1046   1047             # If the value is None, the attribute is  1048             # inherited and need not be set explicitly on  1049             # the class concerned.  1050   1051             if value:  1052                 self.modified_attributes[attr] = value  1053                 self.importer.set_object(attr, value.as_var())  1054   1055     # Simplification of types.  1056   1057     def get_most_general_types(self, types):  1058   1059         "Return the most general types for the given 'types'."  1060   1061         module_types = set()  1062         class_types = set()  1063   1064         for type in types:  1065             ref = self.importer.identify(type)  1066             if ref.has_kind("<module>"):  1067                 module_types.add(type)  1068             else:  1069                 class_types.add(type)  1070   1071         types = set(self.get_most_general_module_types(module_types))  1072         types.update(self.get_most_general_class_types(class_types))  1073         return types  1074   1075     def get_most_general_class_types(self, class_types):  1076   1077         "Return the most general types for the given 'class_types'."  1078   1079         class_types = set(class_types)  1080         to_remove = set()  1081   1082         for class_type in class_types:  1083             for base in self.importer.classes[class_type]:  1084                 base = base.get_origin()  1085                 descendants = self.descendants[base]  1086                 if base in class_types and descendants.issubset(class_types):  1087                     to_remove.update(descendants)  1088   1089         class_types.difference_update(to_remove)  1090         return class_types  1091   1092     def get_most_general_module_types(self, module_types):  1093   1094         "Return the most general type for the given 'module_types'."  1095   1096         # Where all modules are provided, an object would provide the same  1097         # attributes.  1098   1099         if len(module_types) == len(self.importer.modules):  1100             return [self.root_class_type]  1101         else:  1102             return module_types  1103   1104     # More efficient usage-to-type indexing and retrieval.  1105   1106     def init_attr_type_indexes(self):  1107   1108         "Identify the types that can support each attribute name."  1109   1110         self._init_attr_type_index(self.attr_class_types, self.importer.all_class_attrs)  1111         self._init_attr_type_index(self.attr_instance_types, self.importer.all_instance_attrs, True)  1112         self._init_attr_type_index(self.attr_instance_types, self.importer.all_combined_attrs, False)  1113         self._init_attr_type_index(self.attr_module_types, self.importer.all_module_attrs)  1114   1115     def _init_attr_type_index(self, attr_types, attrs, assignment=None):  1116   1117         """  1118         Initialise the 'attr_types' attribute-to-types mapping using the given  1119         'attrs' type-to-attributes mapping.  1120         """  1121   1122         for name, attrnames in attrs.items():  1123             for attrname in attrnames:  1124   1125                 # Permit general access for certain kinds of object.  1126   1127                 if assignment is None:  1128                     init_item(attr_types, (attrname, False), set)  1129                     init_item(attr_types, (attrname, True), set)  1130                     attr_types[(attrname, False)].add(name)  1131                     attr_types[(attrname, True)].add(name)  1132   1133                 # Restrict attribute assignment for instances.  1134   1135                 else:  1136                     init_item(attr_types, (attrname, assignment), set)  1137                     attr_types[(attrname, assignment)].add(name)  1138   1139     def get_class_types_for_usage(self, usage):  1140   1141         "Return names of classes supporting the given 'usage'."  1142   1143         return self._get_types_for_usage(usage, self.attr_class_types, self.importer.all_class_attrs)  1144   1145     def get_instance_types_for_usage(self, usage):  1146   1147         """  1148         Return names of classes whose instances support the given 'usage'  1149         (as either class or instance attributes).  1150         """  1151   1152         return self._get_types_for_usage(usage, self.attr_instance_types, self.importer.all_combined_attrs)  1153   1154     def get_module_types_for_usage(self, usage):  1155   1156         "Return names of modules supporting the given 'usage'."  1157   1158         return self._get_types_for_usage(usage, self.attr_module_types, self.importer.all_module_attrs)  1159   1160     def _get_types_for_usage(self, usage, attr_types, attrs):  1161   1162         """  1163         For the given 'usage' representing attribute usage, return types  1164         recorded in the 'attr_types' attribute-to-types mapping that support  1165         such usage, with the given 'attrs' type-to-attributes mapping used to  1166         quickly assess whether a type supports all of the stated attributes.  1167         """  1168   1169         # Where no attributes are used, any type would be acceptable.  1170   1171         if not usage:  1172             return attrs.keys()  1173   1174         keys = []  1175         for attrname, invocation, assignment in usage:  1176             keys.append((attrname, assignment))  1177   1178         # Obtain types supporting the first (attribute name, assignment) key...  1179   1180         types = set(attr_types.get(keys[0]) or [])  1181   1182         for key in keys[1:]:  1183               1184             # Record types that support all of the other attributes as well.  1185   1186             types.intersection_update(attr_types.get(key) or [])  1187   1188         return types  1189   1190     def init_combined_attribute_index(self):  1191   1192         "Initialise a combined index for the detection of invalid attributes."  1193   1194         self.all_attrnames = set()  1195         for attrs in (self.importer.all_combined_attrs, self.importer.all_module_attrs):  1196             for name, attrnames in attrs.items():  1197                 self.all_attrnames.update(attrnames)  1198   1199     # Reference identification.  1200   1201     def identify_references(self):  1202   1203         "Identify references using usage and name reference information."  1204   1205         # Names with associated attribute usage.  1206   1207         for location, usages in self.location_index.items():  1208   1209             # Obtain attribute usage associated with a name, deducing the nature  1210             # of the name. Obtain types only for branches involving attribute  1211             # usage. (In the absence of usage, any type could be involved, but  1212             # then no accesses exist to require knowledge of the type.)  1213   1214             have_usage = False  1215             have_no_usage_branch = False  1216   1217             for usage in usages:  1218                 if not usage:  1219                     have_no_usage_branch = True  1220                     continue  1221                 elif not have_usage:  1222                     self.init_definition_details(location)  1223                     have_usage = True  1224                 self.record_types_for_usage(location, usage)  1225   1226             # Where some usage occurs, but where branches without usage also  1227             # occur, record the types for those branches anyway.  1228   1229             if have_usage and have_no_usage_branch:  1230                 self.init_definition_details(location)  1231                 self.record_types_for_usage(location, None)  1232   1233         # Specific name-based attribute accesses.  1234   1235         alias_accesses = set()  1236   1237         for access_location, accessor_locations in self.access_index.items():  1238             self.record_types_for_access(access_location, accessor_locations, alias_accesses)  1239   1240         # Anonymous references with attribute chains.  1241   1242         for location, accesses in self.importer.all_attr_accesses.items():  1243   1244             # Get distinct attribute names.  1245   1246             all_attrnames = set()  1247   1248             for attrnames in accesses:  1249                 all_attrnames.update(get_attrnames(attrnames))  1250   1251             # Get attribute and accessor details for each attribute name.  1252   1253             for attrname in all_attrnames:  1254                 access_location = (location, None, attrname, 0)  1255                 self.record_types_for_attribute(access_location, attrname)  1256   1257         # References via constant/identified objects.  1258   1259         for location, name_accesses in self.importer.all_const_accesses.items():  1260   1261             # A mapping from the original name and attributes to resolved access  1262             # details.  1263   1264             for original_access, access in name_accesses.items():  1265                 original_name, original_attrnames = original_access  1266                 objpath, ref, attrnames = access  1267   1268                 # Build an accessor combining the name and attribute names used.  1269   1270                 original_accessor = tuple([original_name] + original_attrnames.split("."))  1271   1272                 # Direct accesses to attributes.  1273   1274                 if not attrnames:  1275   1276                     # Build a descriptive location based on the original  1277                     # details, exposing the final attribute name.  1278   1279                     oa, attrname = original_accessor[:-1], original_accessor[-1]  1280                     oa = ".".join(oa)  1281   1282                     access_location = (location, oa, attrname, 0)  1283                     accessor_location = (location, oa, None, 0)  1284                     self.access_index[access_location] = [accessor_location]  1285   1286                     self.init_access_details(access_location)  1287                     self.init_definition_details(accessor_location)  1288   1289                     # Obtain a reference for the accessor in order to properly  1290                     # determine its type.  1291   1292                     if ref.get_kind() != "<instance>":  1293                         objpath = ref.get_origin()  1294   1295                     objpath = objpath.rsplit(".", 1)[0]  1296   1297                     # Where the object name conflicts with the module  1298                     # providing it, obtain the module details.  1299   1300                     if objpath in self.importer.modules:  1301                         accessor = Reference("<module>", objpath)  1302                     else:  1303                         accessor = self.importer.get_object(objpath)  1304   1305                     self.referenced_attrs[access_location] = [(accessor.get_kind(), accessor.get_origin(), ref)]  1306                     self.access_constrained.add(access_location)  1307   1308                     class_types, instance_types, module_types = accessor.get_types()  1309                     self.record_reference_types(accessor_location, class_types, instance_types, module_types, True, True)  1310   1311                 else:  1312   1313                     # Build a descriptive location based on the original  1314                     # details, employing the first remaining attribute name.  1315   1316                     l = get_attrnames(attrnames)  1317                     attrname = l[0]  1318   1319                     oa = original_accessor[:-len(l)]  1320                     oa = ".".join(oa)  1321   1322                     access_location = (location, oa, attrnames, 0)  1323                     accessor_location = (location, oa, None, 0)  1324                     self.access_index[access_location] = [accessor_location]  1325   1326                     self.init_access_details(access_location)  1327                     self.init_definition_details(accessor_location)  1328   1329                     class_types, instance_types, module_types = ref.get_types()  1330   1331                     self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, True)  1332                     self.record_reference_types(accessor_location, class_types, instance_types, module_types, True, True)  1333   1334                 original_location = (location, original_name, original_attrnames, 0)  1335   1336                 if original_location != access_location:  1337                     self.const_accesses[original_location] = access_location  1338                     self.const_accesses_rev[access_location] = original_location  1339   1340         # Aliased name definitions. All aliases with usage will have been  1341         # defined, but they may be refined according to referenced accesses.  1342   1343         for accessor_location in self.alias_index.keys():  1344             self.record_types_for_alias(accessor_location)  1345   1346         # Update accesses employing aliases.  1347   1348         for access_location in alias_accesses:  1349             self.record_types_for_access(access_location, self.access_index[access_location])  1350   1351     def constrain_types(self, path, class_types, instance_types, module_types):  1352   1353         """  1354         Using the given 'path' to an object, constrain the given 'class_types',  1355         'instance_types' and 'module_types'.  1356   1357         Return the class, instance, module types plus whether the types are  1358         constrained to a specific kind of type.  1359         """  1360   1361         ref = self.importer.identify(path)  1362         if ref:  1363   1364             # Constrain usage suggestions using the identified object.  1365   1366             if ref.has_kind("<class>"):  1367                 return (  1368                     set(class_types).intersection([ref.get_origin()]), [], [], True  1369                     )  1370             elif ref.has_kind("<module>"):  1371                 return (  1372                     [], [], set(module_types).intersection([ref.get_origin()]), True  1373                     )  1374   1375         return class_types, instance_types, module_types, False  1376   1377     def get_target_types(self, location, usage):  1378   1379         """  1380         Return the class, instance and module types constrained for the name at  1381         the given 'location' exhibiting the given 'usage'. Whether the types  1382         have been constrained using contextual information is also indicated,  1383         plus whether the types have been constrained to a specific kind of type.  1384         """  1385   1386         unit_path, name, attrnames, version = location  1387         have_assignments = get_assigned_attributes(usage)  1388   1389         # Detect any initialised name for the location.  1390   1391         if name:  1392             ref = self.get_initialised_name(location)  1393             if ref:  1394                 (class_types, only_instance_types, module_types,  1395                     _function_types, _var_types) = separate_types([ref])  1396                 return class_types, only_instance_types, module_types, True, have_assignments  1397   1398         # Retrieve the recorded types for the usage.  1399   1400         class_types = self.get_class_types_for_usage(usage)  1401         only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)  1402         module_types = self.get_module_types_for_usage(usage)  1403   1404         # Merge usage deductions with observations to obtain reference types  1405         # for names involved with attribute accesses.  1406   1407         if not name:  1408             return class_types, only_instance_types, module_types, False, have_assignments  1409   1410         # Obtain references to known objects.  1411   1412         path = get_name_path(unit_path, name)  1413   1414         class_types, only_instance_types, module_types, constrained_specific = \  1415             self.constrain_types(path, class_types, only_instance_types, module_types)  1416   1417         if constrained_specific:  1418             return class_types, only_instance_types, module_types, constrained_specific, \  1419                 constrained_specific or have_assignments  1420   1421         # Constrain "self" references.  1422   1423         if name == "self":  1424   1425             # Test for the class of the method in the deduced types.  1426   1427             class_name = self.in_method(unit_path)  1428   1429             if class_name and class_name not in class_types and class_name not in only_instance_types:  1430                 raise DeduceError("In %s, usage {%s} is not directly supported by class %s or its instances." %  1431                                   (unit_path, encode_usage(usage), class_name))  1432   1433             # Constrain the types to the class's hierarchy.  1434   1435             t = self.constrain_self_reference(unit_path, class_types, only_instance_types)  1436             if t:  1437                 class_types, only_instance_types, module_types, constrained = t  1438                 return class_types, only_instance_types, module_types, constrained, have_assignments  1439   1440         return class_types, only_instance_types, module_types, False, have_assignments  1441   1442     def constrain_self_reference(self, unit_path, class_types, only_instance_types):  1443   1444         """  1445         Where the name "self" appears in a method, attempt to constrain the  1446         classes involved.  1447   1448         Return the class, instance, module types plus whether the types are  1449         constrained.  1450         """  1451   1452         class_name = self.in_method(unit_path)  1453   1454         if not class_name:  1455             return None  1456   1457         classes = set([class_name])  1458         classes.update(self.get_descendants_for_class(class_name))  1459   1460         # Note that only instances will be expected for these references but  1461         # either classes or instances may provide the attributes.  1462   1463         return (  1464             set(class_types).intersection(classes),  1465             set(only_instance_types).intersection(classes),  1466             [], True  1467             )  1468   1469     def in_method(self, path):  1470   1471         "Return whether 'path' refers to a method."  1472   1473         class_name, method_name = path.rsplit(".", 1)  1474         return class_name != "__builtins__.core.type" and self.importer.classes.has_key(class_name) and class_name  1475   1476     def init_reference_details(self, location):  1477   1478         "Initialise reference-related details for 'location'."  1479   1480         self.init_definition_details(location)  1481         self.init_access_details(location)  1482   1483     def init_definition_details(self, location):  1484   1485         "Initialise name definition details for 'location'."  1486   1487         self.accessor_class_types[location] = set()  1488         self.accessor_instance_types[location] = set()  1489         self.accessor_module_types[location] = set()  1490         self.provider_class_types[location] = set()  1491         self.provider_instance_types[location] = set()  1492         self.provider_module_types[location] = set()  1493   1494     def init_access_details(self, location):  1495   1496         "Initialise access details at 'location'."  1497   1498         self.referenced_attrs[location] = {}  1499   1500     def record_types_for_access(self, access_location, accessor_locations, alias_accesses=None):  1501   1502         """  1503         Define types for the 'access_location' associated with the given  1504         'accessor_locations'.  1505         """  1506   1507         attrname = get_attrname_from_location(access_location)  1508         if not attrname:  1509             return  1510   1511         # Collect all suggested types for the accessors. Accesses may  1512         # require accessors from of a subset of the complete set of types.  1513   1514         class_types = set()  1515         module_types = set()  1516         instance_types = set()  1517   1518         constrained = True  1519   1520         for location in accessor_locations:  1521   1522             # Remember accesses employing aliases.  1523   1524             if alias_accesses is not None and self.alias_index.has_key(location):  1525                 alias_accesses.add(access_location)  1526   1527             # Use the type information deduced for names from above.  1528   1529             if self.accessor_class_types.has_key(location):  1530                 class_types.update(self.accessor_class_types[location])  1531                 module_types.update(self.accessor_module_types[location])  1532                 instance_types.update(self.accessor_instance_types[location])  1533   1534             # Where accesses are associated with assignments but where no  1535             # attribute usage observations have caused such an association,  1536             # the attribute name is considered by itself.  1537   1538             else:  1539                 self.init_definition_details(location)  1540                 self.record_types_for_usage(location, [(attrname, False, False)])  1541   1542             constrained = location in self.accessor_constrained and constrained  1543   1544         self.init_access_details(access_location)  1545         self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained)  1546   1547     def record_types_for_usage(self, accessor_location, usage):  1548   1549         """  1550         Record types for the given 'accessor_location' according to the given  1551         'usage' observations which may be None to indicate an absence of usage.  1552         """  1553   1554         (class_types,  1555          instance_types,  1556          module_types,  1557          constrained,  1558          constrained_specific) = self.get_target_types(accessor_location, usage)  1559   1560         invocations = get_invoked_attributes(usage)  1561   1562         self.record_reference_types(accessor_location, class_types, instance_types,  1563             module_types, constrained, constrained_specific, invocations)  1564   1565     def record_types_for_attribute(self, access_location, attrname):  1566   1567         """  1568         Record types for the 'access_location' employing only the given  1569         'attrname' for type deduction.  1570         """  1571   1572         (class_types,  1573          only_instance_types,  1574          module_types) = self.get_types_for_attribute(attrname)  1575   1576         self.init_reference_details(access_location)  1577   1578         self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False)  1579         self.record_reference_types(access_location, class_types, only_instance_types, module_types, False)  1580   1581     def get_types_for_attribute(self, attrname):  1582   1583         "Return class, instance-only and module types supporting 'attrname'."  1584   1585         usage = ((attrname, False, False),)  1586   1587         class_types = self.get_class_types_for_usage(usage)  1588         only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)  1589         module_types = self.get_module_types_for_usage(usage)  1590   1591         return class_types, only_instance_types, module_types  1592   1593     def record_types_for_alias(self, accessor_location):  1594   1595         """  1596         Define types for the 'accessor_location' not having associated usage.  1597         """  1598   1599         have_access = self.provider_class_types.has_key(accessor_location)  1600   1601         # With an access, attempt to narrow the existing selection of provider  1602         # types.  1603   1604         if have_access:  1605             provider_class_types = self.provider_class_types[accessor_location]  1606             provider_instance_types = self.provider_instance_types[accessor_location]  1607             provider_module_types = self.provider_module_types[accessor_location]  1608   1609             # Find details for any corresponding access.  1610   1611             all_class_types = set()  1612             all_instance_types = set()  1613             all_module_types = set()  1614   1615             for access_location in self.alias_index[accessor_location]:  1616                 location, name, attrnames, access_number = access_location  1617   1618                 # Alias references an attribute access.  1619   1620                 if attrnames:  1621   1622                     # Obtain attribute references for the access.  1623   1624                     attrs = []  1625                     for _attrtype, object_type, attr in self.referenced_attrs[access_location]:  1626                         attrs.append(attr)  1627   1628                     # Separate the different attribute types.  1629   1630                     (class_types, instance_types, module_types,  1631                         function_types, var_types) = separate_types(attrs)  1632   1633                     # Where non-accessor types are found, do not attempt to refine  1634                     # the defined accessor types.  1635   1636                     if function_types or var_types:  1637                         return  1638   1639                     class_types = set(provider_class_types).intersection(class_types)  1640                     instance_types = set(provider_instance_types).intersection(instance_types)  1641                     module_types = set(provider_module_types).intersection(module_types)  1642   1643                 # Alias references a name, not an access.  1644   1645                 else:  1646                     # Attempt to refine the types using initialised names.  1647   1648                     attr = self.get_initialised_name(access_location)  1649                     if attr:  1650                         (class_types, instance_types, module_types,  1651                             _function_types, _var_types) = separate_types([attr])  1652   1653                     # Where no further information is found, do not attempt to  1654                     # refine the defined accessor types.  1655   1656                     else:  1657                         return  1658   1659                 all_class_types.update(class_types)  1660                 all_instance_types.update(instance_types)  1661                 all_module_types.update(module_types)  1662   1663             # Record refined type details for the alias as an accessor.  1664   1665             self.init_definition_details(accessor_location)  1666             self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False)  1667   1668         # Without an access, attempt to identify references for the alias.  1669   1670         else:  1671             refs = set()  1672   1673             for access_location in self.alias_index[accessor_location]:  1674   1675                 # Obtain any redefined constant access location.  1676   1677                 if self.const_accesses.has_key(access_location):  1678                     access_location = self.const_accesses[access_location]  1679   1680                 location, name, attrnames, access_number = access_location  1681   1682                 # Alias references an attribute access.  1683   1684                 if attrnames:  1685                     attrs = []  1686                     for attrtype, object_type, attr in self.referenced_attrs[access_location]:  1687                         attrs.append(attr)  1688                     refs.update(attrs)  1689   1690                 # Alias references a name, not an access.  1691   1692                 else:  1693                     attr = self.get_initialised_name(access_location)  1694                     attrs = attr and [attr] or []  1695                     if not attrs and self.provider_class_types.has_key(access_location):  1696                         class_types = self.provider_class_types[access_location]  1697                         instance_types = self.provider_instance_types[access_location]  1698                         module_types = self.provider_module_types[access_location]  1699                         attrs = combine_types(class_types, instance_types, module_types)  1700                     if attrs:  1701                         refs.update(attrs)  1702   1703             # Record reference details for the alias separately from accessors.  1704   1705             self.referenced_objects[accessor_location] = refs  1706   1707     def get_initialised_name(self, access_location):  1708   1709         """  1710         Return references for any initialised names at 'access_location', or  1711         None if no such references exist.  1712         """  1713   1714         location, name, attrnames, version = access_location  1715         path = get_name_path(location, name)  1716   1717         # Use initialiser information, if available.  1718   1719         refs = self.importer.all_initialised_names.get(path)  1720         if refs and refs.has_key(version):  1721             return refs[version]  1722         else:  1723             return None  1724   1725     def record_reference_types(self, location, class_types, instance_types,  1726         module_types, constrained, constrained_specific=False, invocations=None):  1727   1728         """  1729         Associate attribute provider types with the given 'location', consisting  1730         of the given 'class_types', 'instance_types' and 'module_types'.  1731   1732         If 'constrained' is indicated, the constrained nature of the accessor is  1733         recorded for the location.  1734   1735         If 'constrained_specific' is indicated using a true value, instance types  1736         will not be added to class types to permit access via instances at the  1737         given location. This is only useful where a specific accessor is known  1738         to be a class.  1739   1740         If 'invocations' is given, the given attribute names indicate those  1741         which are involved in invocations. Such invocations, if involving  1742         functions, will employ those functions as bound methods and will  1743         therefore not support classes as accessors, only instances of such  1744         classes.  1745   1746         Note that the specified types only indicate the provider types for  1747         attributes, whereas the recorded accessor types indicate the possible  1748         types of the actual objects used to access attributes.  1749         """  1750   1751         # Update the type details for the location.  1752   1753         self.provider_class_types[location].update(class_types)  1754         self.provider_instance_types[location].update(instance_types)  1755         self.provider_module_types[location].update(module_types)  1756   1757         # Class types support classes and instances as accessors.  1758         # Instance-only and module types support only their own kinds as  1759         # accessors.  1760   1761         path, name, version, attrnames = location  1762   1763         if invocations:  1764             class_only_types = self.filter_for_invocations(class_types, invocations)  1765         else:  1766             class_only_types = class_types  1767   1768         # However, the nature of accessors can be further determined.  1769         # Any self variable may only refer to an instance.  1770   1771         if name != "self" or not self.in_method(path):  1772             self.accessor_class_types[location].update(class_only_types)  1773   1774         if not constrained_specific:  1775             self.accessor_instance_types[location].update(class_types)  1776   1777         self.accessor_instance_types[location].update(instance_types)  1778   1779         if name != "self" or not self.in_method(path):  1780             self.accessor_module_types[location].update(module_types)  1781   1782         if constrained:  1783             self.accessor_constrained.add(location)  1784   1785     def filter_for_invocations(self, class_types, attrnames):  1786   1787         """  1788         From the given 'class_types', identify methods for the given  1789         'attrnames' that are being invoked, returning a filtered collection of  1790         class types.  1791   1792         This method may be used to remove class types from consideration where  1793         their attributes are methods that are directly invoked: method  1794         invocations must involve instance accessors.  1795         """  1796   1797         to_filter = set()  1798   1799         for class_type in class_types:  1800             for attrname in attrnames:  1801   1802                 # Attempt to obtain a class attribute of the given name. This  1803                 # may return an attribute provided by an ancestor class.  1804   1805                 ref = self.importer.get_class_attribute(class_type, attrname)  1806                 parent_class = ref and ref.parent()  1807   1808                 # If such an attribute is a method and would be available on  1809                 # the given class, record the class for filtering.  1810   1811                 if ref and ref.has_kind("<function>") and (  1812                    parent_class == class_type or  1813                    class_type in self.descendants[parent_class]):  1814   1815                     to_filter.add(class_type)  1816                     break  1817   1818         return set(class_types).difference(to_filter)  1819   1820     def identify_reference_attributes(self, location, attrname, class_types, instance_types, module_types, constrained):  1821   1822         """  1823         Identify reference attributes, associating them with the given  1824         'location', identifying the given 'attrname', employing the given  1825         'class_types', 'instance_types' and 'module_types'.  1826   1827         If 'constrained' is indicated, the constrained nature of the access is  1828         recorded for the location.  1829         """  1830   1831         # Record the referenced objects.  1832   1833         self.referenced_attrs[location] = \  1834             self._identify_reference_attribute(location, attrname, class_types, instance_types, module_types)  1835   1836         if constrained:  1837             self.access_constrained.add(location)  1838   1839     def _identify_reference_attribute(self, location, attrname, class_types, instance_types, module_types):  1840   1841         """  1842         Identify the reference attribute at the given access 'location', using  1843         the given 'attrname', and employing the given 'class_types',  1844         'instance_types' and 'module_types'.  1845         """  1846   1847         attrs = set()  1848   1849         # The class types expose class attributes either directly or via  1850         # instances.  1851   1852         for object_type in class_types:  1853             ref = self.importer.get_class_attribute(object_type, attrname)  1854             if ref and self.is_compatible_callable(location, object_type, ref):  1855                 attrs.add(("<class>", object_type, ref))  1856   1857             # Add any distinct instance attributes that would be provided  1858             # by instances also providing indirect class attribute access.  1859   1860             for ref in self.importer.get_instance_attributes(object_type, attrname):  1861                 if self.is_compatible_callable(location, object_type, ref):  1862                     attrs.add(("<instance>", object_type, ref))  1863   1864         # The instance-only types expose instance attributes, but although  1865         # classes are excluded as potential accessors (since they do not provide  1866         # the instance attributes), the class types may still provide some  1867         # attributes.  1868   1869         for object_type in instance_types:  1870             instance_attrs = self.importer.get_instance_attributes(object_type, attrname)  1871   1872             if instance_attrs:  1873                 for ref in instance_attrs:  1874                     if self.is_compatible_callable(location, object_type, ref):  1875                         attrs.add(("<instance>", object_type, ref))  1876             else:  1877                 ref = self.importer.get_class_attribute(object_type, attrname)  1878                 if ref and self.is_compatible_callable(location, object_type, ref):  1879                     attrs.add(("<class>", object_type, ref))  1880   1881         # Module types expose module attributes for module accessors.  1882   1883         for object_type in module_types:  1884             ref = self.importer.get_module_attribute(object_type, attrname)  1885             if ref and self.is_compatible_callable(location, object_type, ref):  1886                 attrs.add(("<module>", object_type, ref))  1887   1888         return attrs  1889   1890     def is_compatible_callable(self, location, object_type, ref):  1891   1892         """  1893         Return whether any invocation at 'location' involving an attribute of  1894         'object_type' identified by 'ref' is compatible with any arguments used.  1895         """  1896   1897         invocation = self.reference_invocations.get(location)  1898         if invocation is None:  1899             return True  1900   1901         objpath = ref.get_origin()  1902         if not objpath:  1903             return True  1904   1905         parameters = self.importer.function_parameters.get(objpath)  1906         if not parameters:  1907             return True  1908   1909         defaults = self.importer.function_defaults.get(objpath)  1910         arguments, keywords = invocation  1911         names = set(parameters)  1912   1913         # Determine whether the specified arguments are  1914         # compatible with the callable signature.  1915   1916         if arguments >= len(parameters) - len(defaults) and \  1917            arguments <= len(parameters) and \  1918            names.issuperset(keywords):  1919   1920             return True  1921         else:  1922             init_item(self.reference_invocations_unsuitable, location, set)  1923             self.reference_invocations_unsuitable[location].add(ref)  1924             return False  1925   1926     # Attribute access plan formulation.  1927   1928     class_tests = (  1929         ("guarded", "specific", "type"),  1930         ("guarded", "common", "type"),  1931         ("test", "specific", "type"),  1932         ("test", "common", "type"),  1933         )  1934   1935     def get_access_plan(self, location):  1936   1937         """  1938         Return details of the access at the given 'location'. The details are as  1939         follows:  1940   1941          * the initial accessor (from which accesses will be performed if no  1942            computed static accessor is found)  1943          * details of any test required on the initial accessor  1944          * details of any type employed by the test  1945          * any static accessor (from which accesses will be performed in  1946            preference to the initial accessor)  1947          * attributes needing to be traversed from the base that yield  1948            unambiguous objects  1949          * access modes for each of the unambiguously-traversed attributes  1950          * remaining attributes needing to be tested and traversed  1951          * details of the context  1952          * any test to apply to the context  1953          * the method of obtaining the first attribute  1954          * the method of obtaining the final attribute  1955          * any static final attribute  1956          * the kinds of objects providing the final attribute  1957         """  1958   1959         const_access = self.const_accesses_rev.get(location)  1960   1961         path, name, attrnames, version = location  1962         remaining = attrnames.split(".")  1963         attrname = remaining[0]  1964   1965         # Obtain reference and accessor information, retaining also distinct  1966         # provider kind details.  1967   1968         attrs = []  1969         objtypes = []  1970         provider_kinds = set()  1971   1972         for attrtype, objtype, attr in self.referenced_attrs[location]:  1973             attrs.append(attr)  1974             objtypes.append(objtype)  1975             provider_kinds.add(attrtype)  1976   1977         # Obtain accessor type and kind information.  1978   1979         accessor_types = self.reference_all_accessor_types[location]  1980         accessor_general_types = self.reference_all_accessor_general_types[location]  1981         accessor_kinds = get_kinds(accessor_general_types)  1982   1983         # Determine any guard or test requirements.  1984   1985         constrained = location in self.access_constrained  1986         test = self.reference_test_types[location]  1987         test_type = self.reference_test_accessor_type.get(location)  1988   1989         # Determine the accessor and provider properties.  1990   1991         class_accessor = "<class>" in accessor_kinds  1992         module_accessor = "<module>" in accessor_kinds  1993         instance_accessor = "<instance>" in accessor_kinds  1994         provided_by_class = "<class>" in provider_kinds  1995         provided_by_instance = "<instance>" in provider_kinds  1996   1997         # Determine how attributes may be accessed relative to the accessor.  1998   1999         object_relative = class_accessor or module_accessor or provided_by_instance  2000         class_relative = instance_accessor and provided_by_class  2001   2002         # Identify the last static attribute for context acquisition.  2003   2004         base = None  2005         dynamic_base = None  2006   2007         # Constant accesses have static accessors.  2008   2009         if const_access:  2010             base = len(objtypes) == 1 and first(objtypes)  2011   2012         # Name-based accesses.  2013   2014         elif name:  2015             ref = self.importer.identify("%s.%s" % (path, name))  2016   2017             # Constant accessors are static.  2018   2019             if ref and ref.static():  2020                 base = ref.get_origin()  2021   2022             # Usage of previously-generated guard and test details.  2023   2024             elif test[:2] == ("constrained", "specific"):  2025                 ref = first(accessor_types)  2026   2027             elif test[:2] == ("constrained", "common"):  2028                 ref = first(accessor_general_types)  2029   2030             elif test[:2] == ("guarded", "specific"):  2031                 ref = first(accessor_types)  2032   2033             elif test[:2] == ("guarded", "common"):  2034                 ref = first(accessor_general_types)  2035   2036             # For attribute-based tests, tentatively identify a dynamic base.  2037             # Such tests allow single or multiple kinds of a type.  2038   2039             elif test[0] == "test" and test[1] in ("common", "specific"):  2040                 dynamic_base = test_type  2041   2042             # Static accessors.  2043   2044             if not base and test in self.class_tests:  2045                 base = ref and ref.get_origin() or dynamic_base  2046   2047             # Accessors that are not static but whose nature is determined.  2048   2049             elif not base and ref:  2050                 dynamic_base = ref.get_origin()  2051   2052         # Determine initial accessor details.  2053   2054         accessor = base or dynamic_base  2055         accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None  2056         provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None  2057   2058         # Traverse remaining attributes.  2059   2060         traversed = []  2061         traversal_modes = []  2062   2063         while len(attrs) == 1 and not first(attrs).has_kind("<var>"):  2064             attr = first(attrs)  2065   2066             traversed.append(attrname)  2067             traversal_modes.append(accessor_kind == provider_kind and "object" or "class")  2068   2069             # Consume attribute names providing unambiguous attributes.  2070   2071             del remaining[0]  2072   2073             if not remaining:  2074                 break  2075   2076             # Update the last static attribute.  2077   2078             if attr.static():  2079                 base = attr.get_origin()  2080                 traversed = []  2081                 traversal_modes = []  2082   2083             # Get the access details.  2084   2085             attrname = remaining[0]  2086             accessor = attr.get_origin()  2087             accessor_kind = attr.get_kind()  2088             provider_kind = self.importer.get_attribute_provider(attr, attrname)  2089             accessor_kinds = [accessor_kind]  2090             provider_kinds = [provider_kind]  2091   2092             # Get the next attribute.  2093   2094             attrs = self.importer.get_attributes(attr, attrname)  2095   2096         # Where many attributes are suggested, no single attribute identity can  2097         # be loaded.  2098   2099         else:  2100             attr = None  2101   2102         # Determine the method of access.  2103   2104         is_assignment = location in self.reference_assignments or const_access in self.reference_assignments  2105         is_invocation = location in self.reference_invocations or const_access in self.reference_invocations  2106   2107         # Identified attribute that must be accessed via its parent.  2108   2109         if attr and attr.get_name() and is_assignment:  2110             final_method = "static-assign"; origin = attr.get_name()  2111   2112         # Static, identified attribute.  2113   2114         elif attr and attr.static():  2115             final_method = is_assignment and "static-assign" or \  2116                            is_invocation and "static-invoke" or \  2117                            "static"  2118             origin = attr.final()  2119   2120         # All other methods of access involve traversal.  2121   2122         else:  2123             final_method = is_assignment and "assign" or \  2124                            is_invocation and "access-invoke" or \  2125                            "access"  2126             origin = None  2127   2128         # First attribute accessed at a known position via the accessor.  2129   2130         # Static bases support object-relative accesses only.  2131   2132         if base:  2133             first_method = "relative-object"  2134   2135         # Dynamic bases support either object- or class-relative accesses.  2136   2137         elif dynamic_base:  2138             first_method = "relative" + (object_relative and "-object" or "") + \  2139                                         (class_relative and "-class" or "")  2140   2141         # The fallback case is always run-time testing and access.  2142   2143         else:  2144             first_method = "check" + (object_relative and "-object" or "") + \  2145                                      (class_relative and "-class" or "")  2146   2147         # Determine whether an unbound method is being accessed via an instance,  2148         # requiring a context test.  2149   2150         context_test = "ignore"  2151   2152         # Assignments do not employ the context.  2153   2154         if is_assignment:  2155             pass  2156   2157         # Obtain a selection of possible attributes if no unambiguous attribute  2158         # was identified.  2159   2160         elif not attr:  2161   2162             # Use previously-deduced attributes for a simple ambiguous access.  2163             # Otherwise, use the final attribute name to obtain possible  2164             # attributes.  2165   2166             if len(remaining) > 1:  2167                 attrname = remaining[-1]  2168   2169                 (class_types,  2170                  only_instance_types,  2171                  module_types) = self.get_types_for_attribute(attrname)  2172   2173                 accessor_kinds = set()  2174                 provider_kinds = set()  2175   2176                 if class_types:  2177                     accessor_kinds.add("<class>")  2178                     accessor_kinds.add("<instance>")  2179                     provider_kinds.add("<class>")  2180                 if only_instance_types:  2181                     accessor_kinds.add("<instance>")  2182                     provider_kinds.add("<instance>")  2183                 if module_types:  2184                     accessor_kinds.add("<module>")  2185                     provider_kinds.add("<module>")  2186   2187                 attrs = set()  2188                 for type in combine_types(class_types, only_instance_types, module_types):  2189                     attrs.update(self.importer.get_attributes(type, attrname))  2190   2191             always_unbound = True  2192             have_function = False  2193             have_var = False  2194   2195             # Determine whether all attributes are unbound methods and whether  2196             # functions or unidentified attributes occur.  2197   2198             for attr in attrs:  2199                 always_unbound = always_unbound and attr.has_kind("<function>") and attr.name_parent() == attr.parent()  2200                 have_function = have_function or attr.has_kind("<function>")  2201                 have_var = have_var or attr.has_kind("<var>")  2202   2203             # Test for class-via-instance accesses.  2204   2205             if accessor_kind == "<instance>" and \  2206                provider_kind == "<class>":  2207   2208                 if always_unbound:  2209                     context_test = "replace"  2210                 else:  2211                     context_test = "test"  2212   2213             # Test for the presence of class-via-instance accesses.  2214   2215             elif "<instance>" in accessor_kinds and \  2216                  "<class>" in provider_kinds and \  2217                  (have_function or have_var):  2218   2219                 context_test = "test"  2220   2221         # With an unambiguous attribute, determine whether a test is needed.  2222   2223         elif accessor_kind == "<instance>" and \  2224              provider_kind == "<class>" and \  2225              (attr.has_kind("<var>") or  2226               attr.has_kind("<function>") and  2227               attr.name_parent() == attr.parent()):  2228   2229             if attr.has_kind("<var>"):  2230                 context_test = "test"  2231             else:  2232                 context_test = "replace"  2233   2234         # With an unambiguous attribute with ambiguity in the access method,  2235         # generate a test.  2236   2237         elif "<instance>" in accessor_kinds and \  2238              "<class>" in provider_kinds and \  2239              (attr.has_kind("<var>") or  2240               attr.has_kind("<function>") and  2241               attr.name_parent() == attr.parent()):  2242   2243             context_test = "test"  2244   2245         # Determine the nature of the context.  2246   2247         context = context_test == "ignore" and "unset" or \  2248                   len(traversed + remaining) == 1 and \  2249                       (base and "base" or "original-accessor") or \  2250                   "final-accessor"  2251   2252         return name, test, test_type, base, \  2253                traversed, traversal_modes, remaining, \  2254                context, context_test, \  2255                first_method, final_method, \  2256                origin, accessor_kinds  2257   2258     def initialise_access_instructions(self):  2259   2260         "Expand access plans into instruction sequences."  2261   2262         for access_location, access_plan in self.access_plans.items():  2263   2264             # Obtain the access details.  2265   2266             name, test, test_type, base, \  2267                 traversed, traversal_modes, attrnames, \  2268                 context, context_test, \  2269                 first_method, final_method, \  2270                 origin, accessor_kinds = access_plan  2271   2272             # Emit instructions by appending them to a list.  2273   2274             instructions = []  2275             emit = instructions.append  2276   2277             # Identify any static original accessor.  2278   2279             if base:  2280                 original_accessor = base  2281   2282             # Employ names as contexts unless the context needs testing and  2283             # potentially updating. In such cases, temporary context storage is  2284             # used instead.  2285   2286             elif name and not (context_test == "test" and  2287                                final_method in ("access-invoke", "static-invoke")):  2288                 original_accessor = "<name>" # refers to the name  2289   2290             # Use a generic placeholder representing the access expression in  2291             # the general case.  2292   2293             else:  2294                 original_accessor = "<expr>"  2295   2296             # Prepare for any first attribute access.  2297   2298             if traversed:  2299                 attrname = traversed[0]  2300                 del traversed[0]  2301             elif attrnames:  2302                 attrname = attrnames[0]  2303                 del attrnames[0]  2304   2305             # Perform the first access explicitly if at least one operation  2306             # requires it.  2307   2308             access_first_attribute = final_method in ("access", "access-invoke", "assign") or traversed or attrnames  2309   2310             # Determine whether the first access involves assignment.  2311   2312             assigning = not traversed and not attrnames and final_method == "assign"  2313             set_accessor = assigning and "<set_target_accessor>" or "<set_accessor>"  2314             stored_accessor = assigning and "<target_accessor>" or "<accessor>"  2315   2316             # Set the context if already available.  2317   2318             context_var = None  2319   2320             if context == "base":  2321                 accessor = context_var = (base,)  2322             elif context == "original-accessor":  2323   2324                 # Prevent re-evaluation of any dynamic expression by storing it.  2325   2326                 if original_accessor == "<expr>":  2327                     if final_method in ("access-invoke", "static-invoke"):  2328                         emit(("<set_context>", original_accessor))  2329                         accessor = context_var = ("<context>",)  2330                     else:  2331                         emit((set_accessor, original_accessor))  2332                         accessor = context_var = (stored_accessor,)  2333                 else:  2334                     accessor = context_var = (original_accessor,)  2335   2336             # Assigning does not set the context.  2337   2338             elif context in ("final-accessor", "unset") and access_first_attribute:  2339   2340                 # Prevent re-evaluation of any dynamic expression by storing it.  2341   2342                 if original_accessor == "<expr>":  2343                     emit((set_accessor, original_accessor))  2344                     accessor = (stored_accessor,)  2345                 else:  2346                     accessor = (original_accessor,)  2347   2348             # Apply any test.  2349   2350             if test[0] == "test":  2351                 accessor = ("__%s_%s_%s" % test, accessor, test_type)  2352   2353             # Perform the first or final access.  2354             # The access only needs performing if the resulting accessor is used.  2355   2356             remaining = len(traversed + attrnames)  2357   2358             if access_first_attribute:  2359   2360                 if first_method == "relative-class":  2361                     if assigning:  2362                         emit(("__store_via_class", accessor, attrname, "<assexpr>"))  2363                     else:  2364                         accessor = ("__load_via_class", accessor, attrname)  2365   2366                 elif first_method == "relative-object":  2367                     if assigning:  2368                         emit(("__store_via_object", accessor, attrname, "<assexpr>"))  2369                     else:  2370                         accessor = ("__load_via_object", accessor, attrname)  2371   2372                 elif first_method == "relative-object-class":  2373                     if assigning:  2374                         emit(("__get_class_and_store", accessor, attrname, "<assexpr>"))  2375                     else:  2376                         accessor = ("__get_class_and_load", accessor, attrname)  2377   2378                 elif first_method == "check-class":  2379                     if assigning:  2380                         emit(("__check_and_store_via_class", accessor, attrname, "<assexpr>"))  2381                     else:  2382                         accessor = ("__check_and_load_via_class", accessor, attrname)  2383   2384                 elif first_method == "check-object":  2385                     if assigning:  2386                         emit(("__check_and_store_via_object", accessor, attrname, "<assexpr>"))  2387                     else:  2388                         accessor = ("__check_and_load_via_object", accessor, attrname)  2389   2390                 elif first_method == "check-object-class":  2391                     if assigning:  2392                         emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>"))  2393                     else:  2394                         accessor = ("__check_and_load_via_any", accessor, attrname)  2395   2396             # Traverse attributes using the accessor.  2397   2398             if traversed:  2399                 for attrname, traversal_mode in zip(traversed, traversal_modes):  2400                     assigning = remaining == 1 and final_method == "assign"  2401   2402                     # Set the context, if appropriate.  2403   2404                     if remaining == 1 and final_method != "assign" and context == "final-accessor":  2405   2406                         # Invoked attributes employ a separate context accessed  2407                         # during invocation.  2408   2409                         if final_method in ("access-invoke", "static-invoke"):  2410                             emit(("<set_context>", accessor))  2411                             accessor = context_var = "<context>"  2412   2413                         # A private context within the access is otherwise  2414                         # retained.  2415   2416                         else:  2417                             emit(("<set_private_context>", accessor))  2418                             accessor = context_var = "<private_context>"  2419   2420                     # Perform the access only if not achieved directly.  2421   2422                     if remaining > 1 or final_method in ("access", "access-invoke", "assign"):  2423   2424                         if traversal_mode == "class":  2425                             if assigning:  2426                                 emit(("__store_via_class", accessor, attrname, "<assexpr>"))  2427                             else:  2428                                 accessor = ("__load_via_class", accessor, attrname)  2429                         else:  2430                             if assigning:  2431                                 emit(("__store_via_object", accessor, attrname, "<assexpr>"))  2432                             else:  2433                                 accessor = ("__load_via_object", accessor, attrname)  2434   2435                     remaining -= 1  2436   2437             if attrnames:  2438                 for attrname in attrnames:  2439                     assigning = remaining == 1 and final_method == "assign"  2440   2441                     # Set the context, if appropriate.  2442   2443                     if remaining == 1 and final_method != "assign" and context == "final-accessor":  2444   2445                         # Invoked attributes employ a separate context accessed  2446                         # during invocation.  2447   2448                         if final_method in ("access-invoke", "static-invoke"):  2449                             emit(("<set_context>", accessor))  2450                             accessor = context_var = "<context>"  2451   2452                         # A private context within the access is otherwise  2453                         # retained.  2454   2455                         else:  2456                             emit(("<set_private_context>", accessor))  2457                             accessor = context_var = "<private_context>"  2458   2459                     # Perform the access only if not achieved directly.  2460   2461                     if remaining > 1 or final_method in ("access", "access-invoke", "assign"):  2462   2463                         if assigning:  2464                             emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>"))  2465                         else:  2466                             accessor = ("__check_and_load_via_any", accessor, attrname)  2467   2468                     remaining -= 1  2469   2470             # Define or emit the means of accessing the actual target.  2471   2472             # Assignments to known attributes.  2473   2474             if final_method == "static-assign":  2475                 parent, attrname = origin.rsplit(".", 1)  2476                 emit(("__store_via_object", parent, attrname, "<assexpr>"))  2477   2478             # Invoked attributes employ a separate context.  2479   2480             elif final_method in ("static", "static-invoke"):  2481                 accessor = ("__load_static_ignore", origin)  2482   2483             # Wrap accesses in context operations.  2484   2485             if context_test == "test":  2486   2487                 # Test and combine the context with static attribute details.  2488   2489                 if final_method == "static":  2490                     emit(("__load_static_test", context_var, origin))  2491   2492                 # Test the context, storing it separately if required for the  2493                 # immediately invoked static attribute.  2494   2495                 elif final_method == "static-invoke":  2496                     emit(("<test_context_static>", context_var, origin))  2497   2498                 # Test the context, storing it separately if required for an  2499                 # immediately invoked attribute.  2500   2501                 elif final_method == "access-invoke":  2502                     emit(("<test_context_revert>", context_var, accessor))  2503   2504                 # Test the context and update the attribute details if  2505                 # appropriate.  2506   2507                 else:  2508                     emit(("__test_context", context_var, accessor))  2509   2510             elif context_test == "replace":  2511   2512                 # Produce an object with updated context.  2513   2514                 if final_method == "static":  2515                     emit(("__load_static_replace", context_var, origin))  2516   2517                 # Omit the context update operation where the target is static  2518                 # and the context is recorded separately.  2519   2520                 elif final_method == "static-invoke":  2521                     pass  2522   2523                 # If a separate context is used for an immediate invocation,  2524                 # produce the attribute details unchanged.  2525   2526                 elif final_method == "access-invoke":  2527                     emit(accessor)  2528   2529                 # Update the context in the attribute details.  2530   2531                 else:  2532                     emit(("__update_context", context_var, accessor))  2533   2534             # Omit the accessor for assignments and for invocations of static  2535             # targets.  2536   2537             elif final_method not in ("assign", "static-assign", "static-invoke"):  2538                 emit(accessor)  2539   2540             # Produce an advisory instruction regarding the context.  2541   2542             if context_var:  2543                 emit(("<context_identity>", context_var))  2544   2545             self.access_instructions[access_location] = instructions  2546             self.accessor_kinds[access_location] = accessor_kinds  2547   2548 # vim: tabstop=4 expandtab shiftwidth=4