2.1 --- a/micropython/data.py Tue Sep 07 20:09:20 2010 +0200
2.2 +++ b/micropython/data.py Fri Sep 10 00:27:49 2010 +0200
2.3 @@ -100,9 +100,8 @@
2.4
2.5 # Attribute users, defining names which use attributes.
2.6
2.7 - self.attribute_users = [{}] # stack of assignments
2.8 - self.user_shelves = []
2.9 - self.loop_users = [{}] # stack of loop nodes
2.10 + self.attribute_users = [{}] # stack of assignments and branches
2.11 + self.attribute_user_shelves = []
2.12
2.13 # Scope usage, indicating the origin of names.
2.14
2.15 @@ -377,8 +376,73 @@
2.16
2.17 usage = set()
2.18 for user in self.all_attribute_users:
2.19 - for name, attrnames in user._attrnames.items():
2.20 - usage.add(tuple(attrnames))
2.21 +
2.22 + # First, visit the contributors and combine their usage with the
2.23 + # usage for each user.
2.24 +
2.25 + user._attrcombined = combined_usage = self.get_usage_from_contributors(user)
2.26 +
2.27 + for name, all_usage in combined_usage.items():
2.28 + for attrnames in all_usage:
2.29 + usage.add(tuple(attrnames))
2.30 +
2.31 + return usage
2.32 +
2.33 + def get_usage_from_contributors(self, node):
2.34 +
2.35 + """
2.36 + Obtain usage information from the given 'node', combined with usage
2.37 + details from its contributors, returning a dictionary mapping names to
2.38 + lists of usage possibilities.
2.39 + """
2.40 +
2.41 + usage = {}
2.42 + contributor_usage = {}
2.43 +
2.44 + # Visit each contributor, gathering usage for each name.
2.45 +
2.46 + for contributor in node._attrbranches:
2.47 +
2.48 + # Get contributed usage for each contributor.
2.49 + # This gathers usage for each name such as {(a, b), (c, d)} and
2.50 + # {(a, b), (e, f)} into a single set {(a, b), (c, d), (e, f)}.
2.51 +
2.52 + for name, all_usage in self.get_usage_from_contributors(contributor).items():
2.53 + if not contributor_usage.has_key(name):
2.54 + contributor_usage[name] = set()
2.55 + contributor_usage[name].update(all_usage)
2.56 +
2.57 + # Then get the resulting usage.
2.58 +
2.59 + contributed_names = contributor_usage.keys()
2.60 + current_usage = node._attrnames
2.61 +
2.62 + for name in contributed_names:
2.63 +
2.64 + # Where contributors define names not present at this level, just
2.65 + # use the contributed usage.
2.66 +
2.67 + current_usage_for_name = current_usage.get(name)
2.68 +
2.69 + if current_usage_for_name is None:
2.70 + usage[name] = contributor_usage[name]
2.71 +
2.72 + # Otherwise, combine the current usage with the contributed usage.
2.73 + # Thus, usage of (f, g) combined with {(a, b), (c, d)} would give
2.74 + # {(f, g, a, b), (f, g, c, d)}.
2.75 +
2.76 + else:
2.77 + usage[name] = set()
2.78 + for attrnames in contributor_usage[name]:
2.79 + usage[name].add(tuple(current_usage_for_name.union(attrnames)))
2.80 +
2.81 + # Maintain usage not affected by contributors.
2.82 + # Usage of (f, g) would become {(f, g)}.
2.83 +
2.84 + for name in current_usage.keys():
2.85 + if name not in contributed_names:
2.86 + usage[name] = set([tuple(current_usage[name])])
2.87 +
2.88 return usage
2.89
2.90 def use_attribute(self, name, attrname):
2.91 @@ -456,17 +520,6 @@
2.92
2.93 self._init_attribute_user_for_name(node, name)
2.94
2.95 - # Propagate any loop usage forward to any redefinition of a name.
2.96 - # This models the name acquiring the existing usage as it remains active
2.97 - # upon starting a new iteration.
2.98 -
2.99 - loop_users = self.loop_users[-1]
2.100 -
2.101 - if loop_users.has_key(name):
2.102 - for loop_user in loop_users[name]:
2.103 - node._attrnames[name].update(loop_user._attrnames[name])
2.104 - del loop_users[name]
2.105 -
2.106 # Remember this user.
2.107
2.108 self.all_attribute_users.add(node)
2.109 @@ -475,10 +528,15 @@
2.110
2.111 "Make sure that 'node' is initialised for 'name'."
2.112
2.113 + self._init_attribute_user(node)
2.114 + node._attrnames[name] = set()
2.115 +
2.116 + def _init_attribute_user(self, node):
2.117 if not hasattr(node, "_attrnames"):
2.118 node._attrnames = {}
2.119
2.120 - node._attrnames[name] = set()
2.121 + if not hasattr(node, "_attrbranches"):
2.122 + node._attrbranches = []
2.123
2.124 # Branch management methods.
2.125
2.126 @@ -489,10 +547,10 @@
2.127 and subsequently converge.
2.128 """
2.129
2.130 - self.user_shelves.append([])
2.131 + self.attribute_user_shelves.append([])
2.132 self.scope_shelves.append([])
2.133
2.134 - def _new_branch(self, node, loop=0):
2.135 + def _new_branch(self, node):
2.136
2.137 """
2.138 Establish a new control-flow branch, transferring attribute usage to
2.139 @@ -502,35 +560,30 @@
2.140 usage.
2.141 """
2.142
2.143 - new_usage = {}
2.144 + attribute_users = self.attribute_users[-1]
2.145 +
2.146 + # Define this node as the active attribute user for all currently
2.147 + # defined names.
2.148 +
2.149 new_users = {}
2.150
2.151 - # Define usage on this node based on current usage.
2.152 -
2.153 - for users in self.attribute_users[-1].values():
2.154 - for user in users:
2.155 - for name, usage in user._attrnames.items():
2.156 - if not new_usage.has_key(name):
2.157 - new_usage[name] = set(usage)
2.158 - else:
2.159 - new_usage[name] = new_usage[name].union(set(usage))
2.160 -
2.161 - # Set the usage on this node.
2.162 -
2.163 - node._attrnames = {}
2.164 -
2.165 - for name, usage in new_usage.items():
2.166 - node._attrnames[name] = usage
2.167 + for name in attribute_users.keys():
2.168 new_users[name] = [node]
2.169 -
2.170 - # Retain a record of active users.
2.171 -
2.172 + self._init_attribute_user_for_name(node, name)
2.173 +
2.174 + self._init_attribute_user(node)
2.175 self.attribute_users.append(new_users)
2.176
2.177 - if loop:
2.178 - loop_users = {}
2.179 - loop_users.update(new_users)
2.180 - self.loop_users.append(loop_users)
2.181 + # Add this user as a contributor to the previously active users.
2.182 +
2.183 + all_users = set()
2.184 +
2.185 + for users in attribute_users.values():
2.186 + all_users.update(users)
2.187 +
2.188 + for user in all_users:
2.189 + self._init_attribute_user(user)
2.190 + user._attrbranches.append(node)
2.191
2.192 # Retain a record of scope usage.
2.193
2.194 @@ -538,10 +591,6 @@
2.195 scope_usage.update(self.scope_usage[-1])
2.196 self.scope_usage.append(scope_usage)
2.197
2.198 - # Remember this user.
2.199 -
2.200 - self.all_attribute_users.add(node)
2.201 -
2.202 def _abandon_branch(self):
2.203
2.204 """
2.205 @@ -561,12 +610,12 @@
2.206 """
2.207
2.208 users = self.attribute_users.pop()
2.209 - self.user_shelves[-1].append(users)
2.210 + self.attribute_user_shelves[-1].append(users)
2.211
2.212 scope_usage = self.scope_usage.pop()
2.213 self.scope_shelves[-1].append(scope_usage)
2.214
2.215 - def _merge_branches(self, loop=0):
2.216 + def _merge_branches(self):
2.217
2.218 """
2.219 Merge control-flow branches. This should find the users active within
2.220 @@ -577,13 +626,10 @@
2.221 # Combine the attribute users. This ensures that a list of users
2.222 # affected by attribute usage is maintained for the current branch.
2.223
2.224 - if loop:
2.225 - self.loop_users.pop()
2.226 -
2.227 users = self.attribute_users[-1]
2.228 new_users = {}
2.229
2.230 - all_shelved_users = self.user_shelves.pop()
2.231 + all_shelved_users = self.attribute_user_shelves.pop()
2.232 all_user_names = set()
2.233
2.234 # Find all the names defined by the branches.
2.235 @@ -727,6 +773,12 @@
2.236 self.old_scope = old_scope
2.237 self.new_scope = new_scope
2.238
2.239 +class NullBranch:
2.240 +
2.241 + "A class representing an attribute user for a non-existent branch."
2.242 +
2.243 + pass
2.244 +
2.245 # Program data structures.
2.246
2.247 class Attr:
3.1 --- a/micropython/inspect.py Tue Sep 07 20:09:20 2010 +0200
3.2 +++ b/micropython/inspect.py Fri Sep 10 00:27:49 2010 +0200
3.3 @@ -350,8 +350,8 @@
3.4 def new_branchpoint(self):
3.5 self.get_namespace()._new_branchpoint()
3.6
3.7 - def new_branch(self, node, loop=0):
3.8 - self.get_namespace()._new_branch(node, loop)
3.9 + def new_branch(self, node):
3.10 + self.get_namespace()._new_branch(node)
3.11
3.12 def abandon_branch(self):
3.13 self.get_namespace()._abandon_branch()
3.14 @@ -359,8 +359,8 @@
3.15 def shelve_branch(self):
3.16 self.get_namespace()._shelve_branch()
3.17
3.18 - def merge_branches(self, loop=0):
3.19 - self.get_namespace()._merge_branches(loop)
3.20 + def merge_branches(self):
3.21 + self.get_namespace()._merge_branches()
3.22
3.23 def define_attribute_user(self, node):
3.24
3.25 @@ -786,7 +786,7 @@
3.26 # Enter the loop.
3.27 # Propagate attribute usage to branches.
3.28
3.29 - self.new_branch(node, 1)
3.30 + self.new_branch(node)
3.31 self.dispatch(node.body)
3.32 self.shelve_branch()
3.33
3.34 @@ -794,12 +794,12 @@
3.35
3.36 # Maintain a branch for the else clause.
3.37
3.38 + self.new_branch(node.else_ or NullBranch())
3.39 if node.else_ is not None:
3.40 - self.new_branch(node.else_)
3.41 self.dispatch(node.else_)
3.42 - self.shelve_branch()
3.43 + self.shelve_branch()
3.44
3.45 - self.merge_branches(1)
3.46 + self.merge_branches()
3.47 return None
3.48
3.49 def visitFrom(self, node):
3.50 @@ -871,10 +871,10 @@
3.51
3.52 # Maintain a branch for the else clause.
3.53
3.54 + self.new_branch(node.else_ or NullBranch())
3.55 if node.else_ is not None:
3.56 - self.new_branch(node.else_)
3.57 self.dispatch(node.else_)
3.58 - self.shelve_branch()
3.59 + self.shelve_branch()
3.60
3.61 self.merge_branches()
3.62 return None
3.63 @@ -932,7 +932,7 @@
3.64 # Enter the loop.
3.65 # Propagate attribute usage to branches.
3.66
3.67 - self.new_branch(node, 1)
3.68 + self.new_branch(node)
3.69
3.70 for if_ in node.ifs:
3.71 self.dispatch(if_)
3.72 @@ -940,7 +940,7 @@
3.73 self.shelve_branch()
3.74 self.in_loop = in_loop
3.75
3.76 - self.merge_branches(1)
3.77 + self.merge_branches()
3.78 return None
3.79
3.80 visitListCompIf = NOP
3.81 @@ -1009,10 +1009,10 @@
3.82
3.83 self.shelve_branch()
3.84
3.85 + self.new_branch(node.else_ or NullBranch())
3.86 if node.else_ is not None:
3.87 - self.new_branch(node.else_)
3.88 self.dispatch(node.else_)
3.89 - self.shelve_branch()
3.90 + self.shelve_branch()
3.91
3.92 self.merge_branches()
3.93 return None
3.94 @@ -1033,7 +1033,7 @@
3.95 in_loop = self.in_loop
3.96 self.in_loop = 1
3.97 self.dispatch(node.test)
3.98 - self.new_branch(node, 1)
3.99 + self.new_branch(node)
3.100 self.dispatch(node.body)
3.101 self.shelve_branch()
3.102 self.in_loop = in_loop
3.103 @@ -1041,10 +1041,10 @@
3.104 # Maintain a branch for the else clause.
3.105 # NOTE: Consider merging here before the else clause.
3.106
3.107 + self.new_branch(node.else_ or NullBranch())
3.108 if node.else_ is not None:
3.109 - self.new_branch(node.else_)
3.110 self.dispatch(node.else_)
3.111 - self.shelve_branch()
3.112 + self.shelve_branch()
3.113
3.114 self.merge_branches()
3.115 return None
4.1 --- a/tests/attribute_access_type_restriction_loop_accumulation.py Tue Sep 07 20:09:20 2010 +0200
4.2 +++ b/tests/attribute_access_type_restriction_loop_accumulation.py Fri Sep 10 00:27:49 2010 +0200
4.3 @@ -29,16 +29,22 @@
4.4 def g(self):
4.5 return 5
4.6
4.7 + def h(self):
4.8 + return 6
4.9 +
4.10 def test_loop(obj, obj2):
4.11 + # obj: f, g (from loop); f, h (outside loop)
4.12 while obj.f():
4.13 + # obj: g (in loop)
4.14 obj.g()
4.15 - obj = obj2 # should support e, f, g
4.16 + obj = obj2 # obj: e, f, h
4.17 obj.e()
4.18 - return obj.f()
4.19 + # (test invoked again)
4.20 + return obj.h()
4.21
4.22 c = C()
4.23 d = D()
4.24 e = E()
4.25 -result1_0 = test_loop(d, e)
4.26 +result_6 = test_loop(d, e)
4.27
4.28 # vim: tabstop=4 expandtab shiftwidth=4