micropython

Changeset

508:fd23a6b4666d
2012-05-20 Paul Boddie raw files shortlog changelog graph Attempted to perform more thorough attribute usage analysis, especially where loops and complicated control-flow structures are involved. Added some more tests.
micropython/data.py (file) tests/attribute_access_constant.py (file) tests/for_if_usage_continue.py (file)
     1.1 --- a/micropython/data.py	Sat May 19 01:33:10 2012 +0200
     1.2 +++ b/micropython/data.py	Sun May 20 02:16:08 2012 +0200
     1.3 @@ -389,11 +389,12 @@
     1.4                  # First, visit the contributors and combine their attribute
     1.5                  # usage with the usage recorded directly on the user.
     1.6  
     1.7 -                contributors, combined_usage = self.get_usage_from_contributors(user)
     1.8 +                self.get_usage_from_contributors(user)
     1.9 +                self.set_contributors(user)
    1.10  
    1.11                  # Record the defining user on each contributor.
    1.12  
    1.13 -                for contributor in contributors:
    1.14 +                for contributor in user._attrcontributors:
    1.15                      contributor._attrdefs.append(user)
    1.16  
    1.17                  # Then, tell the importer about the usage.
    1.18 @@ -402,7 +403,7 @@
    1.19  
    1.20                      # Only provide information about names defined by this user.
    1.21  
    1.22 -                    usage = combined_usage.get(name, [])
    1.23 +                    usage = user._attrcombined.get(name, [])
    1.24  
    1.25                      # Skip reporting where no actual usage occurs.
    1.26  
    1.27 @@ -479,6 +480,21 @@
    1.28                  attrtypes[name] = get_object_types_for_usage(combined_usage, objtable, name, self.full_name())
    1.29          return attrtypes
    1.30  
    1.31 +    def set_contributors(self, node):
    1.32 +        if not hasattr(node, "_attrcontributors"):
    1.33 +            node._attrcontributors = None
    1.34 +            all_contributors = set()
    1.35 +
    1.36 +            for contributor in node._attrbranches:
    1.37 +                all_contributors.add(contributor)
    1.38 +                self.set_contributors(contributor)
    1.39 +
    1.40 +                contributors = contributor._attrcontributors
    1.41 +                if contributors is not None:
    1.42 +                    all_contributors.update(contributors)
    1.43 +
    1.44 +            node._attrcontributors = all_contributors
    1.45 +
    1.46      def get_usage_from_contributors(self, node):
    1.47  
    1.48          """
    1.49 @@ -488,44 +504,76 @@
    1.50          usage possibilities (each a collection of attribute names).
    1.51          """
    1.52  
    1.53 +        unfinished = {}
    1.54 +
    1.55          if not hasattr(node, "_attrcombined"):
    1.56 -
    1.57 -            node._attrcontributors = set()
    1.58 -            node._attrcombined = {}
    1.59 -
    1.60 -            contributor_usage = {}
    1.61 -            all_contributors = set()
    1.62 -
    1.63 -            # Visit each contributor, gathering usage for each name.
    1.64 +            node._attrcombined = None
    1.65  
    1.66              for contributor in node._attrbranches:
    1.67  
    1.68 -                # Get contributed usage for each contributor.
    1.69 -                # This gathers usage for each name such as {(a, b), (c, d)} and
    1.70 -                # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}.
    1.71 -
    1.72 -                contributors, contributed_usage = self.get_usage_from_contributors(contributor)
    1.73 -                update_mapping_dict(contributor_usage, [contributed_usage])
    1.74 -
    1.75 -                # Record all contributors.
    1.76 -
    1.77 -                all_contributors.add(contributor)
    1.78 -                all_contributors.update(contributors)
    1.79 -
    1.80 -            # Then get the resulting usage.
    1.81 -            # First, make the current usage compatible with the contributed
    1.82 -            # usage: this makes the attribute usage for each name merely one
    1.83 -            # member in a list of many possibilities.
    1.84 -            # Then, combine the current usage with the contributed usage.
    1.85 -            # Thus, usage of {(f, g)} combined with {(a, b), (c, d)} would give
    1.86 -            # {(f, g, a, b), (f, g, c, d)}.
    1.87 -
    1.88 -            usage = combine_mapping_dicts(deepen_mapping_dict(node._attrnames), contributor_usage)
    1.89 -
    1.90 -            node._attrcontributors = all_contributors
    1.91 -            node._attrcombined = usage
    1.92 -
    1.93 -        return node._attrcontributors, node._attrcombined
    1.94 +                # Get contributor details.
    1.95 +
    1.96 +                unfinished_contributors = self.get_usage_from_contributors(contributor)
    1.97 +
    1.98 +                # Collect unfinished contributors and affected nodes.
    1.99 +
   1.100 +                if node._attrcombined is None:
   1.101 +                    if not unfinished.has_key(contributor):
   1.102 +                        unfinished[contributor] = []
   1.103 +                    unfinished[contributor].append(node)
   1.104 +                    continue
   1.105 +
   1.106 +                for unfinished_contributor, nodes in unfinished_contributors.items():
   1.107 +                    if not unfinished.has_key(unfinished_contributor):
   1.108 +                        unfinished[unfinished_contributor] = nodes
   1.109 +                    else:
   1.110 +                        unfinished[unfinished_contributor] += nodes
   1.111 +
   1.112 +                unfinished[contributor].append(node)
   1.113 +
   1.114 +            # Set the current state of the usage on this node.
   1.115 +
   1.116 +            node._attrcombined = self.get_usage_from_contributors_for_node(node)
   1.117 +
   1.118 +            # Complete unfinished contributors relying on this node.
   1.119 +
   1.120 +            if unfinished.has_key(node):
   1.121 +                processed = set()
   1.122 +                for contributor in unfinished[node]:
   1.123 +                    if not contributor in processed:
   1.124 +                        processed.add(contributor)
   1.125 +                        contributor._attrcombined = self.get_usage_from_contributors_for_node(contributor)
   1.126 +                del unfinished[node]
   1.127 +
   1.128 +        return unfinished
   1.129 +
   1.130 +    def get_usage_from_contributors_for_node(self, node):
   1.131 +
   1.132 +        # Visit each contributor, gathering usage for each name.
   1.133 +
   1.134 +        contributor_usage = {}
   1.135 +        all_contributions = []
   1.136 +
   1.137 +        for contributor in node._attrbranches:
   1.138 +            usage = contributor._attrcombined
   1.139 +            if usage is not None:
   1.140 +                all_contributions.append(usage)
   1.141 +
   1.142 +        # Get contributed usage for each contributor.
   1.143 +        # This gathers usage for each name such as {(a, b), (c, d)} and
   1.144 +        # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}.
   1.145 +
   1.146 +        update_mapping_dict(contributor_usage, all_contributions)
   1.147 +
   1.148 +        # Then get the resulting usage.
   1.149 +        # First, make the current usage compatible with the contributed
   1.150 +        # usage: this makes the attribute usage for each name merely one
   1.151 +        # member in a list of many possibilities.
   1.152 +        # Then, combine the current usage with the contributed usage.
   1.153 +        # Thus, usage of {(f, g)} combined with {(a, b), (c, d)} would give
   1.154 +        # {(f, g, a, b), (f, g, c, d)}.
   1.155 +
   1.156 +        return combine_mapping_dicts(deepen_mapping_dict(node._attrnames), contributor_usage)
   1.157  
   1.158      def use_attribute(self, name, attrname, value=None):
   1.159  
   1.160 @@ -995,6 +1043,8 @@
   1.161          else:
   1.162              return None
   1.163  
   1.164 +    __call__ = get_value    # convenient access to any single value
   1.165 +
   1.166      def update(self, context_values, single_assignment):
   1.167  
   1.168          """
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tests/attribute_access_constant.py	Sun May 20 02:16:08 2012 +0200
     2.3 @@ -0,0 +1,16 @@
     2.4 +#!/usr/bin/env python
     2.5 +
     2.6 +class C:
     2.7 +    a = 123
     2.8 +
     2.9 +a1 = C.a            # no need for usage tracking on C
    2.10 +f1 = a1.__add__     # no need for usage tracking on a1
    2.11 +
    2.12 +c = C()
    2.13 +a2 = c.a
    2.14 +f2 = a2.__add__
    2.15 +
    2.16 +result1_579 = f1(456)
    2.17 +result2_579 = f2(456)
    2.18 +
    2.19 +# vim: tabstop=4 expandtab shiftwidth=4
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/tests/for_if_usage_continue.py	Sun May 20 02:16:08 2012 +0200
     3.3 @@ -0,0 +1,21 @@
     3.4 +#!/usr/bin/env python
     3.5 +
     3.6 +class C:
     3.7 +    p = 1
     3.8 +    q = 2
     3.9 +
    3.10 +c = C()
    3.11 +a = 1
    3.12 +
    3.13 +for x in 0, 1, 2:
    3.14 +    if x == 1:
    3.15 +        continue
    3.16 +
    3.17 +    if a:
    3.18 +        b = c.p
    3.19 +    else:
    3.20 +        b = c.q
    3.21 +
    3.22 +result_1 = b
    3.23 +
    3.24 +# vim: tabstop=4 expandtab shiftwidth=4