1.1 --- a/common.py Mon Mar 13 18:45:41 2017 +0100
1.2 +++ b/common.py Thu Mar 16 18:13:34 2017 +0100
1.3 @@ -562,7 +562,6 @@
1.4
1.5 # <t0> = {n.list}
1.6 # <t1> = <t0>.__iter__()
1.7 - # <i0> = <t1>.next
1.8
1.9 compiler.ast.Assign(
1.10 [compiler.ast.AssName(t0, "OP_ASSIGN")],
1.11 @@ -574,13 +573,9 @@
1.12 compiler.ast.Getattr(compiler.ast.Name(t0), "__iter__"),
1.13 [])),
1.14
1.15 - compiler.ast.Assign(
1.16 - [compiler.ast.AssName(i0, "OP_ASSIGN")],
1.17 - compiler.ast.Getattr(compiler.ast.Name(t1), "next")),
1.18 -
1.19 # try:
1.20 # while True:
1.21 - # <var>... = <next>()
1.22 + # <var>... = <t1>.next()
1.23 # ...
1.24 # except StopIteration:
1.25 # pass
1.26 @@ -592,7 +587,7 @@
1.27 compiler.ast.Assign(
1.28 [n.assign],
1.29 compiler.ast.CallFunc(
1.30 - compiler.ast.Name(i0),
1.31 + compiler.ast.Getattr(compiler.ast.Name(t1), "next"),
1.32 []
1.33 )),
1.34 n.body]),
2.1 --- a/deducer.py Mon Mar 13 18:45:41 2017 +0100
2.2 +++ b/deducer.py Thu Mar 16 18:13:34 2017 +0100
2.3 @@ -146,7 +146,6 @@
2.4 self.init_accessors()
2.5 self.init_accesses()
2.6 self.init_aliases()
2.7 - self.init_alias_network()
2.8 self.modify_mutated_attributes()
2.9 self.identify_references()
2.10 self.classify_accessors()
2.11 @@ -969,6 +968,15 @@
2.12 for accessor_location, access_locations in self.alias_index.items():
2.13 self.update_aliases(accessor_location, access_locations)
2.14
2.15 + # Get accesses affected by aliases.
2.16 +
2.17 + self.alias_index_rev = {}
2.18 +
2.19 + for accessor_location, access_locations in self.alias_index.items():
2.20 + for access_location in access_locations:
2.21 + init_item(self.alias_index_rev, access_location, set)
2.22 + self.alias_index_rev[access_location].add(accessor_location)
2.23 +
2.24 def update_aliases(self, accessor_location, access_locations, visited=None):
2.25
2.26 """
2.27 @@ -1019,21 +1027,6 @@
2.28 self.alias_index[accessor_location] = updated_locations
2.29 return updated_locations
2.30
2.31 - def init_alias_network(self):
2.32 -
2.33 - """
2.34 - Initialise a network of aliases, their initialising accesses, and the
2.35 - accessors supporting those accesses.
2.36 - """
2.37 -
2.38 - self.alias_network = {}
2.39 - self.alias_network.update(self.alias_index)
2.40 -
2.41 - for accessor_location, access_locations in self.alias_index.items():
2.42 - for access_location in access_locations:
2.43 - if not self.alias_network.has_key(access_location):
2.44 - self.alias_network[access_location] = self.get_accessors_for_access(access_location)
2.45 -
2.46 # Attribute mutation for types.
2.47
2.48 def modify_mutated_attributes(self):
2.49 @@ -1365,17 +1358,39 @@
2.50 self.const_accesses[original_location] = access_location
2.51 self.const_accesses_rev[access_location] = original_location
2.52
2.53 - # Aliased name definitions. All aliases with usage will have been
2.54 - # defined, but they may be refined according to referenced accesses.
2.55 -
2.56 - for location in order_dependencies_partial(self.alias_network):
2.57 - if self.alias_index.has_key(location):
2.58 - self.record_types_for_alias(location)
2.59 -
2.60 - # Update accesses employing aliases.
2.61 -
2.62 - for access_location in alias_accesses:
2.63 - self.record_types_for_access(access_location, self.access_index[access_location])
2.64 + # Propagate alias-related information.
2.65 +
2.66 + affected_aliases = set(self.alias_index.keys())
2.67 +
2.68 + while True:
2.69 +
2.70 + # Aliased name definitions. All aliases with usage will have been
2.71 + # defined, but they may be refined according to referenced accesses.
2.72 +
2.73 + updated_aliases = set()
2.74 +
2.75 + for location in affected_aliases:
2.76 + if self.record_types_for_alias(location):
2.77 + updated_aliases.add(location)
2.78 +
2.79 + # Update accesses employing aliases.
2.80 +
2.81 + updated_accesses = set()
2.82 +
2.83 + for access_location in alias_accesses:
2.84 + if self.record_types_for_access(access_location, self.access_index[access_location]):
2.85 + updated_accesses.add(access_location)
2.86 +
2.87 + # Update aliases for updated accesses.
2.88 +
2.89 + affected_aliases = set()
2.90 +
2.91 + for access_location in updated_accesses:
2.92 + if self.alias_index_rev.has_key(access_location):
2.93 + affected_aliases.update(self.alias_index_rev[access_location])
2.94 +
2.95 + if not affected_aliases:
2.96 + break
2.97
2.98 def constrain_types(self, path, class_types, instance_types, module_types):
2.99
2.100 @@ -1530,12 +1545,12 @@
2.101
2.102 """
2.103 Define types for the 'access_location' associated with the given
2.104 - 'accessor_locations'.
2.105 + 'accessor_locations'. Return whether referenced attributes were updated.
2.106 """
2.107
2.108 attrname = get_attrname_from_location(access_location)
2.109 if not attrname:
2.110 - return
2.111 + return False
2.112
2.113 invocation = access_location in self.reference_invocations
2.114 assignment = access_location in self.reference_assignments
2.115 @@ -1549,6 +1564,8 @@
2.116
2.117 constrained = True
2.118
2.119 + old_referenced_attrs = self.referenced_attrs.get(access_location)
2.120 +
2.121 for location in accessor_locations:
2.122
2.123 # Remember accesses employing aliases.
2.124 @@ -1576,6 +1593,10 @@
2.125 self.init_access_details(access_location)
2.126 self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained)
2.127
2.128 + # Return whether the referenced attributes have changed.
2.129 +
2.130 + return old_referenced_attrs != self.referenced_attrs.get(access_location)
2.131 +
2.132 def record_types_for_usage(self, accessor_location, usage):
2.133
2.134 """
2.135 @@ -1626,6 +1647,7 @@
2.136
2.137 """
2.138 Define types for the 'accessor_location' not having associated usage.
2.139 + Return whether the types were updated.
2.140 """
2.141
2.142 have_access = self.provider_class_types.has_key(accessor_location)
2.143 @@ -1666,7 +1688,7 @@
2.144 # need to be traversed first.
2.145
2.146 if remaining:
2.147 - return
2.148 + return False
2.149
2.150 # Alias references an attribute access.
2.151
2.152 @@ -1680,7 +1702,7 @@
2.153 # to refine the alias's types.
2.154
2.155 if not attrs:
2.156 - return
2.157 + return False
2.158
2.159 # Invocations converting class accessors to instances do not
2.160 # change the nature of class providers.
2.161 @@ -1694,7 +1716,7 @@
2.162 # the defined accessor types.
2.163
2.164 if function_types or var_types:
2.165 - return
2.166 + return False
2.167
2.168 class_types = set(provider_class_types).intersection(class_types)
2.169 instance_types = set(provider_instance_types).intersection(instance_types)
2.170 @@ -1758,7 +1780,7 @@
2.171 # refine the defined accessor types.
2.172
2.173 else:
2.174 - return
2.175 + return False
2.176
2.177 # Record refined type details for the alias as an accessor.
2.178
2.179 @@ -1766,11 +1788,16 @@
2.180 self.update_provider_types(accessor_location, new_provider_class_types, new_provider_instance_types, new_provider_module_types)
2.181 self.update_accessor_types(accessor_location, new_accessor_class_types, new_accessor_instance_types, new_accessor_module_types)
2.182
2.183 + return new_accessor_class_types != accessor_class_types or \
2.184 + new_accessor_instance_types != accessor_instance_types or \
2.185 + new_accessor_module_types != accessor_module_types
2.186 +
2.187 # Without an access, attempt to identify references for the alias.
2.188 # Invocations convert classes to instances and also attempt to find
2.189 # return value information.
2.190
2.191 else:
2.192 + old_refs = self.referenced_objects.get(accessor_location)
2.193 refs = set()
2.194
2.195 for access_location in self.alias_index[accessor_location]:
2.196 @@ -1791,7 +1818,7 @@
2.197 # need to be traversed first.
2.198
2.199 if remaining:
2.200 - return
2.201 + return False
2.202
2.203 # Alias references an attribute access.
2.204
2.205 @@ -1827,6 +1854,8 @@
2.206
2.207 self.referenced_objects[accessor_location] = refs
2.208
2.209 + return old_refs != refs
2.210 +
2.211 def get_references_for_access(self, access_location):
2.212
2.213 "Return the references identified for 'access_location'."
2.214 @@ -1864,9 +1893,7 @@
2.215 if ref.has_kind("<class>"):
2.216 return ref
2.217 elif ref.has_kind("<function>"):
2.218 - refs = self.importer.all_return_values.get(ref.get_origin())
2.219 - if refs and len(refs) == 1:
2.220 - return first(refs)
2.221 + return self.convert_function_invocation(ref)
2.222
2.223 return Reference("<var>")
2.224
2.225 @@ -1887,9 +1914,19 @@
2.226 if ref.has_kind("<class>"):
2.227 return ref.instance_of()
2.228 elif ref.has_kind("<function>"):
2.229 - refs = self.importer.all_return_values.get(ref.get_origin())
2.230 - if refs and len(refs) == 1:
2.231 - return first(refs)
2.232 + return self.convert_function_invocation(ref)
2.233 +
2.234 + return Reference("<var>")
2.235 +
2.236 + def convert_function_invocation(self, ref):
2.237 +
2.238 + "Convert the function 'ref' to its return value reference."
2.239 +
2.240 + initialised_names = self.importer.all_initialised_names.get((ref.get_origin(), "$return"))
2.241 + if initialised_names:
2.242 + refs = set(initialised_names.values())
2.243 + if len(refs) == 1:
2.244 + return first(refs)
2.245
2.246 return Reference("<var>")
2.247
3.1 --- a/importer.py Mon Mar 13 18:45:41 2017 +0100
3.2 +++ b/importer.py Thu Mar 16 18:13:34 2017 +0100
3.3 @@ -116,10 +116,6 @@
3.4 self.all_constants = {}
3.5 self.all_constant_values = {}
3.6
3.7 - # Return values.
3.8 -
3.9 - self.all_return_values = {}
3.10 -
3.11 self.make_cache()
3.12
3.13 def give_warning(self, name):
4.1 --- a/inspector.py Mon Mar 13 18:45:41 2017 +0100
4.2 +++ b/inspector.py Thu Mar 16 18:13:34 2017 +0100
4.3 @@ -1487,7 +1487,7 @@
4.4 "Record the given return 'expr'."
4.5
4.6 path = self.get_namespace_path()
4.7 - init_item(self.return_values, path, set)
4.8 - self.return_values[path].add(expr)
4.9 + init_item(self.return_values, path, list)
4.10 + self.return_values[path].append(expr)
4.11
4.12 # vim: tabstop=4 expandtab shiftwidth=4
5.1 --- a/modules.py Mon Mar 13 18:45:41 2017 +0100
5.2 +++ b/modules.py Thu Mar 16 18:13:34 2017 +0100
5.3 @@ -115,7 +115,6 @@
5.4 self.propagate_name_references()
5.5 self.propagate_attr_accesses()
5.6 self.propagate_constants()
5.7 - self.propagate_return_values()
5.8
5.9 def unpropagate(self):
5.10
5.11 @@ -152,7 +151,6 @@
5.12 remove_items(self.importer.all_attr_access_modifiers, self.attr_access_modifiers)
5.13 remove_items(self.importer.all_constants, self.constants)
5.14 remove_items(self.importer.all_constant_values, self.constant_values)
5.15 - remove_items(self.importer.all_return_values, self.return_values)
5.16
5.17 # Remove this module's objects from the importer. Objects are
5.18 # automatically propagated when defined.
5.19 @@ -222,12 +220,6 @@
5.20 self.importer.all_instance_attrs[name] = self.instance_attrs.get(name) or {}
5.21 self.importer.all_instance_attr_constants[name] = self.instance_attr_constants.get(name) or {}
5.22
5.23 - def propagate_return_values(self):
5.24 -
5.25 - "Propagate return values for the module."
5.26 -
5.27 - self.importer.all_return_values.update(self.return_values)
5.28 -
5.29 def set_object(self, name, value=None):
5.30
5.31 "Set an object with the given 'name' and the given 'value'."
5.32 @@ -410,7 +402,6 @@
5.33 self._get_constant_literals(f)
5.34 self._get_constant_values(f)
5.35 self._get_exception_namespaces(f)
5.36 - self._get_return_values(f)
5.37
5.38 finally:
5.39 f.close()
5.40 @@ -631,15 +622,6 @@
5.41 self.exception_namespaces = value and set(value.split(", ")) or set()
5.42 f.readline()
5.43
5.44 - def _get_return_values(self, f):
5.45 - f.readline() # "return values:"
5.46 - line = f.readline().rstrip()
5.47 - while line:
5.48 - path, values = self._get_fields(line)
5.49 - values = values.split(", ")
5.50 - self.return_values[path] = map(decode_reference, values)
5.51 - line = f.readline().rstrip()
5.52 -
5.53 # Generic parsing methods.
5.54
5.55 def from_lines(self, f, d):
5.56 @@ -897,13 +879,6 @@
5.57 paths.sort()
5.58 print >>f, ", ".join(paths)
5.59
5.60 - print >>f
5.61 - print >>f, "return values:"
5.62 - paths = self.return_values.keys()
5.63 - paths.sort()
5.64 - for path in paths:
5.65 - print >>f, path, ", ".join(map(str, self.return_values[path]))
5.66 -
5.67 finally:
5.68 f.close()
5.69
6.1 --- a/resolving.py Mon Mar 13 18:45:41 2017 +0100
6.2 +++ b/resolving.py Thu Mar 16 18:13:34 2017 +0100
6.3 @@ -259,26 +259,26 @@
6.4
6.5 "Resolve return values using name references."
6.6
6.7 - return_values = {}
6.8 -
6.9 # Get the return values from each namespace.
6.10
6.11 for path, values in self.return_values.items():
6.12 - l = set()
6.13 +
6.14 + # Resolve each return value provided by the scope.
6.15
6.16 - for value in values:
6.17 - if not value:
6.18 - ref = None
6.19 - else:
6.20 - ref, aliased_name = self.resolve_reference(path, value)
6.21 + initialised_names = {}
6.22 + aliased_names = {}
6.23
6.24 - l.add(ref or Reference("<var>"))
6.25 -
6.26 - return_values[path] = l
6.27 + for i, name_ref in enumerate(values):
6.28 + initialised_ref, aliased_name = self.resolve_reference(path, name_ref)
6.29 + if initialised_ref:
6.30 + initialised_names[i] = initialised_ref
6.31 + if aliased_name:
6.32 + aliased_names[i] = aliased_name
6.33
6.34 - # Replace the original values.
6.35 -
6.36 - self.return_values = return_values
6.37 + if initialised_names:
6.38 + self.initialised_names[(path, "$return")] = initialised_names
6.39 + if aliased_names:
6.40 + self.aliased_names[(path, "$return")] = aliased_names
6.41
6.42 def resolve_reference(self, path, name_ref):
6.43
7.1 --- a/translator.py Mon Mar 13 18:45:41 2017 +0100
7.2 +++ b/translator.py Thu Mar 16 18:13:34 2017 +0100
7.3 @@ -647,16 +647,11 @@
7.4 access_location = self.deducer.const_accesses.get(location)
7.5 return self.deducer.reference_invocations_unsuitable.get(access_location or location)
7.6
7.7 - def get_accessor_kinds(self, locations):
7.8 -
7.9 - "Return the accessor kinds for 'locations'."
7.10 -
7.11 - accessor_kinds = set()
7.12 - for location in locations:
7.13 - kinds = self.deducer.accessor_kinds.get(location)
7.14 - if kinds:
7.15 - accessor_kinds.update(kinds)
7.16 - return accessor_kinds
7.17 + def get_accessor_kinds(self, location):
7.18 +
7.19 + "Return the accessor kinds for 'location'."
7.20 +
7.21 + return self.deducer.accessor_kinds.get(location)
7.22
7.23 def get_access_location(self, name, attrnames=None):
7.24
7.25 @@ -1026,7 +1021,6 @@
7.26
7.27 objpath = expr.get_origin()
7.28 location = expr.access_location()
7.29 - locations = expr.access_locations()
7.30
7.31 # Identified target details.
7.32
7.33 @@ -1081,8 +1075,7 @@
7.34
7.35 context_required = self.is_method(objpath)
7.36
7.37 - accessor_kinds = location and self.get_accessor_kinds([location]) or \
7.38 - locations and self.get_accessor_kinds(locations)
7.39 + accessor_kinds = location and self.get_accessor_kinds(location)
7.40
7.41 instance_accessor = accessor_kinds and \
7.42 len(accessor_kinds) == 1 and \
7.43 @@ -1386,11 +1379,6 @@
7.44 ref, paths = self.importer.get_module(self.name).special[n.name]
7.45 return TrResolvedNameRef(n.name, ref)
7.46
7.47 - # Temporary names are output program locals.
7.48 -
7.49 - elif n.name.startswith("$t"):
7.50 - return TrResolvedNameRef(n.name, Reference("<var>"), expr=expr)
7.51 -
7.52 # Get the appropriate name for the name reference, using the same method
7.53 # as in the inspector.
7.54
7.55 @@ -1453,8 +1441,7 @@
7.56
7.57 name_ref = TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global,
7.58 location=location)
7.59 - result = self.get_aliases(name_ref)
7.60 - return result or name_ref
7.61 + return not expr and self.get_aliases(name_ref) or name_ref
7.62
7.63 def get_aliases(self, name_ref):
7.64
7.65 @@ -1462,21 +1449,9 @@
7.66
7.67 location = name_ref.access_location()
7.68
7.69 - accessor_locations = location and self.deducer.get_accessors_for_access(location)
7.70 - alias_refs = set()
7.71 - access_locations = set()
7.72 -
7.73 - if accessor_locations:
7.74 - for accessor_location in accessor_locations:
7.75 - aliased_accesses = self.deducer.alias_index.get(accessor_location)
7.76 - if not aliased_accesses:
7.77 - continue
7.78 - access_locations.update(aliased_accesses)
7.79 - refs = self.deducer.referenced_objects.get(accessor_location)
7.80 - if refs:
7.81 - alias_refs.update(refs)
7.82 -
7.83 - return AliasResult(name_ref, alias_refs, access_locations)
7.84 + refs = self.deducer.referenced_objects.get(location)
7.85 + refs = refs or self.deducer.accessor_all_types.get(location)
7.86 + return AliasResult(name_ref, refs or set(), location)
7.87
7.88 def make_volatile(self, name):
7.89
8.1 --- a/transresults.py Mon Mar 13 18:45:41 2017 +0100
8.2 +++ b/transresults.py Thu Mar 16 18:13:34 2017 +0100
8.3 @@ -70,13 +70,18 @@
8.4 def access_location(self):
8.5 return self.location
8.6
8.7 - def access_locations(self):
8.8 - return self.location and [self.location]
8.9 -
8.10 def __str__(self):
8.11
8.12 "Return an output representation of the referenced name."
8.13
8.14 + # Temporary names are output program locals.
8.15 +
8.16 + if self.name.startswith("$t"):
8.17 + if self.expr:
8.18 + return "%s = %s" % (encode_path(self.name), self.expr)
8.19 + else:
8.20 + return encode_path(self.name)
8.21 +
8.22 # For sources, any identified static origin will be constant and thus
8.23 # usable directly. For targets, no constant should be assigned and thus
8.24 # the alias (or any plain name) will be used.
8.25 @@ -179,9 +184,6 @@
8.26 def access_location(self):
8.27 return self.location
8.28
8.29 - def access_locations(self):
8.30 - return self.location and [self.location]
8.31 -
8.32 def context(self):
8.33 return self.context_identity
8.34
8.35 @@ -209,11 +211,11 @@
8.36
8.37 "An alias for other values."
8.38
8.39 - def __init__(self, name_ref, refs, locations):
8.40 + def __init__(self, name_ref, refs, location):
8.41 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name())
8.42 self.name_ref = name_ref
8.43 self.refs = refs
8.44 - self.locations = locations
8.45 + self.location = location
8.46
8.47 def references(self):
8.48 ref = self.name_ref.reference()
8.49 @@ -224,10 +226,7 @@
8.50 return len(refs) == 1 and first(refs) or None
8.51
8.52 def access_location(self):
8.53 - return len(self.locations) == 1 and first(self.locations) or None
8.54 -
8.55 - def access_locations(self):
8.56 - return self.locations
8.57 + return self.location
8.58
8.59 def get_name(self):
8.60 ref = self.reference()