# HG changeset patch # User Paul Boddie # Date 1489684414 -3600 # Node ID 5e4ac6b12b8f87b2864b49e3be6425503b3089a6 # Parent 21356f3943f17cdfe0a6bd8582f2c24cdef9adea# Parent b7de08fc69b09d18630e97ddf469a3910ffee3bb Merged changes from the default branch. diff -r 21356f3943f1 -r 5e4ac6b12b8f common.py --- a/common.py Mon Mar 13 18:45:41 2017 +0100 +++ b/common.py Thu Mar 16 18:13:34 2017 +0100 @@ -562,7 +562,6 @@ # = {n.list} # = .__iter__() - # = .next compiler.ast.Assign( [compiler.ast.AssName(t0, "OP_ASSIGN")], @@ -574,13 +573,9 @@ compiler.ast.Getattr(compiler.ast.Name(t0), "__iter__"), [])), - compiler.ast.Assign( - [compiler.ast.AssName(i0, "OP_ASSIGN")], - compiler.ast.Getattr(compiler.ast.Name(t1), "next")), - # try: # while True: - # ... = () + # ... = .next() # ... # except StopIteration: # pass @@ -592,7 +587,7 @@ compiler.ast.Assign( [n.assign], compiler.ast.CallFunc( - compiler.ast.Name(i0), + compiler.ast.Getattr(compiler.ast.Name(t1), "next"), [] )), n.body]), diff -r 21356f3943f1 -r 5e4ac6b12b8f deducer.py --- a/deducer.py Mon Mar 13 18:45:41 2017 +0100 +++ b/deducer.py Thu Mar 16 18:13:34 2017 +0100 @@ -146,7 +146,6 @@ self.init_accessors() self.init_accesses() self.init_aliases() - self.init_alias_network() self.modify_mutated_attributes() self.identify_references() self.classify_accessors() @@ -969,6 +968,15 @@ for accessor_location, access_locations in self.alias_index.items(): self.update_aliases(accessor_location, access_locations) + # Get accesses affected by aliases. + + self.alias_index_rev = {} + + for accessor_location, access_locations in self.alias_index.items(): + for access_location in access_locations: + init_item(self.alias_index_rev, access_location, set) + self.alias_index_rev[access_location].add(accessor_location) + def update_aliases(self, accessor_location, access_locations, visited=None): """ @@ -1019,21 +1027,6 @@ self.alias_index[accessor_location] = updated_locations return updated_locations - def init_alias_network(self): - - """ - Initialise a network of aliases, their initialising accesses, and the - accessors supporting those accesses. - """ - - self.alias_network = {} - self.alias_network.update(self.alias_index) - - for accessor_location, access_locations in self.alias_index.items(): - for access_location in access_locations: - if not self.alias_network.has_key(access_location): - self.alias_network[access_location] = self.get_accessors_for_access(access_location) - # Attribute mutation for types. def modify_mutated_attributes(self): @@ -1365,17 +1358,39 @@ self.const_accesses[original_location] = access_location self.const_accesses_rev[access_location] = original_location - # Aliased name definitions. All aliases with usage will have been - # defined, but they may be refined according to referenced accesses. - - for location in order_dependencies_partial(self.alias_network): - if self.alias_index.has_key(location): - self.record_types_for_alias(location) - - # Update accesses employing aliases. - - for access_location in alias_accesses: - self.record_types_for_access(access_location, self.access_index[access_location]) + # Propagate alias-related information. + + affected_aliases = set(self.alias_index.keys()) + + while True: + + # Aliased name definitions. All aliases with usage will have been + # defined, but they may be refined according to referenced accesses. + + updated_aliases = set() + + for location in affected_aliases: + if self.record_types_for_alias(location): + updated_aliases.add(location) + + # Update accesses employing aliases. + + updated_accesses = set() + + for access_location in alias_accesses: + if self.record_types_for_access(access_location, self.access_index[access_location]): + updated_accesses.add(access_location) + + # Update aliases for updated accesses. + + affected_aliases = set() + + for access_location in updated_accesses: + if self.alias_index_rev.has_key(access_location): + affected_aliases.update(self.alias_index_rev[access_location]) + + if not affected_aliases: + break def constrain_types(self, path, class_types, instance_types, module_types): @@ -1530,12 +1545,12 @@ """ Define types for the 'access_location' associated with the given - 'accessor_locations'. + 'accessor_locations'. Return whether referenced attributes were updated. """ attrname = get_attrname_from_location(access_location) if not attrname: - return + return False invocation = access_location in self.reference_invocations assignment = access_location in self.reference_assignments @@ -1549,6 +1564,8 @@ constrained = True + old_referenced_attrs = self.referenced_attrs.get(access_location) + for location in accessor_locations: # Remember accesses employing aliases. @@ -1576,6 +1593,10 @@ self.init_access_details(access_location) self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained) + # Return whether the referenced attributes have changed. + + return old_referenced_attrs != self.referenced_attrs.get(access_location) + def record_types_for_usage(self, accessor_location, usage): """ @@ -1626,6 +1647,7 @@ """ Define types for the 'accessor_location' not having associated usage. + Return whether the types were updated. """ have_access = self.provider_class_types.has_key(accessor_location) @@ -1666,7 +1688,7 @@ # need to be traversed first. if remaining: - return + return False # Alias references an attribute access. @@ -1680,7 +1702,7 @@ # to refine the alias's types. if not attrs: - return + return False # Invocations converting class accessors to instances do not # change the nature of class providers. @@ -1694,7 +1716,7 @@ # the defined accessor types. if function_types or var_types: - return + return False class_types = set(provider_class_types).intersection(class_types) instance_types = set(provider_instance_types).intersection(instance_types) @@ -1758,7 +1780,7 @@ # refine the defined accessor types. else: - return + return False # Record refined type details for the alias as an accessor. @@ -1766,11 +1788,16 @@ self.update_provider_types(accessor_location, new_provider_class_types, new_provider_instance_types, new_provider_module_types) self.update_accessor_types(accessor_location, new_accessor_class_types, new_accessor_instance_types, new_accessor_module_types) + return new_accessor_class_types != accessor_class_types or \ + new_accessor_instance_types != accessor_instance_types or \ + new_accessor_module_types != accessor_module_types + # Without an access, attempt to identify references for the alias. # Invocations convert classes to instances and also attempt to find # return value information. else: + old_refs = self.referenced_objects.get(accessor_location) refs = set() for access_location in self.alias_index[accessor_location]: @@ -1791,7 +1818,7 @@ # need to be traversed first. if remaining: - return + return False # Alias references an attribute access. @@ -1827,6 +1854,8 @@ self.referenced_objects[accessor_location] = refs + return old_refs != refs + def get_references_for_access(self, access_location): "Return the references identified for 'access_location'." @@ -1864,9 +1893,7 @@ if ref.has_kind(""): return ref elif ref.has_kind(""): - refs = self.importer.all_return_values.get(ref.get_origin()) - if refs and len(refs) == 1: - return first(refs) + return self.convert_function_invocation(ref) return Reference("") @@ -1887,9 +1914,19 @@ if ref.has_kind(""): return ref.instance_of() elif ref.has_kind(""): - refs = self.importer.all_return_values.get(ref.get_origin()) - if refs and len(refs) == 1: - return first(refs) + return self.convert_function_invocation(ref) + + return Reference("") + + def convert_function_invocation(self, ref): + + "Convert the function 'ref' to its return value reference." + + initialised_names = self.importer.all_initialised_names.get((ref.get_origin(), "$return")) + if initialised_names: + refs = set(initialised_names.values()) + if len(refs) == 1: + return first(refs) return Reference("") diff -r 21356f3943f1 -r 5e4ac6b12b8f importer.py --- a/importer.py Mon Mar 13 18:45:41 2017 +0100 +++ b/importer.py Thu Mar 16 18:13:34 2017 +0100 @@ -116,10 +116,6 @@ self.all_constants = {} self.all_constant_values = {} - # Return values. - - self.all_return_values = {} - self.make_cache() def give_warning(self, name): diff -r 21356f3943f1 -r 5e4ac6b12b8f inspector.py --- a/inspector.py Mon Mar 13 18:45:41 2017 +0100 +++ b/inspector.py Thu Mar 16 18:13:34 2017 +0100 @@ -1487,7 +1487,7 @@ "Record the given return 'expr'." path = self.get_namespace_path() - init_item(self.return_values, path, set) - self.return_values[path].add(expr) + init_item(self.return_values, path, list) + self.return_values[path].append(expr) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 21356f3943f1 -r 5e4ac6b12b8f modules.py --- a/modules.py Mon Mar 13 18:45:41 2017 +0100 +++ b/modules.py Thu Mar 16 18:13:34 2017 +0100 @@ -115,7 +115,6 @@ self.propagate_name_references() self.propagate_attr_accesses() self.propagate_constants() - self.propagate_return_values() def unpropagate(self): @@ -152,7 +151,6 @@ remove_items(self.importer.all_attr_access_modifiers, self.attr_access_modifiers) remove_items(self.importer.all_constants, self.constants) remove_items(self.importer.all_constant_values, self.constant_values) - remove_items(self.importer.all_return_values, self.return_values) # Remove this module's objects from the importer. Objects are # automatically propagated when defined. @@ -222,12 +220,6 @@ self.importer.all_instance_attrs[name] = self.instance_attrs.get(name) or {} self.importer.all_instance_attr_constants[name] = self.instance_attr_constants.get(name) or {} - def propagate_return_values(self): - - "Propagate return values for the module." - - self.importer.all_return_values.update(self.return_values) - def set_object(self, name, value=None): "Set an object with the given 'name' and the given 'value'." @@ -410,7 +402,6 @@ self._get_constant_literals(f) self._get_constant_values(f) self._get_exception_namespaces(f) - self._get_return_values(f) finally: f.close() @@ -631,15 +622,6 @@ self.exception_namespaces = value and set(value.split(", ")) or set() f.readline() - def _get_return_values(self, f): - f.readline() # "return values:" - line = f.readline().rstrip() - while line: - path, values = self._get_fields(line) - values = values.split(", ") - self.return_values[path] = map(decode_reference, values) - line = f.readline().rstrip() - # Generic parsing methods. def from_lines(self, f, d): @@ -897,13 +879,6 @@ paths.sort() print >>f, ", ".join(paths) - print >>f - print >>f, "return values:" - paths = self.return_values.keys() - paths.sort() - for path in paths: - print >>f, path, ", ".join(map(str, self.return_values[path])) - finally: f.close() diff -r 21356f3943f1 -r 5e4ac6b12b8f resolving.py --- a/resolving.py Mon Mar 13 18:45:41 2017 +0100 +++ b/resolving.py Thu Mar 16 18:13:34 2017 +0100 @@ -259,26 +259,26 @@ "Resolve return values using name references." - return_values = {} - # Get the return values from each namespace. for path, values in self.return_values.items(): - l = set() + + # Resolve each return value provided by the scope. - for value in values: - if not value: - ref = None - else: - ref, aliased_name = self.resolve_reference(path, value) + initialised_names = {} + aliased_names = {} - l.add(ref or Reference("")) - - return_values[path] = l + for i, name_ref in enumerate(values): + initialised_ref, aliased_name = self.resolve_reference(path, name_ref) + if initialised_ref: + initialised_names[i] = initialised_ref + if aliased_name: + aliased_names[i] = aliased_name - # Replace the original values. - - self.return_values = return_values + if initialised_names: + self.initialised_names[(path, "$return")] = initialised_names + if aliased_names: + self.aliased_names[(path, "$return")] = aliased_names def resolve_reference(self, path, name_ref): diff -r 21356f3943f1 -r 5e4ac6b12b8f translator.py --- a/translator.py Mon Mar 13 18:45:41 2017 +0100 +++ b/translator.py Thu Mar 16 18:13:34 2017 +0100 @@ -647,16 +647,11 @@ access_location = self.deducer.const_accesses.get(location) return self.deducer.reference_invocations_unsuitable.get(access_location or location) - def get_accessor_kinds(self, locations): - - "Return the accessor kinds for 'locations'." - - accessor_kinds = set() - for location in locations: - kinds = self.deducer.accessor_kinds.get(location) - if kinds: - accessor_kinds.update(kinds) - return accessor_kinds + def get_accessor_kinds(self, location): + + "Return the accessor kinds for 'location'." + + return self.deducer.accessor_kinds.get(location) def get_access_location(self, name, attrnames=None): @@ -1026,7 +1021,6 @@ objpath = expr.get_origin() location = expr.access_location() - locations = expr.access_locations() # Identified target details. @@ -1081,8 +1075,7 @@ context_required = self.is_method(objpath) - accessor_kinds = location and self.get_accessor_kinds([location]) or \ - locations and self.get_accessor_kinds(locations) + accessor_kinds = location and self.get_accessor_kinds(location) instance_accessor = accessor_kinds and \ len(accessor_kinds) == 1 and \ @@ -1386,11 +1379,6 @@ ref, paths = self.importer.get_module(self.name).special[n.name] return TrResolvedNameRef(n.name, ref) - # Temporary names are output program locals. - - elif n.name.startswith("$t"): - return TrResolvedNameRef(n.name, Reference(""), expr=expr) - # Get the appropriate name for the name reference, using the same method # as in the inspector. @@ -1453,8 +1441,7 @@ name_ref = TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global, location=location) - result = self.get_aliases(name_ref) - return result or name_ref + return not expr and self.get_aliases(name_ref) or name_ref def get_aliases(self, name_ref): @@ -1462,21 +1449,9 @@ location = name_ref.access_location() - accessor_locations = location and self.deducer.get_accessors_for_access(location) - alias_refs = set() - access_locations = set() - - if accessor_locations: - for accessor_location in accessor_locations: - aliased_accesses = self.deducer.alias_index.get(accessor_location) - if not aliased_accesses: - continue - access_locations.update(aliased_accesses) - refs = self.deducer.referenced_objects.get(accessor_location) - if refs: - alias_refs.update(refs) - - return AliasResult(name_ref, alias_refs, access_locations) + refs = self.deducer.referenced_objects.get(location) + refs = refs or self.deducer.accessor_all_types.get(location) + return AliasResult(name_ref, refs or set(), location) def make_volatile(self, name): diff -r 21356f3943f1 -r 5e4ac6b12b8f transresults.py --- a/transresults.py Mon Mar 13 18:45:41 2017 +0100 +++ b/transresults.py Thu Mar 16 18:13:34 2017 +0100 @@ -70,13 +70,18 @@ def access_location(self): return self.location - def access_locations(self): - return self.location and [self.location] - def __str__(self): "Return an output representation of the referenced name." + # Temporary names are output program locals. + + if self.name.startswith("$t"): + if self.expr: + return "%s = %s" % (encode_path(self.name), self.expr) + else: + return encode_path(self.name) + # For sources, any identified static origin will be constant and thus # usable directly. For targets, no constant should be assigned and thus # the alias (or any plain name) will be used. @@ -179,9 +184,6 @@ def access_location(self): return self.location - def access_locations(self): - return self.location and [self.location] - def context(self): return self.context_identity @@ -209,11 +211,11 @@ "An alias for other values." - def __init__(self, name_ref, refs, locations): + def __init__(self, name_ref, refs, location): NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) self.name_ref = name_ref self.refs = refs - self.locations = locations + self.location = location def references(self): ref = self.name_ref.reference() @@ -224,10 +226,7 @@ return len(refs) == 1 and first(refs) or None def access_location(self): - return len(self.locations) == 1 and first(self.locations) or None - - def access_locations(self): - return self.locations + return self.location def get_name(self): ref = self.reference()