1.1 --- a/deducer.py Thu Mar 30 18:48:36 2017 +0200
1.2 +++ b/deducer.py Thu Mar 30 19:41:24 2017 +0200
1.3 @@ -19,10 +19,10 @@
1.4 this program. If not, see <http://www.gnu.org/licenses/>.
1.5 """
1.6
1.7 -from common import first, get_assigned_attributes, \
1.8 - get_attrname_from_location, get_attrnames, \
1.9 +from common import first, get_assigned_attributes, get_attrnames, \
1.10 get_invoked_attributes, get_name_path, init_item, \
1.11 - order_dependencies_partial, sorted_output, CommonOutput
1.12 + order_dependencies_partial, sorted_output, \
1.13 + AccessLocation, CommonOutput, Location
1.14 from encoders import encode_access_location, encode_alias_location, \
1.15 encode_constrained, encode_instruction, encode_location, \
1.16 encode_usage, get_kinds, \
1.17 @@ -75,6 +75,11 @@
1.18
1.19 self.alias_index_rev = {}
1.20
1.21 + # Map definitions and accesses to initialised names.
1.22 +
1.23 + self.initialised_names = {}
1.24 + self.initialised_accesses = {}
1.25 +
1.26 # Map constant accesses to redefined accesses.
1.27
1.28 self.const_accesses = {}
1.29 @@ -401,7 +406,7 @@
1.30 referenced_attrs = self.referenced_attrs[location]
1.31
1.32 if referenced_attrs:
1.33 - attrname = get_attrname_from_location(location)
1.34 + attrname = location.get_attrname()
1.35
1.36 all_accessed_attrs = list(set(self.reference_all_attrs[location]))
1.37 all_accessed_attrs.sort()
1.38 @@ -636,7 +641,7 @@
1.39 # Record attribute information for each name used on the
1.40 # accessor.
1.41
1.42 - attrname = get_attrname_from_location(location)
1.43 + attrname = location.get_attrname()
1.44
1.45 self.reference_all_attrs[location] = all_accessed_attrs = []
1.46 self.reference_all_providers[location] = all_providers = []
1.47 @@ -744,12 +749,11 @@
1.48 "Introduce more module dependencies to the importer."
1.49
1.50 for location, referenced_attrs in self.referenced_attrs.items():
1.51 - path, name, attrnames, version = location
1.52
1.53 # Identify references providing dependencies.
1.54
1.55 for attrtype, objtype, attr in referenced_attrs:
1.56 - self.importer.add_dependency(path, attr.get_origin())
1.57 + self.importer.add_dependency(location.path, attr.get_origin())
1.58
1.59 def get_referenced_attrs(self, location):
1.60
1.61 @@ -817,10 +821,10 @@
1.62 for path, assignments in module.attr_usage.items():
1.63 self.add_usage(assignments, path)
1.64
1.65 - for location, all_attrnames in self.importer.all_attr_accesses.items():
1.66 + for path, all_attrnames in self.importer.all_attr_accesses.items():
1.67 for attrnames in all_attrnames:
1.68 attrname = get_attrnames(attrnames)[-1]
1.69 - access_location = (location, None, attrnames, 0)
1.70 + access_location = AccessLocation(path, None, attrnames, 0)
1.71 self.add_usage_term(access_location, ((attrname, False, False),))
1.72
1.73 def add_usage(self, assignments, path):
1.74 @@ -833,7 +837,7 @@
1.75
1.76 for name, versions in assignments.items():
1.77 for i, usages in enumerate(versions):
1.78 - location = (path, name, None, i)
1.79 + location = Location(path, name, None, i)
1.80
1.81 for usage in usages:
1.82 self.add_usage_term(location, usage)
1.83 @@ -872,11 +876,11 @@
1.84 # Obtain the usage details using the access information.
1.85
1.86 for access_number, versions in enumerate(accesses):
1.87 - access_location = (path, name, attrnames, access_number)
1.88 + access_location = AccessLocation(path, name, attrnames, access_number)
1.89 locations = []
1.90
1.91 for version in versions:
1.92 - location = (path, name, None, version)
1.93 + location = Location(path, name, None, version)
1.94 locations.append(location)
1.95
1.96 # Map accessors to affected accesses.
1.97 @@ -922,9 +926,9 @@
1.98 for access_number, (assignment, invocation) in enumerate(modifiers):
1.99
1.100 if name:
1.101 - access_location = (path, name, attrname_str, access_number)
1.102 + access_location = AccessLocation(path, name, attrname_str, access_number)
1.103 else:
1.104 - access_location = (path, None, attrname_str, 0)
1.105 + access_location = AccessLocation(path, None, attrname_str, 0)
1.106
1.107 # Plain name accesses do not employ attributes and are
1.108 # ignored. Whether they are invoked is of interest, however.
1.109 @@ -983,15 +987,24 @@
1.110
1.111 for version, aliases in all_aliases.items():
1.112 for (original_path, original_name, attrnames, access_number) in aliases:
1.113 - accessor_location = (path, name, None, version)
1.114 - access_location = (original_path, original_name, attrnames, access_number)
1.115 + accessor_location = Location(path, name, None, version)
1.116 + access_location = AccessLocation(original_path, original_name, attrnames, access_number)
1.117 init_item(self.alias_index, accessor_location, list)
1.118 self.alias_index[accessor_location].append(access_location)
1.119
1.120 - # Get aliases in terms of non-aliases and accesses.
1.121 -
1.122 - for accessor_location, access_locations in self.alias_index.items():
1.123 - self.update_aliases(accessor_location, access_locations)
1.124 + # Get initialised name information for accessors and accesses.
1.125 +
1.126 + for (path, name), versions in self.importer.all_initialised_names.items():
1.127 + for version, ref in versions.items():
1.128 + location = Location(path, name, None, version)
1.129 +
1.130 + self.initialised_names[location] = ref
1.131 +
1.132 + access_locations = self.access_index_rev.get(location)
1.133 + if access_locations:
1.134 + for access_location in access_locations:
1.135 + init_item(self.initialised_accesses, access_location, set)
1.136 + self.initialised_accesses[access_location].add(ref)
1.137
1.138 # Get a mapping from accesses to affected aliases.
1.139
1.140 @@ -1000,62 +1013,6 @@
1.141 init_item(self.alias_index_rev, access_location, set)
1.142 self.alias_index_rev[access_location].add(accessor_location)
1.143
1.144 - def update_aliases(self, accessor_location, access_locations, visited=None):
1.145 -
1.146 - """
1.147 - Update the given 'accessor_location' defining an alias, update
1.148 - 'access_locations' to refer to non-aliases, following name references
1.149 - via the access index.
1.150 -
1.151 - If 'visited' is specified, it contains a set of accessor locations (and
1.152 - thus keys to the alias index) that are currently being defined.
1.153 - """
1.154 -
1.155 - if visited is None:
1.156 - visited = set()
1.157 -
1.158 - updated_locations = set()
1.159 -
1.160 - for access_location in access_locations:
1.161 - (path, original_name, attrnames, access_number) = access_location
1.162 -
1.163 - # Locations may have been recorded for return values, but they may
1.164 - # not correspond to actual accesses.
1.165 -
1.166 - if not self.access_index.has_key(access_location):
1.167 - updated_locations.add(access_location)
1.168 -
1.169 - # Where an alias refers to a name access, obtain the original name
1.170 - # version details.
1.171 -
1.172 - elif attrnames is None:
1.173 -
1.174 - # For each name version, attempt to determine any accesses that
1.175 - # initialise the name.
1.176 -
1.177 - for name_accessor_location in self.access_index[access_location]:
1.178 -
1.179 - # Already-visited aliases do not contribute details.
1.180 -
1.181 - if name_accessor_location in visited:
1.182 - continue
1.183 -
1.184 - visited.add(name_accessor_location)
1.185 -
1.186 - name_access_locations = self.alias_index.get(name_accessor_location)
1.187 - if name_access_locations:
1.188 - updated_locations.update(self.update_aliases(name_accessor_location, name_access_locations, visited))
1.189 - else:
1.190 - updated_locations.add(name_accessor_location)
1.191 -
1.192 - # Otherwise, record the access details.
1.193 -
1.194 - else:
1.195 - updated_locations.add(access_location)
1.196 -
1.197 - self.alias_index[accessor_location] = updated_locations
1.198 - return updated_locations
1.199 -
1.200 # Attribute mutation for types.
1.201
1.202 def modify_mutated_attributes(self):
1.203 @@ -1284,7 +1241,7 @@
1.204
1.205 # Anonymous references with attribute chains.
1.206
1.207 - for location, accesses in self.importer.all_attr_accesses.items():
1.208 + for path, accesses in self.importer.all_attr_accesses.items():
1.209
1.210 # Get distinct attribute names.
1.211
1.212 @@ -1296,12 +1253,12 @@
1.213 # Get attribute and accessor details for each attribute name.
1.214
1.215 for attrname in all_attrnames:
1.216 - access_location = (location, None, attrname, 0)
1.217 + access_location = AccessLocation(path, None, attrname, 0)
1.218 self.record_types_for_attribute(access_location, attrname)
1.219
1.220 # References via constant/identified objects.
1.221
1.222 - for location, name_accesses in self.importer.all_const_accesses.items():
1.223 + for path, name_accesses in self.importer.all_const_accesses.items():
1.224
1.225 # A mapping from the original name and attributes to resolved access
1.226 # details.
1.227 @@ -1324,8 +1281,8 @@
1.228 oa, attrname = original_accessor[:-1], original_accessor[-1]
1.229 oa = ".".join(oa)
1.230
1.231 - access_location = (location, oa, attrname, 0)
1.232 - accessor_location = (location, oa, None, 0)
1.233 + access_location = AccessLocation(path, oa, attrname, 0)
1.234 + accessor_location = Location(path, oa, None, 0)
1.235 self.access_index[access_location] = [accessor_location]
1.236
1.237 self.init_access_details(access_location)
1.238 @@ -1364,8 +1321,8 @@
1.239 oa = original_accessor[:-len(l)]
1.240 oa = ".".join(oa)
1.241
1.242 - access_location = (location, oa, attrnames, 0)
1.243 - accessor_location = (location, oa, None, 0)
1.244 + access_location = AccessLocation(path, oa, attrnames, 0)
1.245 + accessor_location = Location(path, oa, None, 0)
1.246 self.access_index[access_location] = [accessor_location]
1.247
1.248 self.init_access_details(access_location)
1.249 @@ -1379,7 +1336,7 @@
1.250 # Define mappings between the original and access locations
1.251 # so that translation can work from the source details.
1.252
1.253 - original_location = (location, original_name, original_attrnames, 0)
1.254 + original_location = AccessLocation(path, original_name, original_attrnames, 0)
1.255
1.256 if original_location != access_location:
1.257 self.const_accesses[original_location] = access_location
1.258 @@ -1469,12 +1426,11 @@
1.259 plus whether the types have been constrained to a specific kind of type.
1.260 """
1.261
1.262 - unit_path, name, attrnames, version = location
1.263 have_assignments = get_assigned_attributes(usage)
1.264
1.265 # Detect any initialised name for the location.
1.266
1.267 - if name:
1.268 + if location.name:
1.269 refs = self.get_initialised_name(location)
1.270 if refs:
1.271 (class_types, only_instance_types, module_types,
1.272 @@ -1490,12 +1446,12 @@
1.273 # Merge usage deductions with observations to obtain reference types
1.274 # for names involved with attribute accesses.
1.275
1.276 - if not name:
1.277 + if not location.name:
1.278 return class_types, only_instance_types, module_types, False, have_assignments
1.279
1.280 # Obtain references to known objects.
1.281
1.282 - path = get_name_path(unit_path, name)
1.283 + path = get_name_path(location.path, location.name)
1.284
1.285 class_types, only_instance_types, module_types, constrained_specific = \
1.286 self.constrain_types(path, class_types, only_instance_types, module_types)
1.287 @@ -1506,19 +1462,19 @@
1.288
1.289 # Constrain "self" references.
1.290
1.291 - if name == "self":
1.292 + if location.name == "self":
1.293
1.294 # Test for the class of the method in the deduced types.
1.295
1.296 - class_name = self.in_method(unit_path)
1.297 + class_name = self.in_method(location.path)
1.298
1.299 if class_name and class_name not in class_types and class_name not in only_instance_types:
1.300 raise DeduceError("In %s, usage {%s} is not directly supported by class %s or its instances." %
1.301 - (unit_path, encode_usage(usage), class_name))
1.302 + (location.path, encode_usage(usage), class_name))
1.303
1.304 # Constrain the types to the class's hierarchy.
1.305
1.306 - t = self.constrain_self_reference(unit_path, class_types, only_instance_types)
1.307 + t = self.constrain_self_reference(location.path, class_types, only_instance_types)
1.308 if t:
1.309 class_types, only_instance_types, module_types, constrained = t
1.310 return class_types, only_instance_types, module_types, constrained, have_assignments
1.311 @@ -1590,7 +1546,7 @@
1.312 'accessor_locations'. Return whether referenced attributes were updated.
1.313 """
1.314
1.315 - attrname = get_attrname_from_location(access_location)
1.316 + attrname = access_location.get_attrname()
1.317 if not attrname:
1.318 return False
1.319
1.320 @@ -1716,8 +1672,8 @@
1.321 refs = set()
1.322
1.323 for access_location in self.alias_index[accessor_location]:
1.324 - location, name, attrnames, access_number = access_location
1.325 - invocation = self.reference_invocations.get(access_location)
1.326 + attrnames = access_location.attrnames
1.327 + invocation = self.reference_invocations.has_key(access_location)
1.328
1.329 attrnames = attrnames and attrnames.split(".")
1.330 remaining = attrnames and len(attrnames) > 1
1.331 @@ -1755,7 +1711,7 @@
1.332 # Attempt to refine the types using initialised names or
1.333 # accessors.
1.334
1.335 - attrs = self.get_initialised_name(access_location)
1.336 + attrs = self.get_initialised_access(access_location)
1.337
1.338 if attrs:
1.339 provider_attrs = self.convert_invocation_providers(attrs, invocation)
1.340 @@ -1826,8 +1782,8 @@
1.341 refs = set()
1.342
1.343 for access_location in self.alias_index[accessor_location]:
1.344 - location, name, attrnames, access_number = access_location
1.345 - invocation = self.reference_invocations.get(access_location)
1.346 + attrnames = access_location.attrnames
1.347 + invocation = self.reference_invocations.has_key(access_location)
1.348
1.349 attrnames = attrnames and attrnames.split(".")
1.350 remaining = attrnames and len(attrnames) > 1
1.351 @@ -1853,7 +1809,7 @@
1.352
1.353 # Obtain initialiser information.
1.354
1.355 - attrs = self.get_initialised_name(access_location)
1.356 + attrs = self.get_initialised_access(access_location)
1.357
1.358 if attrs:
1.359 provider_attrs = self.convert_invocation_providers(attrs, invocation)
1.360 @@ -1923,8 +1879,10 @@
1.361 if not versions:
1.362 return
1.363
1.364 + path, name = key
1.365 +
1.366 for version in versions.keys():
1.367 - location = key + (None, version)
1.368 + location = Location(path, name, None, version)
1.369 l = init_item(self.alias_index_rev, location, set)
1.370 l.add(access_location)
1.371
1.372 @@ -2031,22 +1989,27 @@
1.373 else:
1.374 return refs
1.375
1.376 - def get_initialised_name(self, access_location):
1.377 + def get_initialised_name(self, location):
1.378
1.379 """
1.380 - Return references for any initialised names at 'access_location', or
1.381 - None if no such references exist.
1.382 + Return references for any initialised names at 'location', or None if no
1.383 + such references exist.
1.384 """
1.385
1.386 - path, name, attrnames, version = access_location
1.387 -
1.388 - # Use initialiser information, if available.
1.389 -
1.390 - initialisers = self.importer.all_initialised_names.get((path, name))
1.391 - if initialisers and initialisers.has_key(version):
1.392 - return [initialisers[version]]
1.393 + ref = self.initialised_names.get(location)
1.394 + return ref and [ref] or None
1.395 +
1.396 + def get_initialised_access(self, location):
1.397 +
1.398 + """
1.399 + Return references for any initialised names supplying the given access
1.400 + 'location', or None if no such references exist.
1.401 + """
1.402 +
1.403 + if location.access_number is not None:
1.404 + return self.initialised_accesses.get(location)
1.405 else:
1.406 - return None
1.407 + return self.get_initialised_name(location)
1.408
1.409 def get_accessor_references(self, access_location):
1.410
1.411 @@ -2112,7 +2075,7 @@
1.412 # Instance-only and module types support only their own kinds as
1.413 # accessors.
1.414
1.415 - path, name, attrnames, version = location
1.416 + path, name = location.path, location.name
1.417
1.418 if invocations:
1.419 class_only_types = self.filter_for_invocations(class_types, invocations)
1.420 @@ -2334,7 +2297,7 @@
1.421
1.422 const_access = self.const_accesses_rev.get(location)
1.423
1.424 - path, name, attrnames, version = location
1.425 + path, name, attrnames = location.path, location.name, location.attrnames
1.426 remaining = attrnames.split(".")
1.427 attrname = remaining[0]
1.428