1.1 --- a/deducer.py Tue Mar 14 22:52:13 2017 +0100
1.2 +++ b/deducer.py Tue Mar 14 22:54:03 2017 +0100
1.3 @@ -146,7 +146,6 @@
1.4 self.init_accessors()
1.5 self.init_accesses()
1.6 self.init_aliases()
1.7 - self.init_alias_network()
1.8 self.modify_mutated_attributes()
1.9 self.identify_references()
1.10 self.classify_accessors()
1.11 @@ -969,6 +968,15 @@
1.12 for accessor_location, access_locations in self.alias_index.items():
1.13 self.update_aliases(accessor_location, access_locations)
1.14
1.15 + # Get accesses affected by aliases.
1.16 +
1.17 + self.alias_index_rev = {}
1.18 +
1.19 + for accessor_location, access_locations in self.alias_index.items():
1.20 + for access_location in access_locations:
1.21 + init_item(self.alias_index_rev, access_location, set)
1.22 + self.alias_index_rev[access_location].add(accessor_location)
1.23 +
1.24 def update_aliases(self, accessor_location, access_locations, visited=None):
1.25
1.26 """
1.27 @@ -1019,21 +1027,6 @@
1.28 self.alias_index[accessor_location] = updated_locations
1.29 return updated_locations
1.30
1.31 - def init_alias_network(self):
1.32 -
1.33 - """
1.34 - Initialise a network of aliases, their initialising accesses, and the
1.35 - accessors supporting those accesses.
1.36 - """
1.37 -
1.38 - self.alias_network = {}
1.39 - self.alias_network.update(self.alias_index)
1.40 -
1.41 - for accessor_location, access_locations in self.alias_index.items():
1.42 - for access_location in access_locations:
1.43 - if not self.alias_network.has_key(access_location):
1.44 - self.alias_network[access_location] = self.get_accessors_for_access(access_location)
1.45 -
1.46 # Attribute mutation for types.
1.47
1.48 def modify_mutated_attributes(self):
1.49 @@ -1365,17 +1358,39 @@
1.50 self.const_accesses[original_location] = access_location
1.51 self.const_accesses_rev[access_location] = original_location
1.52
1.53 - # Aliased name definitions. All aliases with usage will have been
1.54 - # defined, but they may be refined according to referenced accesses.
1.55 -
1.56 - for location in order_dependencies_partial(self.alias_network):
1.57 - if self.alias_index.has_key(location):
1.58 - self.record_types_for_alias(location)
1.59 -
1.60 - # Update accesses employing aliases.
1.61 -
1.62 - for access_location in alias_accesses:
1.63 - self.record_types_for_access(access_location, self.access_index[access_location])
1.64 + # Propagate alias-related information.
1.65 +
1.66 + affected_aliases = set(self.alias_index.keys())
1.67 +
1.68 + while True:
1.69 +
1.70 + # Aliased name definitions. All aliases with usage will have been
1.71 + # defined, but they may be refined according to referenced accesses.
1.72 +
1.73 + updated_aliases = set()
1.74 +
1.75 + for location in affected_aliases:
1.76 + if self.record_types_for_alias(location):
1.77 + updated_aliases.add(location)
1.78 +
1.79 + # Update accesses employing aliases.
1.80 +
1.81 + updated_accesses = set()
1.82 +
1.83 + for access_location in alias_accesses:
1.84 + if self.record_types_for_access(access_location, self.access_index[access_location]):
1.85 + updated_accesses.add(access_location)
1.86 +
1.87 + # Update aliases for updated accesses.
1.88 +
1.89 + affected_aliases = set()
1.90 +
1.91 + for access_location in updated_accesses:
1.92 + if self.alias_index_rev.has_key(access_location):
1.93 + affected_aliases.update(self.alias_index_rev[access_location])
1.94 +
1.95 + if not affected_aliases:
1.96 + break
1.97
1.98 def constrain_types(self, path, class_types, instance_types, module_types):
1.99
1.100 @@ -1530,12 +1545,12 @@
1.101
1.102 """
1.103 Define types for the 'access_location' associated with the given
1.104 - 'accessor_locations'.
1.105 + 'accessor_locations'. Return whether referenced attributes were updated.
1.106 """
1.107
1.108 attrname = get_attrname_from_location(access_location)
1.109 if not attrname:
1.110 - return
1.111 + return False
1.112
1.113 invocation = access_location in self.reference_invocations
1.114 assignment = access_location in self.reference_assignments
1.115 @@ -1549,6 +1564,8 @@
1.116
1.117 constrained = True
1.118
1.119 + old_referenced_attrs = self.referenced_attrs.get(access_location)
1.120 +
1.121 for location in accessor_locations:
1.122
1.123 # Remember accesses employing aliases.
1.124 @@ -1576,6 +1593,10 @@
1.125 self.init_access_details(access_location)
1.126 self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained)
1.127
1.128 + # Return whether the referenced attributes have changed.
1.129 +
1.130 + return old_referenced_attrs != self.referenced_attrs.get(access_location)
1.131 +
1.132 def record_types_for_usage(self, accessor_location, usage):
1.133
1.134 """
1.135 @@ -1626,6 +1647,7 @@
1.136
1.137 """
1.138 Define types for the 'accessor_location' not having associated usage.
1.139 + Return whether the types were updated.
1.140 """
1.141
1.142 have_access = self.provider_class_types.has_key(accessor_location)
1.143 @@ -1666,7 +1688,7 @@
1.144 # need to be traversed first.
1.145
1.146 if remaining:
1.147 - return
1.148 + return False
1.149
1.150 # Alias references an attribute access.
1.151
1.152 @@ -1680,7 +1702,7 @@
1.153 # to refine the alias's types.
1.154
1.155 if not attrs:
1.156 - return
1.157 + return False
1.158
1.159 # Invocations converting class accessors to instances do not
1.160 # change the nature of class providers.
1.161 @@ -1694,7 +1716,7 @@
1.162 # the defined accessor types.
1.163
1.164 if function_types or var_types:
1.165 - return
1.166 + return False
1.167
1.168 class_types = set(provider_class_types).intersection(class_types)
1.169 instance_types = set(provider_instance_types).intersection(instance_types)
1.170 @@ -1758,7 +1780,7 @@
1.171 # refine the defined accessor types.
1.172
1.173 else:
1.174 - return
1.175 + return False
1.176
1.177 # Record refined type details for the alias as an accessor.
1.178
1.179 @@ -1766,11 +1788,16 @@
1.180 self.update_provider_types(accessor_location, new_provider_class_types, new_provider_instance_types, new_provider_module_types)
1.181 self.update_accessor_types(accessor_location, new_accessor_class_types, new_accessor_instance_types, new_accessor_module_types)
1.182
1.183 + return new_accessor_class_types != accessor_class_types or \
1.184 + new_accessor_instance_types != accessor_instance_types or \
1.185 + new_accessor_module_types != accessor_module_types
1.186 +
1.187 # Without an access, attempt to identify references for the alias.
1.188 # Invocations convert classes to instances and also attempt to find
1.189 # return value information.
1.190
1.191 else:
1.192 + old_refs = self.referenced_objects.get(accessor_location)
1.193 refs = set()
1.194
1.195 for access_location in self.alias_index[accessor_location]:
1.196 @@ -1791,7 +1818,7 @@
1.197 # need to be traversed first.
1.198
1.199 if remaining:
1.200 - return
1.201 + return False
1.202
1.203 # Alias references an attribute access.
1.204
1.205 @@ -1827,6 +1854,8 @@
1.206
1.207 self.referenced_objects[accessor_location] = refs
1.208
1.209 + return old_refs != refs
1.210 +
1.211 def get_references_for_access(self, access_location):
1.212
1.213 "Return the references identified for 'access_location'."