Lichen

Changeset

737:5e4ac6b12b8f
2017-03-16 Paul Boddie raw files shortlog changelog graph Merged changes from the default branch. normal-function-parameters
common.py (file) translator.py (file) transresults.py (file)
     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()