2.1 --- a/deducer.py Thu Mar 30 18:48:36 2017 +0200
2.2 +++ b/deducer.py Thu Mar 30 19:41:24 2017 +0200
2.3 @@ -19,10 +19,10 @@
2.4 this program. If not, see <http://www.gnu.org/licenses/>.
2.5 """
2.6
2.7 -from common import first, get_assigned_attributes, \
2.8 - get_attrname_from_location, get_attrnames, \
2.9 +from common import first, get_assigned_attributes, get_attrnames, \
2.10 get_invoked_attributes, get_name_path, init_item, \
2.11 - order_dependencies_partial, sorted_output, CommonOutput
2.12 + order_dependencies_partial, sorted_output, \
2.13 + AccessLocation, CommonOutput, Location
2.14 from encoders import encode_access_location, encode_alias_location, \
2.15 encode_constrained, encode_instruction, encode_location, \
2.16 encode_usage, get_kinds, \
2.17 @@ -75,6 +75,11 @@
2.18
2.19 self.alias_index_rev = {}
2.20
2.21 + # Map definitions and accesses to initialised names.
2.22 +
2.23 + self.initialised_names = {}
2.24 + self.initialised_accesses = {}
2.25 +
2.26 # Map constant accesses to redefined accesses.
2.27
2.28 self.const_accesses = {}
2.29 @@ -401,7 +406,7 @@
2.30 referenced_attrs = self.referenced_attrs[location]
2.31
2.32 if referenced_attrs:
2.33 - attrname = get_attrname_from_location(location)
2.34 + attrname = location.get_attrname()
2.35
2.36 all_accessed_attrs = list(set(self.reference_all_attrs[location]))
2.37 all_accessed_attrs.sort()
2.38 @@ -636,7 +641,7 @@
2.39 # Record attribute information for each name used on the
2.40 # accessor.
2.41
2.42 - attrname = get_attrname_from_location(location)
2.43 + attrname = location.get_attrname()
2.44
2.45 self.reference_all_attrs[location] = all_accessed_attrs = []
2.46 self.reference_all_providers[location] = all_providers = []
2.47 @@ -744,12 +749,11 @@
2.48 "Introduce more module dependencies to the importer."
2.49
2.50 for location, referenced_attrs in self.referenced_attrs.items():
2.51 - path, name, attrnames, version = location
2.52
2.53 # Identify references providing dependencies.
2.54
2.55 for attrtype, objtype, attr in referenced_attrs:
2.56 - self.importer.add_dependency(path, attr.get_origin())
2.57 + self.importer.add_dependency(location.path, attr.get_origin())
2.58
2.59 def get_referenced_attrs(self, location):
2.60
2.61 @@ -817,10 +821,10 @@
2.62 for path, assignments in module.attr_usage.items():
2.63 self.add_usage(assignments, path)
2.64
2.65 - for location, all_attrnames in self.importer.all_attr_accesses.items():
2.66 + for path, all_attrnames in self.importer.all_attr_accesses.items():
2.67 for attrnames in all_attrnames:
2.68 attrname = get_attrnames(attrnames)[-1]
2.69 - access_location = (location, None, attrnames, 0)
2.70 + access_location = AccessLocation(path, None, attrnames, 0)
2.71 self.add_usage_term(access_location, ((attrname, False, False),))
2.72
2.73 def add_usage(self, assignments, path):
2.74 @@ -833,7 +837,7 @@
2.75
2.76 for name, versions in assignments.items():
2.77 for i, usages in enumerate(versions):
2.78 - location = (path, name, None, i)
2.79 + location = Location(path, name, None, i)
2.80
2.81 for usage in usages:
2.82 self.add_usage_term(location, usage)
2.83 @@ -872,11 +876,11 @@
2.84 # Obtain the usage details using the access information.
2.85
2.86 for access_number, versions in enumerate(accesses):
2.87 - access_location = (path, name, attrnames, access_number)
2.88 + access_location = AccessLocation(path, name, attrnames, access_number)
2.89 locations = []
2.90
2.91 for version in versions:
2.92 - location = (path, name, None, version)
2.93 + location = Location(path, name, None, version)
2.94 locations.append(location)
2.95
2.96 # Map accessors to affected accesses.
2.97 @@ -922,9 +926,9 @@
2.98 for access_number, (assignment, invocation) in enumerate(modifiers):
2.99
2.100 if name:
2.101 - access_location = (path, name, attrname_str, access_number)
2.102 + access_location = AccessLocation(path, name, attrname_str, access_number)
2.103 else:
2.104 - access_location = (path, None, attrname_str, 0)
2.105 + access_location = AccessLocation(path, None, attrname_str, 0)
2.106
2.107 # Plain name accesses do not employ attributes and are
2.108 # ignored. Whether they are invoked is of interest, however.
2.109 @@ -983,15 +987,24 @@
2.110
2.111 for version, aliases in all_aliases.items():
2.112 for (original_path, original_name, attrnames, access_number) in aliases:
2.113 - accessor_location = (path, name, None, version)
2.114 - access_location = (original_path, original_name, attrnames, access_number)
2.115 + accessor_location = Location(path, name, None, version)
2.116 + access_location = AccessLocation(original_path, original_name, attrnames, access_number)
2.117 init_item(self.alias_index, accessor_location, list)
2.118 self.alias_index[accessor_location].append(access_location)
2.119
2.120 - # Get aliases in terms of non-aliases and accesses.
2.121 -
2.122 - for accessor_location, access_locations in self.alias_index.items():
2.123 - self.update_aliases(accessor_location, access_locations)
2.124 + # Get initialised name information for accessors and accesses.
2.125 +
2.126 + for (path, name), versions in self.importer.all_initialised_names.items():
2.127 + for version, ref in versions.items():
2.128 + location = Location(path, name, None, version)
2.129 +
2.130 + self.initialised_names[location] = ref
2.131 +
2.132 + access_locations = self.access_index_rev.get(location)
2.133 + if access_locations:
2.134 + for access_location in access_locations:
2.135 + init_item(self.initialised_accesses, access_location, set)
2.136 + self.initialised_accesses[access_location].add(ref)
2.137
2.138 # Get a mapping from accesses to affected aliases.
2.139
2.140 @@ -1000,62 +1013,6 @@
2.141 init_item(self.alias_index_rev, access_location, set)
2.142 self.alias_index_rev[access_location].add(accessor_location)
2.143
2.144 - def update_aliases(self, accessor_location, access_locations, visited=None):
2.145 -
2.146 - """
2.147 - Update the given 'accessor_location' defining an alias, update
2.148 - 'access_locations' to refer to non-aliases, following name references
2.149 - via the access index.
2.150 -
2.151 - If 'visited' is specified, it contains a set of accessor locations (and
2.152 - thus keys to the alias index) that are currently being defined.
2.153 - """
2.154 -
2.155 - if visited is None:
2.156 - visited = set()
2.157 -
2.158 - updated_locations = set()
2.159 -
2.160 - for access_location in access_locations:
2.161 - (path, original_name, attrnames, access_number) = access_location
2.162 -
2.163 - # Locations may have been recorded for return values, but they may
2.164 - # not correspond to actual accesses.
2.165 -
2.166 - if not self.access_index.has_key(access_location):
2.167 - updated_locations.add(access_location)
2.168 -
2.169 - # Where an alias refers to a name access, obtain the original name
2.170 - # version details.
2.171 -
2.172 - elif attrnames is None:
2.173 -
2.174 - # For each name version, attempt to determine any accesses that
2.175 - # initialise the name.
2.176 -
2.177 - for name_accessor_location in self.access_index[access_location]:
2.178 -
2.179 - # Already-visited aliases do not contribute details.
2.180 -
2.181 - if name_accessor_location in visited:
2.182 - continue
2.183 -
2.184 - visited.add(name_accessor_location)
2.185 -
2.186 - name_access_locations = self.alias_index.get(name_accessor_location)
2.187 - if name_access_locations:
2.188 - updated_locations.update(self.update_aliases(name_accessor_location, name_access_locations, visited))
2.189 - else:
2.190 - updated_locations.add(name_accessor_location)
2.191 -
2.192 - # Otherwise, record the access details.
2.193 -
2.194 - else:
2.195 - updated_locations.add(access_location)
2.196 -
2.197 - self.alias_index[accessor_location] = updated_locations
2.198 - return updated_locations
2.199 -
2.200 # Attribute mutation for types.
2.201
2.202 def modify_mutated_attributes(self):
2.203 @@ -1284,7 +1241,7 @@
2.204
2.205 # Anonymous references with attribute chains.
2.206
2.207 - for location, accesses in self.importer.all_attr_accesses.items():
2.208 + for path, accesses in self.importer.all_attr_accesses.items():
2.209
2.210 # Get distinct attribute names.
2.211
2.212 @@ -1296,12 +1253,12 @@
2.213 # Get attribute and accessor details for each attribute name.
2.214
2.215 for attrname in all_attrnames:
2.216 - access_location = (location, None, attrname, 0)
2.217 + access_location = AccessLocation(path, None, attrname, 0)
2.218 self.record_types_for_attribute(access_location, attrname)
2.219
2.220 # References via constant/identified objects.
2.221
2.222 - for location, name_accesses in self.importer.all_const_accesses.items():
2.223 + for path, name_accesses in self.importer.all_const_accesses.items():
2.224
2.225 # A mapping from the original name and attributes to resolved access
2.226 # details.
2.227 @@ -1324,8 +1281,8 @@
2.228 oa, attrname = original_accessor[:-1], original_accessor[-1]
2.229 oa = ".".join(oa)
2.230
2.231 - access_location = (location, oa, attrname, 0)
2.232 - accessor_location = (location, oa, None, 0)
2.233 + access_location = AccessLocation(path, oa, attrname, 0)
2.234 + accessor_location = Location(path, oa, None, 0)
2.235 self.access_index[access_location] = [accessor_location]
2.236
2.237 self.init_access_details(access_location)
2.238 @@ -1364,8 +1321,8 @@
2.239 oa = original_accessor[:-len(l)]
2.240 oa = ".".join(oa)
2.241
2.242 - access_location = (location, oa, attrnames, 0)
2.243 - accessor_location = (location, oa, None, 0)
2.244 + access_location = AccessLocation(path, oa, attrnames, 0)
2.245 + accessor_location = Location(path, oa, None, 0)
2.246 self.access_index[access_location] = [accessor_location]
2.247
2.248 self.init_access_details(access_location)
2.249 @@ -1379,7 +1336,7 @@
2.250 # Define mappings between the original and access locations
2.251 # so that translation can work from the source details.
2.252
2.253 - original_location = (location, original_name, original_attrnames, 0)
2.254 + original_location = AccessLocation(path, original_name, original_attrnames, 0)
2.255
2.256 if original_location != access_location:
2.257 self.const_accesses[original_location] = access_location
2.258 @@ -1469,12 +1426,11 @@
2.259 plus whether the types have been constrained to a specific kind of type.
2.260 """
2.261
2.262 - unit_path, name, attrnames, version = location
2.263 have_assignments = get_assigned_attributes(usage)
2.264
2.265 # Detect any initialised name for the location.
2.266
2.267 - if name:
2.268 + if location.name:
2.269 refs = self.get_initialised_name(location)
2.270 if refs:
2.271 (class_types, only_instance_types, module_types,
2.272 @@ -1490,12 +1446,12 @@
2.273 # Merge usage deductions with observations to obtain reference types
2.274 # for names involved with attribute accesses.
2.275
2.276 - if not name:
2.277 + if not location.name:
2.278 return class_types, only_instance_types, module_types, False, have_assignments
2.279
2.280 # Obtain references to known objects.
2.281
2.282 - path = get_name_path(unit_path, name)
2.283 + path = get_name_path(location.path, location.name)
2.284
2.285 class_types, only_instance_types, module_types, constrained_specific = \
2.286 self.constrain_types(path, class_types, only_instance_types, module_types)
2.287 @@ -1506,19 +1462,19 @@
2.288
2.289 # Constrain "self" references.
2.290
2.291 - if name == "self":
2.292 + if location.name == "self":
2.293
2.294 # Test for the class of the method in the deduced types.
2.295
2.296 - class_name = self.in_method(unit_path)
2.297 + class_name = self.in_method(location.path)
2.298
2.299 if class_name and class_name not in class_types and class_name not in only_instance_types:
2.300 raise DeduceError("In %s, usage {%s} is not directly supported by class %s or its instances." %
2.301 - (unit_path, encode_usage(usage), class_name))
2.302 + (location.path, encode_usage(usage), class_name))
2.303
2.304 # Constrain the types to the class's hierarchy.
2.305
2.306 - t = self.constrain_self_reference(unit_path, class_types, only_instance_types)
2.307 + t = self.constrain_self_reference(location.path, class_types, only_instance_types)
2.308 if t:
2.309 class_types, only_instance_types, module_types, constrained = t
2.310 return class_types, only_instance_types, module_types, constrained, have_assignments
2.311 @@ -1590,7 +1546,7 @@
2.312 'accessor_locations'. Return whether referenced attributes were updated.
2.313 """
2.314
2.315 - attrname = get_attrname_from_location(access_location)
2.316 + attrname = access_location.get_attrname()
2.317 if not attrname:
2.318 return False
2.319
2.320 @@ -1716,8 +1672,8 @@
2.321 refs = set()
2.322
2.323 for access_location in self.alias_index[accessor_location]:
2.324 - location, name, attrnames, access_number = access_location
2.325 - invocation = self.reference_invocations.get(access_location)
2.326 + attrnames = access_location.attrnames
2.327 + invocation = self.reference_invocations.has_key(access_location)
2.328
2.329 attrnames = attrnames and attrnames.split(".")
2.330 remaining = attrnames and len(attrnames) > 1
2.331 @@ -1755,7 +1711,7 @@
2.332 # Attempt to refine the types using initialised names or
2.333 # accessors.
2.334
2.335 - attrs = self.get_initialised_name(access_location)
2.336 + attrs = self.get_initialised_access(access_location)
2.337
2.338 if attrs:
2.339 provider_attrs = self.convert_invocation_providers(attrs, invocation)
2.340 @@ -1826,8 +1782,8 @@
2.341 refs = set()
2.342
2.343 for access_location in self.alias_index[accessor_location]:
2.344 - location, name, attrnames, access_number = access_location
2.345 - invocation = self.reference_invocations.get(access_location)
2.346 + attrnames = access_location.attrnames
2.347 + invocation = self.reference_invocations.has_key(access_location)
2.348
2.349 attrnames = attrnames and attrnames.split(".")
2.350 remaining = attrnames and len(attrnames) > 1
2.351 @@ -1853,7 +1809,7 @@
2.352
2.353 # Obtain initialiser information.
2.354
2.355 - attrs = self.get_initialised_name(access_location)
2.356 + attrs = self.get_initialised_access(access_location)
2.357
2.358 if attrs:
2.359 provider_attrs = self.convert_invocation_providers(attrs, invocation)
2.360 @@ -1923,8 +1879,10 @@
2.361 if not versions:
2.362 return
2.363
2.364 + path, name = key
2.365 +
2.366 for version in versions.keys():
2.367 - location = key + (None, version)
2.368 + location = Location(path, name, None, version)
2.369 l = init_item(self.alias_index_rev, location, set)
2.370 l.add(access_location)
2.371
2.372 @@ -2031,22 +1989,27 @@
2.373 else:
2.374 return refs
2.375
2.376 - def get_initialised_name(self, access_location):
2.377 + def get_initialised_name(self, location):
2.378
2.379 """
2.380 - Return references for any initialised names at 'access_location', or
2.381 - None if no such references exist.
2.382 + Return references for any initialised names at 'location', or None if no
2.383 + such references exist.
2.384 """
2.385
2.386 - path, name, attrnames, version = access_location
2.387 -
2.388 - # Use initialiser information, if available.
2.389 -
2.390 - initialisers = self.importer.all_initialised_names.get((path, name))
2.391 - if initialisers and initialisers.has_key(version):
2.392 - return [initialisers[version]]
2.393 + ref = self.initialised_names.get(location)
2.394 + return ref and [ref] or None
2.395 +
2.396 + def get_initialised_access(self, location):
2.397 +
2.398 + """
2.399 + Return references for any initialised names supplying the given access
2.400 + 'location', or None if no such references exist.
2.401 + """
2.402 +
2.403 + if location.access_number is not None:
2.404 + return self.initialised_accesses.get(location)
2.405 else:
2.406 - return None
2.407 + return self.get_initialised_name(location)
2.408
2.409 def get_accessor_references(self, access_location):
2.410
2.411 @@ -2112,7 +2075,7 @@
2.412 # Instance-only and module types support only their own kinds as
2.413 # accessors.
2.414
2.415 - path, name, attrnames, version = location
2.416 + path, name = location.path, location.name
2.417
2.418 if invocations:
2.419 class_only_types = self.filter_for_invocations(class_types, invocations)
2.420 @@ -2334,7 +2297,7 @@
2.421
2.422 const_access = self.const_accesses_rev.get(location)
2.423
2.424 - path, name, attrnames, version = location
2.425 + path, name, attrnames = location.path, location.name, location.attrnames
2.426 remaining = attrnames.split(".")
2.427 attrname = remaining[0]
2.428
4.1 --- a/translator.py Thu Mar 30 18:48:36 2017 +0200
4.2 +++ b/translator.py Thu Mar 30 19:41:24 2017 +0200
4.3 @@ -19,7 +19,7 @@
4.4 this program. If not, see <http://www.gnu.org/licenses/>.
4.5 """
4.6
4.7 -from common import CommonModule, CommonOutput, \
4.8 +from common import AccessLocation, CommonModule, CommonOutput, Location, \
4.9 first, get_builtin_class, init_item, is_newer, \
4.10 predefined_constants
4.11 from encoders import encode_access_instruction, encode_access_instruction_arg, \
4.12 @@ -648,7 +648,7 @@
4.13 # Determine whether any deduced references refer to the accessed
4.14 # attribute.
4.15
4.16 - path, accessor_name, attrnames, access_number = location
4.17 + attrnames = location.attrnames
4.18 attrnames = attrnames and attrnames.split(".")
4.19 remaining = attrnames and len(attrnames) > 1
4.20
4.21 @@ -695,7 +695,7 @@
4.22 attrnames = attrnames and ".".join(self.attrs)
4.23 access_number = self.get_access_number(path, name, attrnames)
4.24 self.update_access_number(path, name, attrnames)
4.25 - return (path, name, attrnames, access_number)
4.26 + return AccessLocation(path, name, attrnames, access_number)
4.27
4.28 def get_access_number(self, path, name, attrnames):
4.29 access = name, attrnames
4.30 @@ -723,9 +723,9 @@
4.31 # Get the location used by the deducer and optimiser and find any
4.32 # recorded accessor.
4.33
4.34 - access_number = self.get_accessor_number(path, name)
4.35 + version = self.get_accessor_number(path, name)
4.36 self.update_accessor_number(path, name)
4.37 - return (path, name, None, access_number)
4.38 + return Location(path, name, None, version)
4.39
4.40 def get_accessor_number(self, path, name):
4.41 if self.attr_accessors.has_key(path) and self.attr_accessors[path].has_key(name):
4.42 @@ -1127,7 +1127,7 @@
4.43
4.44 else:
4.45 if location:
4.46 - path, name, attrnames, access_number = location
4.47 + attrnames = location.attrnames
4.48 attrname = attrnames and attrnames.rsplit(".", 1)[-1]
4.49
4.50 # Determine any common aspects of any attribute.