1.1 --- a/micropython/data.py Fri Nov 06 01:07:48 2009 +0100
1.2 +++ b/micropython/data.py Fri Nov 06 01:17:07 2009 +0100
1.3 @@ -119,6 +119,14 @@
1.4 self.module = module
1.5 self.finalised = 0
1.6
1.7 + # Attributes accessed on objects, potentially narrowing their types.
1.8 + # Specific namespaces should define known names during initialisation.
1.9 +
1.10 + self.attributes_used = [{}] # stack of usage
1.11 + self.attribute_shelves = [] # stack of unmerged definitions
1.12 +
1.13 + # Attribute/name definition and access.
1.14 +
1.15 def __delitem__(self, name):
1.16 del self.namespace[name]
1.17
1.18 @@ -241,6 +249,8 @@
1.19 else:
1.20 return 0
1.21
1.22 + # Attribute positioning.
1.23 +
1.24 def attributes_as_list(self):
1.25
1.26 "Return the attributes in a list."
1.27 @@ -265,6 +275,52 @@
1.28
1.29 self.finalised = 1
1.30
1.31 + # Attribute usage methods.
1.32 +
1.33 + def _use_attribute(self, attr, attrname):
1.34 + name = attr.name
1.35 + defs = self.attributes_used[-1]
1.36 + if not defs.has_key(name):
1.37 + defs[name] = set()
1.38 + defs[name].add(attrname)
1.39 + return defs[name]
1.40 +
1.41 + def _reset_attributes(self, name):
1.42 + defs = self.attributes_used[-1]
1.43 + defs[name] = set()
1.44 +
1.45 + def _reset_all_attributes(self):
1.46 + self.attributes_used[-1] = {}
1.47 +
1.48 + def _new_branchpoint(self):
1.49 + self.attribute_shelves.append([])
1.50 +
1.51 + def _new_branch(self):
1.52 + d = {}
1.53 + for name, attrnames in self.attributes_used[-1].items():
1.54 + d[name] = set(attrnames)
1.55 + self.attributes_used.append(d)
1.56 +
1.57 + def _shelve_branch(self):
1.58 + self.attribute_shelves[-1].append(self.attributes_used.pop())
1.59 +
1.60 + def _merge_branches(self):
1.61 + active = self.attributes_used[-1]
1.62 +
1.63 + shelved_defs = self.attribute_shelves.pop()
1.64 + defs = shelved_defs[0]
1.65 +
1.66 + for next_defs in shelved_defs[1:]:
1.67 + for name, attrnames in next_defs.items():
1.68 + if defs.has_key(name):
1.69 + defs[name] = defs[name].intersection(attrnames)
1.70 +
1.71 + for name, attrnames in defs.items():
1.72 + if active.has_key(name):
1.73 + active[name] = active[name].intersection(attrnames)
1.74 + else:
1.75 + active[name] = attrnames
1.76 +
1.77 # Program data structures. There are two separate kinds of structures: those
1.78 # with context, which are the values manipulated by programs, and those without
1.79 # context, which are typically constant things which are stored alongside the
1.80 @@ -286,10 +342,6 @@
1.81 self.parent = parent
1.82 self.name = name
1.83
1.84 - # Attribute usage.
1.85 -
1.86 - self.attributes_used = set()
1.87 -
1.88 # Possible values.
1.89
1.90 self.context_values = set()
1.91 @@ -298,15 +350,6 @@
1.92
1.93 self.assignments = None
1.94
1.95 - # Attribute usage methods.
1.96 -
1.97 - def use_attribute(self, attrname):
1.98 - self.attributes_used.add(attrname)
1.99 - return self.attributes_used
1.100 -
1.101 - def exposes_name(self, attrname):
1.102 - return attrname in self.attributes_used
1.103 -
1.104 # Value-related methods.
1.105
1.106 def get_contexts(self):
1.107 @@ -808,8 +851,6 @@
1.108 name, pos = held.pop()
1.109 namearray[i] = name
1.110
1.111 - #print self.name, positions
1.112 - #print "->", namearray
1.113 return namearray
1.114
1.115 def _cmp_positions(self, a, b):
1.116 @@ -895,6 +936,11 @@
1.117
1.118 self.default_attrs = []
1.119
1.120 + # Initialise attribute usage.
1.121 +
1.122 + for arg in argnames:
1.123 + self.attributes_used[-1][arg] = set()
1.124 +
1.125 # Caches.
1.126
1.127 self.localnames = None # cache for locals
2.1 --- a/micropython/inspect.py Fri Nov 06 01:07:48 2009 +0100
2.2 +++ b/micropython/inspect.py Fri Nov 06 01:17:07 2009 +0100
2.3 @@ -293,7 +293,7 @@
2.4
2.5 self.namespaces[-2].add_instance_attribute(name)
2.6
2.7 - def get_parent(self):
2.8 + def get_namespace(self):
2.9
2.10 "Return the parent (or most recent) namespace currently exposed."
2.11
2.12 @@ -303,9 +303,30 @@
2.13
2.14 "Use the given 'name' within the current namespace/unit."
2.15
2.16 - unit = self.get_parent()
2.17 + unit = self.get_namespace()
2.18 self.importer.use_name(name, unit.name)
2.19
2.20 + def new_branchpoint(self):
2.21 + self.get_namespace()._new_branchpoint()
2.22 +
2.23 + def new_branch(self):
2.24 + self.get_namespace()._new_branch()
2.25 +
2.26 + def shelve_branch(self):
2.27 + self.get_namespace()._shelve_branch()
2.28 +
2.29 + def merge_branches(self):
2.30 + self.get_namespace()._merge_branches()
2.31 +
2.32 + def reset_attributes(self, name):
2.33 + self.get_namespace()._reset_attributes(name)
2.34 +
2.35 + def reset_all_attributes(self):
2.36 + self.get_namespace()._reset_all_attributes()
2.37 +
2.38 + def use_attribute(self, attr, attrname):
2.39 + return self.get_namespace()._use_attribute(attr, attrname)
2.40 +
2.41 # Visitor methods.
2.42
2.43 def default(self, node, *args):
2.44 @@ -353,7 +374,7 @@
2.45
2.46 function = Function(
2.47 name,
2.48 - self.get_parent(),
2.49 + self.get_namespace(),
2.50 node.argnames,
2.51 node.defaults,
2.52 (node.flags & 4 != 0),
2.53 @@ -437,7 +458,8 @@
2.54
2.55 # Note usage of the attribute.
2.56
2.57 - node._attrnames = expr.use_attribute(node.attrname)
2.58 + if expr.parent is self.get_namespace():
2.59 + node._attrnames = self.use_attribute(expr, node.attrname)
2.60
2.61 return None
2.62
2.63 @@ -459,6 +481,7 @@
2.64 raise InspectError(self.full_name(), node, "Deletion of attribute %r is not supported." % node.name)
2.65
2.66 self.store(node.name, self.expr)
2.67 + self.reset_attributes(node.name)
2.68 self.use_name(node.name)
2.69 return None
2.70
2.71 @@ -497,7 +520,8 @@
2.72
2.73 visitBitxor = _visitBinary
2.74
2.75 - visitBreak = NOP
2.76 + def visitBreak(self, node):
2.77 + self.reset_all_attributes()
2.78
2.79 visitCallFunc = OP
2.80
2.81 @@ -512,7 +536,7 @@
2.82 print "Class %r in %r is not global: ignored." % (node.name, self.namespaces[-1].full_name())
2.83 return None
2.84 else:
2.85 - cls = Class(node.name, self.get_parent(), self, node)
2.86 + cls = Class(node.name, self.get_namespace(), self, node)
2.87
2.88 # Visit the base class expressions, attempting to find concrete
2.89 # definitions of classes.
2.90 @@ -576,7 +600,8 @@
2.91 self.use_name(self.importer.get_constant_type_name(node.value))
2.92 return self.importer.make_constant(node.value)
2.93
2.94 - visitContinue = NOP
2.95 + def visitContinue(self, node):
2.96 + self.reset_all_attributes()
2.97
2.98 visitDecorators = NOP
2.99
2.100 @@ -595,17 +620,34 @@
2.101 visitFloorDiv = _visitBinary
2.102
2.103 def visitFor(self, node):
2.104 + self.new_branchpoint()
2.105
2.106 # Declare names which will be used by generated code.
2.107
2.108 self.use_name("__iter__")
2.109 self.use_name("next")
2.110
2.111 + self.dispatch(node.assign)
2.112 + self.dispatch(node.list)
2.113 +
2.114 # Enter the loop.
2.115 + # Propagate attribute usage to branches.
2.116
2.117 self.in_loop = 1
2.118 - self.NOP(node)
2.119 + self.new_branch()
2.120 + self.dispatch(node.body)
2.121 + self.shelve_branch()
2.122 self.in_loop = 0
2.123 +
2.124 + # Maintain a branch for the else clause or the current retained usage
2.125 + # where execution avoids the conditional clauses.
2.126 +
2.127 + self.new_branch()
2.128 + if node.else_ is not None:
2.129 + self.dispatch(node.else_)
2.130 + self.shelve_branch()
2.131 +
2.132 + self.merge_branches()
2.133 return None
2.134
2.135 def visitFrom(self, node):
2.136 @@ -664,7 +706,8 @@
2.137
2.138 # Note usage of the attribute.
2.139
2.140 - node._attrnames = expr.use_attribute(node.attrname)
2.141 + if expr.parent is self.get_namespace():
2.142 + node._attrnames = self.use_attribute(expr, attrname)
2.143
2.144 elif self.builtins is not None:
2.145 attr = self.builtins.get(attrname)
2.146 @@ -687,11 +730,26 @@
2.147 # The name is recorded in an earlier process.
2.148
2.149 def visitIf(self, node):
2.150 + self.new_branchpoint()
2.151 +
2.152 + # Propagate attribute usage to branches.
2.153 +
2.154 for test, body in node.tests:
2.155 self.dispatch(test)
2.156 +
2.157 + self.new_branch()
2.158 self.dispatch(body)
2.159 + self.shelve_branch()
2.160 +
2.161 + # Maintain a branch for the else clause or the current retained usage
2.162 + # where execution avoids the conditional clauses.
2.163 +
2.164 + self.new_branch()
2.165 if node.else_ is not None:
2.166 self.dispatch(node.else_)
2.167 + self.shelve_branch()
2.168 +
2.169 + self.merge_branches()
2.170 return None
2.171
2.172 visitIfExp = NOP
2.173 @@ -792,7 +850,11 @@
2.174
2.175 def visitTryExcept(self, node):
2.176 self.dispatch(node.body)
2.177 +
2.178 + self.new_branchpoint()
2.179 +
2.180 for name, var, n in node.handlers:
2.181 + self.new_branch()
2.182
2.183 # Establish the local for the handler.
2.184
2.185 @@ -800,8 +862,15 @@
2.186 self.dispatch(var)
2.187 if n is not None:
2.188 self.dispatch(n)
2.189 +
2.190 + self.shelve_branch()
2.191 +
2.192 if node.else_ is not None:
2.193 + self.new_branch()
2.194 self.dispatch(node.else_)
2.195 + self.shelve_branch()
2.196 +
2.197 + self.merge_branches()
2.198 return None
2.199
2.200 visitTryFinally = NOP
2.201 @@ -813,9 +882,26 @@
2.202 visitUnarySub = _visitUnary
2.203
2.204 def visitWhile(self, node):
2.205 + self.new_branchpoint()
2.206 +
2.207 + # Propagate attribute usage to branches.
2.208 +
2.209 self.in_loop = 1
2.210 - self.NOP(node)
2.211 + self.dispatch(node.test)
2.212 + self.new_branch()
2.213 + self.dispatch(node.body)
2.214 + self.shelve_branch()
2.215 self.in_loop = 0
2.216 +
2.217 + # Maintain a branch for the else clause or the current retained usage
2.218 + # where execution avoids the conditional clauses.
2.219 +
2.220 + self.new_branch()
2.221 + if node.else_ is not None:
2.222 + self.dispatch(node.else_)
2.223 + self.shelve_branch()
2.224 +
2.225 + self.merge_branches()
2.226 return None
2.227
2.228 visitWith = NOP
3.1 --- a/tests/attribute_access_type_restriction.py Fri Nov 06 01:07:48 2009 +0100
3.2 +++ b/tests/attribute_access_type_restriction.py Fri Nov 06 01:17:07 2009 +0100
3.3 @@ -11,34 +11,63 @@
3.4 def g(self):
3.5 return 3
3.6
3.7 +class E:
3.8 + def f(self):
3.9 + return 4
3.10 +
3.11 + def h(self):
3.12 + return 5
3.13 +
3.14 def test_one(obj):
3.15 - obj.f()
3.16 - return obj.g()
3.17 + obj.f() # C, D, E -> D
3.18 + return obj.g() # D
3.19 + # obj: D
3.20 +
3.21 +def test_two(obj, obj2):
3.22 + if obj.f(): # C, D, E (f)
3.23 + obj.g() # D (f, g)
3.24 + # else:
3.25 + # ... # obj: C, D, E (f)
3.26 + # # (f, g) ^ (f)
3.27 + return 2
3.28 + # obj: C, D, E (f)
3.29 +
3.30 +def test_new(obj, obj2):
3.31 + if obj.f(): # C, D, E (f)
3.32 + obj = obj2
3.33 + obj.g() # D (g)
3.34 + # else:
3.35 + # ... # obj: C, D, E (f)
3.36 + # # (g) ^ (f)
3.37 + return obj.f() # C, D, E (f)
3.38 + # obj: C, D, E (f)
3.39
3.40 def test_neither(obj, obj2):
3.41 - if obj.f():
3.42 - obj = obj2
3.43 - obj.g()
3.44 - return 2
3.45 + if 0:
3.46 + obj.g() # D (g)
3.47 + else:
3.48 + obj.f() # C, D, E (f)
3.49 + # # (g) ^ (f)
3.50 + return 4
3.51 + # obj:
3.52
3.53 -def test_either(obj, obj2):
3.54 - if obj:
3.55 +def test_three(obj, obj2):
3.56 + if obj.f(): # C, D, E (f)
3.57 obj = obj2
3.58 - obj.g()
3.59 - return obj.f()
3.60 -
3.61 -def test_neither2(obj, obj2):
3.62 - if obj:
3.63 - obj.g()
3.64 + obj.g() # D (g)
3.65 else:
3.66 - obj.f()
3.67 - return 4
3.68 + obj.h() # E (f, h)
3.69 + # # (g) ^ (f, h)
3.70 + return 5
3.71 + # obj:
3.72
3.73 c = C()
3.74 d = D()
3.75 +e = E()
3.76 result1_3 = test_one(d)
3.77 -result1_2 = test_neither(c, d)
3.78 -result2_2 = test_either(c, d)
3.79 -result1_4 = test_neither2(c, d)
3.80 +result1_2 = test_two(c, d)
3.81 +result2_2 = test_new(c, d)
3.82 +result1_4 = test_neither(c, d)
3.83 +result1_5 = test_three(e, d)
3.84
3.85 # vim: tabstop=4 expandtab shiftwidth=4