1.1 --- a/micropython/data.py Sun Oct 25 18:35:53 2009 +0100
1.2 +++ b/micropython/data.py Sat Oct 31 22:59:35 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,48 @@
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 +
1.40 + def _reset_attributes(self, name):
1.41 + defs = self.attributes_used[-1]
1.42 + defs[name] = set()
1.43 +
1.44 + def _new_branchpoint(self):
1.45 + self.attribute_shelves.append([])
1.46 +
1.47 + def _new_branch(self):
1.48 + d = {}
1.49 + for name, attrnames in self.attributes_used[-1].items():
1.50 + d[name] = set(attrnames)
1.51 + self.attributes_used.append(d)
1.52 +
1.53 + def _shelve_branch(self):
1.54 + self.attribute_shelves[-1].append(self.attributes_used.pop())
1.55 +
1.56 + def _merge_branches(self):
1.57 + active = self.attributes_used[-1]
1.58 +
1.59 + shelved_defs = self.attribute_shelves.pop()
1.60 + defs = shelved_defs[0]
1.61 +
1.62 + for next_defs in shelved_defs[1:]:
1.63 + for name, attrnames in next_defs.items():
1.64 + if defs.has_key(name):
1.65 + defs[name].intersection_update(attrnames)
1.66 +
1.67 + for name, attrnames in defs.items():
1.68 + if active.has_key(name):
1.69 + active[name].intersection_update(attrnames)
1.70 + else:
1.71 + active[name] = attrnames
1.72 +
1.73 # Program data structures. There are two separate kinds of structures: those
1.74 # with context, which are the values manipulated by programs, and those without
1.75 # context, which are typically constant things which are stored alongside the
1.76 @@ -286,6 +338,8 @@
1.77 self.parent = parent
1.78 self.name = name
1.79
1.80 + # Possible values.
1.81 +
1.82 self.context_values = set()
1.83
1.84 # Number of assignments per name.
1.85 @@ -791,8 +845,6 @@
1.86 name, pos = held.pop()
1.87 namearray[i] = name
1.88
1.89 - #print self.name, positions
1.90 - #print "->", namearray
1.91 return namearray
1.92
1.93 def _cmp_positions(self, a, b):
1.94 @@ -878,6 +930,11 @@
1.95
1.96 self.default_attrs = []
1.97
1.98 + # Initialise attribute usage.
1.99 +
1.100 + for arg in argnames:
1.101 + self.attributes_used[-1][arg] = set()
1.102 +
1.103 # Caches.
1.104
1.105 self.localnames = None # cache for locals
2.1 --- a/micropython/inspect.py Sun Oct 25 18:35:53 2009 +0100
2.2 +++ b/micropython/inspect.py Sat Oct 31 22:59:35 2009 +0100
2.3 @@ -286,7 +286,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 @@ -296,9 +296,27 @@
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 use_attribute(self, attr, attrname):
2.36 + self.get_namespace()._use_attribute(attr, attrname)
2.37 +
2.38 # Visitor methods.
2.39
2.40 def default(self, node, *args):
2.41 @@ -346,7 +364,7 @@
2.42
2.43 function = Function(
2.44 name,
2.45 - self.get_parent(),
2.46 + self.get_namespace(),
2.47 node.argnames,
2.48 node.defaults,
2.49 (node.flags & 4 != 0),
2.50 @@ -417,6 +435,9 @@
2.51
2.52 def visitAssAttr(self, node):
2.53 expr = self.dispatch(node.expr)
2.54 +
2.55 + # Record the attribute on the presumed target.
2.56 +
2.57 if isinstance(expr, Attr):
2.58 if expr.name == "self":
2.59 if not self.store_class_attr(node.attrname):
2.60 @@ -424,6 +445,12 @@
2.61 elif isinstance(expr.get_value(), Module):
2.62 self.store_module_attr(node.attrname, expr.get_value())
2.63 print "Warning: attribute %r of module %r set outside the module." % (node.attrname, expr.get_value().name)
2.64 +
2.65 + # Note usage of the attribute.
2.66 +
2.67 + if expr.parent is self.get_namespace():
2.68 + self.use_attribute(expr, node.attrname)
2.69 +
2.70 return None
2.71
2.72 def visitAssList(self, node):
2.73 @@ -441,6 +468,7 @@
2.74
2.75 def visitAssName(self, node):
2.76 self.store(node.name, self.expr)
2.77 + self.reset_attributes(node.name)
2.78 self.use_name(node.name)
2.79 return None
2.80
2.81 @@ -494,7 +522,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 @@ -577,17 +605,30 @@
2.91 visitFloorDiv = _visitBinary
2.92
2.93 def visitFor(self, node):
2.94 + self.new_branchpoint()
2.95
2.96 # Declare names which will be used by generated code.
2.97
2.98 self.use_name("__iter__")
2.99 self.use_name("next")
2.100
2.101 + self.dispatch(node.assign)
2.102 + self.dispatch(node.list)
2.103 +
2.104 # Enter the loop.
2.105
2.106 self.in_loop = 1
2.107 - self.NOP(node)
2.108 + self.new_branch()
2.109 + self.dispatch(node.body)
2.110 + self.shelve_branch()
2.111 self.in_loop = 0
2.112 +
2.113 + if node.else_ is not None:
2.114 + self.new_branch()
2.115 + self.dispatch(node.else_)
2.116 + self.shelve_branch()
2.117 +
2.118 + self.merge_branches()
2.119 return None
2.120
2.121 def visitFrom(self, node):
2.122 @@ -633,6 +674,8 @@
2.123 expr = self.dispatch(node.expr)
2.124 attrname = node.attrname
2.125
2.126 + # Attempt to identify the nature of the attribute.
2.127 +
2.128 if isinstance(expr, Attr):
2.129 value = expr.get_value()
2.130 if isinstance(value, (Class, Module)):
2.131 @@ -641,6 +684,12 @@
2.132 attr = UnresolvedName(attrname, value.full_name(), self)
2.133 else:
2.134 attr = None
2.135 +
2.136 + # Note usage of the attribute.
2.137 +
2.138 + if expr.parent is self.get_namespace():
2.139 + self.use_attribute(expr, attrname)
2.140 +
2.141 elif self.builtins is not None:
2.142 attr = self.builtins.get(attrname)
2.143 else:
2.144 @@ -662,11 +711,21 @@
2.145 # The name is recorded in an earlier process.
2.146
2.147 def visitIf(self, node):
2.148 + self.new_branchpoint()
2.149 +
2.150 for test, body in node.tests:
2.151 self.dispatch(test)
2.152 +
2.153 + self.new_branch()
2.154 self.dispatch(body)
2.155 + self.shelve_branch()
2.156 +
2.157 if node.else_ is not None:
2.158 + self.new_branch()
2.159 self.dispatch(node.else_)
2.160 + self.shelve_branch()
2.161 +
2.162 + self.merge_branches()
2.163 return None
2.164
2.165 visitIfExp = NOP
2.166 @@ -767,7 +826,11 @@
2.167
2.168 def visitTryExcept(self, node):
2.169 self.dispatch(node.body)
2.170 +
2.171 + self.new_branchpoint()
2.172 +
2.173 for name, var, n in node.handlers:
2.174 + self.new_branch()
2.175
2.176 # Establish the local for the handler.
2.177
2.178 @@ -775,8 +838,15 @@
2.179 self.dispatch(var)
2.180 if n is not None:
2.181 self.dispatch(n)
2.182 +
2.183 + self.shelve_branch()
2.184 +
2.185 if node.else_ is not None:
2.186 + self.new_branch()
2.187 self.dispatch(node.else_)
2.188 + self.shelve_branch()
2.189 +
2.190 + self.merge_branches()
2.191 return None
2.192
2.193 visitTryFinally = NOP
2.194 @@ -788,9 +858,21 @@
2.195 visitUnarySub = _visitUnary
2.196
2.197 def visitWhile(self, node):
2.198 + self.new_branchpoint()
2.199 +
2.200 self.in_loop = 1
2.201 - self.NOP(node)
2.202 + self.dispatch(node.test)
2.203 + self.new_branch()
2.204 + self.dispatch(node.body)
2.205 + self.shelve_branch()
2.206 self.in_loop = 0
2.207 +
2.208 + if node.else_ is not None:
2.209 + self.new_branch()
2.210 + self.dispatch(node.else_)
2.211 + self.shelve_branch()
2.212 +
2.213 + self.merge_branches()
2.214 return None
2.215
2.216 visitWith = NOP
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/tests/attribute_access_type_restriction.py Sat Oct 31 22:59:35 2009 +0100
3.3 @@ -0,0 +1,44 @@
3.4 +#!/usr/bin/env python
3.5 +
3.6 +class C:
3.7 + def f(self):
3.8 + return 1
3.9 +
3.10 +class D:
3.11 + def f(self):
3.12 + return 2
3.13 +
3.14 + def g(self):
3.15 + return 3
3.16 +
3.17 +def test_one(obj):
3.18 + obj.f()
3.19 + return obj.g()
3.20 +
3.21 +def test_neither(obj, obj2):
3.22 + if obj.f():
3.23 + obj = obj2
3.24 + obj.g()
3.25 + return 2
3.26 +
3.27 +def test_either(obj, obj2):
3.28 + if obj:
3.29 + obj = obj2
3.30 + obj.g()
3.31 + return obj.f()
3.32 +
3.33 +def test_neither2(obj, obj2):
3.34 + if obj:
3.35 + obj.g()
3.36 + else:
3.37 + obj.f()
3.38 + return 4
3.39 +
3.40 +c = C()
3.41 +d = D()
3.42 +result1_3 = test_one(d)
3.43 +result1_2 = test_neither(c, d)
3.44 +result2_2 = test_either(c, d)
3.45 +result1_4 = test_neither2(c, d)
3.46 +
3.47 +# vim: tabstop=4 expandtab shiftwidth=4