1.1 --- a/micropython/data.py Sun Feb 14 16:28:12 2010 +0100
1.2 +++ b/micropython/data.py Sun Feb 14 23:49:17 2010 +0100
1.3 @@ -140,15 +140,23 @@
1.4 # the consensus.
1.5
1.6 self.abandon_attributes = 0 # used when a block will never contribute
1.7 - self.abandoned_shelves = []
1.8 +
1.9 + # Speculative attribute usage where names may not contribute to a
1.10 + # consensus beyond a branch, but where the usage will still influence
1.11 + # acceptable types for a name.
1.12 +
1.13 + self.speculative_attributes = [{}] # stack of speculative usage
1.14 + self.speculative_shelves = [] # stack of unmerged speculative definitions
1.15
1.16 # Attribute users, defining names which use attributes.
1.17
1.18 self.attribute_users = [{}] # stack of assignments
1.19 + self.all_attribute_users = [{}] # stack of assignments (to record any usage)
1.20 self.user_shelves = []
1.21
1.22 # Define attribute usage to identify active program sections.
1.23
1.24 + self.all_usage_shelves = [] # stack of unmerged coverage-related definitions
1.25 self.all_attributes_used = []
1.26
1.27 # Attribute/name definition and access.
1.28 @@ -269,6 +277,9 @@
1.29 return results
1.30
1.31 def make_global(self, name):
1.32 +
1.33 + "Declare 'name' as a global in the current namespace."
1.34 +
1.35 if not self.namespace.has_key(name):
1.36 self.globals.add(name)
1.37 return 1
1.38 @@ -322,6 +333,9 @@
1.39 return usage
1.40
1.41 def use_attribute(self, name, attrname):
1.42 +
1.43 + "Declare the usage on 'name' of the given 'attrname'."
1.44 +
1.45 return self._use_attribute(name, attrname)
1.46
1.47 # These shadow various methods in the InspectedModule class, and provide
1.48 @@ -370,7 +384,17 @@
1.49
1.50 defs = self.attributes_used[-1]
1.51 users = self.attribute_users[-1]
1.52 + all_users = self.all_attribute_users[-1]
1.53 + speculative = self.speculative_attributes[-1]
1.54
1.55 + # This node overrides previous definitions.
1.56 +
1.57 + if not speculative.has_key(name):
1.58 + speculative[name] = defs.get(name, set())
1.59 + else:
1.60 + self.all_usage_shelves[-1].append({name : defs[name]})
1.61 +
1.62 + all_users[name] = set([node])
1.63 users[name] = set([node])
1.64 defs[name] = set()
1.65
1.66 @@ -378,11 +402,10 @@
1.67
1.68 if not hasattr(node, "_attrnames"):
1.69 node._attrnames = {}
1.70 + node._alternative_attrnames = {}
1.71
1.72 - # Copy the current usage for the user since this may eventually become
1.73 - # independent from the current usage.
1.74 -
1.75 - node._attrnames[name] = set(defs[name])
1.76 + node._attrnames[name] = set()
1.77 + node._alternative_attrnames[name] = set()
1.78
1.79 # Remember all attribute combinations.
1.80
1.81 @@ -396,7 +419,8 @@
1.82 """
1.83
1.84 self.attribute_shelves.append([])
1.85 - self.abandoned_shelves.append([])
1.86 + self.speculative_shelves.append([])
1.87 + self.all_usage_shelves.append([])
1.88 self.user_shelves.append([])
1.89
1.90 def _new_branch(self):
1.91 @@ -416,6 +440,16 @@
1.92
1.93 self.attribute_users.append({})
1.94
1.95 + # Retain a record of all active users.
1.96 +
1.97 + d = {}
1.98 + d.update(self.all_attribute_users[-1])
1.99 + self.all_attribute_users.append(d)
1.100 +
1.101 + # Speculative attributes are recorded for subsequent feedback to users.
1.102 +
1.103 + self.speculative_attributes.append({})
1.104 +
1.105 def _abandon_branch(self):
1.106 self.abandon_attributes = 1
1.107
1.108 @@ -428,12 +462,26 @@
1.109 observations after a merge.
1.110 """
1.111
1.112 + usage = self.attributes_used.pop()
1.113 + speculative = self.speculative_attributes.pop()
1.114 +
1.115 + # Record contributing usage.
1.116 +
1.117 if not self.abandon_attributes:
1.118 - collector = self.attribute_shelves[-1]
1.119 - else:
1.120 - collector = self.abandoned_shelves[-1]
1.121 + self.attribute_shelves[-1].append(usage)
1.122 +
1.123 + # Record all usage (for coverage).
1.124 +
1.125 + self.all_usage_shelves[-1].append(usage)
1.126
1.127 - collector.append(self.attributes_used.pop())
1.128 + # Record speculative usage.
1.129 +
1.130 + if self.abandon_attributes:
1.131 + for name, attrnames in usage.items():
1.132 + if not speculative.has_key(name):
1.133 + speculative[name] = attrnames
1.134 +
1.135 + self.speculative_shelves[-1].append(speculative)
1.136
1.137 # Forget about any nodes which defined names employing attributes in
1.138 # this branch if the branch is abandoned.
1.139 @@ -443,6 +491,7 @@
1.140 if not self.abandon_attributes:
1.141 self.user_shelves[-1].append(users)
1.142
1.143 + self.all_attribute_users.pop()
1.144 self.abandon_attributes = 0
1.145
1.146 def _merge_branches(self):
1.147 @@ -458,19 +507,22 @@
1.148 """
1.149
1.150 # The active dictionary holds the usage for names defined before the
1.151 - # branches. The abandoned dictionary collects abandoned usage.
1.152 + # branches. The users dictionary holds the active users defining each
1.153 + # name.
1.154
1.155 active = self.attributes_used[-1]
1.156 + users = self.attribute_users[-1]
1.157 + all_users = self.all_attribute_users[-1]
1.158 + speculative = self.speculative_attributes[-1]
1.159
1.160 # Take each alternative branch, currently shelved, and find the
1.161 # intersection of their contributions for each name.
1.162
1.163 - shelved_defs = self.attribute_shelves.pop()
1.164 - abandoned_defs = self.abandoned_shelves.pop()
1.165 -
1.166 # Where branches contribute attribute usage observations, process these
1.167 # as described above. Otherwise, preserve the previous observations.
1.168
1.169 + shelved_defs = self.attribute_shelves.pop()
1.170 +
1.171 if shelved_defs:
1.172 defs = dict(shelved_defs[0])
1.173
1.174 @@ -487,9 +539,28 @@
1.175 else:
1.176 active[name] = attrnames
1.177
1.178 - # Combine the attribute users.
1.179 + # Feed speculative definitions back to the users previously known.
1.180 +
1.181 + speculative_defs = self.speculative_shelves.pop()
1.182 +
1.183 + if speculative_defs:
1.184 + for defs in speculative_defs:
1.185 + for name, attrnames in defs.items():
1.186 +
1.187 + # Where users are active for a name, produce alternatives.
1.188
1.189 - users = self.attribute_users[-1]
1.190 + if all_users.has_key(name):
1.191 + for node in all_users[name]:
1.192 +
1.193 + # Add the usage to the alternatives.
1.194 +
1.195 + if not node._alternative_attrnames.has_key(name):
1.196 + node._alternative_attrnames[name] = set([tuple(attrnames)])
1.197 + else:
1.198 + node._alternative_attrnames[name].add(tuple(attrnames))
1.199 +
1.200 + # Combine the attribute users. This ensures that a list of users
1.201 + # affected by attribute usage is maintained for the current branch.
1.202
1.203 for shelved_users in self.user_shelves.pop():
1.204 for name, nodes in shelved_users.items():
1.205 @@ -502,7 +573,9 @@
1.206 # although they do not contribute to further usage in a namespace, any
1.207 # attribute combinations do indicate function usage.
1.208
1.209 - for defs in shelved_defs + abandoned_defs:
1.210 + all_usage_defs = self.all_usage_shelves.pop()
1.211 +
1.212 + for defs in all_usage_defs:
1.213 for name, attrnames in defs.items():
1.214
1.215 # Check for isolated pockets of attribute usage as well as more
2.1 --- a/micropython/trans.py Sun Feb 14 16:28:12 2010 +0100
2.2 +++ b/micropython/trans.py Sun Feb 14 23:49:17 2010 +0100
2.3 @@ -315,9 +315,20 @@
2.4
2.5 for name, names_used in node._attrnames.items():
2.6
2.7 + # Consider alternatives.
2.8 +
2.9 + if node._alternative_attrnames.has_key(name):
2.10 + all_names_used = set()
2.11 + all_names_used.update(node._alternative_attrnames)
2.12 + all_names_used.add(tuple(names_used))
2.13 + else:
2.14 + all_names_used = [names_used]
2.15 +
2.16 # Get the names of all object types supporting these names.
2.17
2.18 - targets = self.objtable.all_possible_objects_plus_status(names_used)
2.19 + targets = set()
2.20 + for names_used in all_names_used:
2.21 + targets.update(self.objtable.all_possible_objects_plus_status(names_used))
2.22
2.23 # Where only one object type is suggested, produce a guard.
2.24 # NOTE: This only supports classes as types, not modules.
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/tests/abandoned_attribute_usage_multiple_candidates_nested.py Sun Feb 14 23:49:17 2010 +0100
3.3 @@ -0,0 +1,31 @@
3.4 +#!/usr/bin/env python
3.5 +
3.6 +"""
3.7 +This test attempts to cause the recording of the usage of 'C' in the function
3.8 +'g', alongside the expectation that 'D' might be used instead. A guard
3.9 +stipulating constraints for all of 'g' cannot therefore be generated.
3.10 +"""
3.11 +
3.12 +class C:
3.13 + def f(self):
3.14 + return 1
3.15 +
3.16 +class D:
3.17 + def g(self):
3.18 + return 2
3.19 +
3.20 + def h(self):
3.21 + return 3
3.22 +
3.23 +def g(c):
3.24 + if 1:
3.25 + c.g()
3.26 + if 1:
3.27 + return c.h()
3.28 + return c.f()
3.29 +
3.30 +c = C()
3.31 +d = D()
3.32 +result1_3 = g(d)
3.33 +
3.34 +# vim: tabstop=4 expandtab shiftwidth=4