# HG changeset patch # User Paul Boddie # Date 1337472968 -7200 # Node ID fd23a6b4666d40e2be0a38afa754050b6577e205 # Parent faec8d7e1a16b702bd3869768db06c07085fef8b Attempted to perform more thorough attribute usage analysis, especially where loops and complicated control-flow structures are involved. Added some more tests. diff -r faec8d7e1a16 -r fd23a6b4666d micropython/data.py --- a/micropython/data.py Sat May 19 01:33:10 2012 +0200 +++ b/micropython/data.py Sun May 20 02:16:08 2012 +0200 @@ -389,11 +389,12 @@ # First, visit the contributors and combine their attribute # usage with the usage recorded directly on the user. - contributors, combined_usage = self.get_usage_from_contributors(user) + self.get_usage_from_contributors(user) + self.set_contributors(user) # Record the defining user on each contributor. - for contributor in contributors: + for contributor in user._attrcontributors: contributor._attrdefs.append(user) # Then, tell the importer about the usage. @@ -402,7 +403,7 @@ # Only provide information about names defined by this user. - usage = combined_usage.get(name, []) + usage = user._attrcombined.get(name, []) # Skip reporting where no actual usage occurs. @@ -479,6 +480,21 @@ attrtypes[name] = get_object_types_for_usage(combined_usage, objtable, name, self.full_name()) return attrtypes + def set_contributors(self, node): + if not hasattr(node, "_attrcontributors"): + node._attrcontributors = None + all_contributors = set() + + for contributor in node._attrbranches: + all_contributors.add(contributor) + self.set_contributors(contributor) + + contributors = contributor._attrcontributors + if contributors is not None: + all_contributors.update(contributors) + + node._attrcontributors = all_contributors + def get_usage_from_contributors(self, node): """ @@ -488,44 +504,76 @@ usage possibilities (each a collection of attribute names). """ + unfinished = {} + if not hasattr(node, "_attrcombined"): - - node._attrcontributors = set() - node._attrcombined = {} - - contributor_usage = {} - all_contributors = set() - - # Visit each contributor, gathering usage for each name. + node._attrcombined = None for contributor in node._attrbranches: - # Get contributed usage for each contributor. - # This gathers usage for each name such as {(a, b), (c, d)} and - # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}. - - contributors, contributed_usage = self.get_usage_from_contributors(contributor) - update_mapping_dict(contributor_usage, [contributed_usage]) - - # Record all contributors. - - all_contributors.add(contributor) - all_contributors.update(contributors) - - # Then get the resulting usage. - # First, make the current usage compatible with the contributed - # usage: this makes the attribute usage for each name merely one - # member in a list of many possibilities. - # Then, combine the current usage with the contributed usage. - # Thus, usage of {(f, g)} combined with {(a, b), (c, d)} would give - # {(f, g, a, b), (f, g, c, d)}. - - usage = combine_mapping_dicts(deepen_mapping_dict(node._attrnames), contributor_usage) - - node._attrcontributors = all_contributors - node._attrcombined = usage - - return node._attrcontributors, node._attrcombined + # Get contributor details. + + unfinished_contributors = self.get_usage_from_contributors(contributor) + + # Collect unfinished contributors and affected nodes. + + if node._attrcombined is None: + if not unfinished.has_key(contributor): + unfinished[contributor] = [] + unfinished[contributor].append(node) + continue + + for unfinished_contributor, nodes in unfinished_contributors.items(): + if not unfinished.has_key(unfinished_contributor): + unfinished[unfinished_contributor] = nodes + else: + unfinished[unfinished_contributor] += nodes + + unfinished[contributor].append(node) + + # Set the current state of the usage on this node. + + node._attrcombined = self.get_usage_from_contributors_for_node(node) + + # Complete unfinished contributors relying on this node. + + if unfinished.has_key(node): + processed = set() + for contributor in unfinished[node]: + if not contributor in processed: + processed.add(contributor) + contributor._attrcombined = self.get_usage_from_contributors_for_node(contributor) + del unfinished[node] + + return unfinished + + def get_usage_from_contributors_for_node(self, node): + + # Visit each contributor, gathering usage for each name. + + contributor_usage = {} + all_contributions = [] + + for contributor in node._attrbranches: + usage = contributor._attrcombined + if usage is not None: + all_contributions.append(usage) + + # Get contributed usage for each contributor. + # This gathers usage for each name such as {(a, b), (c, d)} and + # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}. + + update_mapping_dict(contributor_usage, all_contributions) + + # Then get the resulting usage. + # First, make the current usage compatible with the contributed + # usage: this makes the attribute usage for each name merely one + # member in a list of many possibilities. + # Then, combine the current usage with the contributed usage. + # Thus, usage of {(f, g)} combined with {(a, b), (c, d)} would give + # {(f, g, a, b), (f, g, c, d)}. + + return combine_mapping_dicts(deepen_mapping_dict(node._attrnames), contributor_usage) def use_attribute(self, name, attrname, value=None): @@ -995,6 +1043,8 @@ else: return None + __call__ = get_value # convenient access to any single value + def update(self, context_values, single_assignment): """ diff -r faec8d7e1a16 -r fd23a6b4666d tests/attribute_access_constant.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/attribute_access_constant.py Sun May 20 02:16:08 2012 +0200 @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +class C: + a = 123 + +a1 = C.a # no need for usage tracking on C +f1 = a1.__add__ # no need for usage tracking on a1 + +c = C() +a2 = c.a +f2 = a2.__add__ + +result1_579 = f1(456) +result2_579 = f2(456) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r faec8d7e1a16 -r fd23a6b4666d tests/for_if_usage_continue.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/for_if_usage_continue.py Sun May 20 02:16:08 2012 +0200 @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +class C: + p = 1 + q = 2 + +c = C() +a = 1 + +for x in 0, 1, 2: + if x == 1: + continue + + if a: + b = c.p + else: + b = c.q + +result_1 = b + +# vim: tabstop=4 expandtab shiftwidth=4