1.1 --- a/micropython/ast.py Sat Nov 07 02:36:33 2009 +0100
1.2 +++ b/micropython/ast.py Mon Nov 16 00:34:47 2009 +0100
1.3 @@ -507,6 +507,10 @@
1.4 self._visitName(node, self.name_store_instructions)
1.5 self.set_source()
1.6
1.7 + # Add any attribute usage guards.
1.8 +
1.9 + self._generateGuards(node)
1.10 +
1.11 visitAssTuple = visitAssList
1.12
1.13 def visitAugAssign(self, node):
1.14 @@ -629,8 +633,17 @@
1.15 self.new_op(CheckExtra(nparams))
1.16 self.new_op(StoreTemp(nparams))
1.17
1.18 + # Add any attribute usage guards.
1.19 +
1.20 + if self.optimiser.should_optimise_accesses_by_attribute_usage() and hasattr(node, "_attrnames"):
1.21 + self._generateGuards(node)
1.22 +
1.23 + # Visit the actual code.
1.24 +
1.25 self.dispatch(node.code)
1.26
1.27 + # Add a return statement where one is not already produced.
1.28 +
1.29 if not isinstance(self.last_op(), Return):
1.30 self.dispatch(compiler.ast.Name("None"))
1.31 self.new_op(StoreResult())
2.1 --- a/micropython/data.py Sat Nov 07 02:36:33 2009 +0100
2.2 +++ b/micropython/data.py Mon Nov 16 00:34:47 2009 +0100
2.3 @@ -286,11 +286,23 @@
2.4 return defs[name]
2.5
2.6 def _define_attribute_user(self, node):
2.7 +
2.8 + """
2.9 + Define 'node' as the user of attributes, indicating the point where the
2.10 + user is defined.
2.11 + """
2.12 +
2.13 name = node.name
2.14 + self._define_attribute_user_for_name(node, name)
2.15 +
2.16 + def _define_attribute_user_for_name(self, node, name):
2.17 defs = self.attributes_used[-1]
2.18 users = self.attribute_users[-1]
2.19 users[name] = node
2.20 - users[name]._attrnames = defs[name] = set()
2.21 + defs[name] = set()
2.22 + if not hasattr(node, "_attrnames"):
2.23 + node._attrnames = {}
2.24 + node._attrnames[name] = defs[name]
2.25
2.26 def _reset_all_attributes(self):
2.27 self.attributes_used[-1] = {}
2.28 @@ -945,7 +957,16 @@
2.29 # Initialise attribute usage.
2.30
2.31 for arg in argnames:
2.32 - self.attributes_used[-1][arg] = set()
2.33 +
2.34 + # Define attribute users.
2.35 +
2.36 + if node is not None:
2.37 + self._define_attribute_user_for_name(node, arg)
2.38 +
2.39 + # Or just record the usage.
2.40 +
2.41 + else:
2.42 + self.attributes_used[-1][arg] = set()
2.43
2.44 # Caches.
2.45
3.1 --- a/micropython/inspect.py Sat Nov 07 02:36:33 2009 +0100
3.2 +++ b/micropython/inspect.py Mon Nov 16 00:34:47 2009 +0100
3.3 @@ -306,6 +306,10 @@
3.4 unit = self.get_namespace()
3.5 self.importer.use_name(name, unit.name)
3.6
3.7 + # Attribute usage methods.
3.8 + # These are convenience methods which refer to the specific namespace's
3.9 + # implementation of these operations.
3.10 +
3.11 def new_branchpoint(self):
3.12 self.get_namespace()._new_branchpoint()
3.13
3.14 @@ -319,6 +323,12 @@
3.15 self.get_namespace()._merge_branches()
3.16
3.17 def define_attribute_user(self, node):
3.18 +
3.19 + """
3.20 + Define 'node' as the user of attributes, indicating the point where the
3.21 + user is defined.
3.22 + """
3.23 +
3.24 self.get_namespace()._define_attribute_user(node)
3.25
3.26 def reset_all_attributes(self):
4.1 --- a/micropython/trans.py Sat Nov 07 02:36:33 2009 +0100
4.2 +++ b/micropython/trans.py Mon Nov 16 00:34:47 2009 +0100
4.3 @@ -306,6 +306,56 @@
4.4
4.5 # Common methods.
4.6
4.7 + def _generateGuards(self, node):
4.8 +
4.9 + if not (self.optimiser.should_optimise_accesses_by_attribute_usage() and hasattr(node, "_attrnames")):
4.10 + return
4.11 +
4.12 + # For each name, attempt to restrict the type employed.
4.13 +
4.14 + for name, names_used in node._attrnames.items():
4.15 +
4.16 + # Get the names of all object types supporting these names.
4.17 +
4.18 + target_names = self.objtable.all_possible_objects(names_used)
4.19 +
4.20 + # Where only one object type is suggested, produce a guard.
4.21 + # NOTE: This only supports classes as types, not modules.
4.22 +
4.23 + if len(target_names) == 1:
4.24 + target_name = target_names[0]
4.25 +
4.26 + # Access the object table to get the attribute.
4.27 + # NOTE: This depends on the special entry in the table
4.28 + # NOTE: for class equivalence tests.
4.29 +
4.30 + try:
4.31 + attr = self.objtable.access(target_name, target_name)
4.32 + except TableError, exc:
4.33 + raise TranslateError(self.module.full_name(), node, exc.args[0])
4.34 +
4.35 + after_test_block = self.new_block()
4.36 +
4.37 + # Generate isinstance(name, target).
4.38 +
4.39 + self.new_op(LoadClass(attr))
4.40 + temp_target = self.optimiser.optimise_temp_storage()
4.41 + self.dispatch(compiler.ast.Name(name))
4.42 + self.new_op(CheckSelf()) # NOTE: Should be CheckInstance.
4.43 + self.optimiser.set_source(temp_target)
4.44 +
4.45 + # Jump to the next guard or the code if successful.
4.46 +
4.47 + self.new_op(JumpIfTrue(after_test_block))
4.48 +
4.49 + # Where the type is inappropriate, raise an exception.
4.50 +
4.51 + self.make_exception("TypeError", node)
4.52 + self.new_op(StoreException())
4.53 + self.new_op(RaiseException())
4.54 +
4.55 + self.set_block(after_test_block)
4.56 +
4.57 def _visitAttr(self, node, classes):
4.58
4.59 """
4.60 @@ -424,11 +474,15 @@
4.61 except KeyError:
4.62 pass
4.63
4.64 + # Attempt to deduce the target of an attribute access by searching for a
4.65 + # unique type providing the names associated with the accessed object.
4.66 + # NOTE: This should re-use type information defined at assignment
4.67 + # NOTE: locations.
4.68 +
4.69 elif self.optimiser.should_optimise_accesses_by_attribute_usage():
4.70
4.71 if hasattr(node, "_attrnames"):
4.72 target_names = self.objtable.all_possible_objects(node._attrnames)
4.73 - print self.expr_temp, node._attrnames, "->", target_names
4.74
4.75 if len(target_names) == 1:
4.76 target_name = target_names[0]
5.1 --- a/tests/attribute_access_type_restriction.py Sat Nov 07 02:36:33 2009 +0100
5.2 +++ b/tests/attribute_access_type_restriction.py Mon Nov 16 00:34:47 2009 +0100
5.3 @@ -18,56 +18,53 @@
5.4 def h(self):
5.5 return 5
5.6
5.7 -def test_one(obj):
5.8 - obj.f() # C, D, E -> D
5.9 - return obj.g() # D
5.10 - # obj: D
5.11 -
5.12 -def test_two(obj, obj2):
5.13 +def test_conditional(obj):
5.14 + # obj: C, D, E (f)
5.15 if obj.f(): # C, D, E (f)
5.16 obj.g() # D (f, g)
5.17 # else:
5.18 # ... # obj: C, D, E (f)
5.19 # # (f, g) ^ (f)
5.20 return 2
5.21 - # obj: C, D, E (f)
5.22
5.23 def test_new(obj, obj2):
5.24 + # obj: C, D, E (f)
5.25 + # obj2:
5.26 if obj.f(): # C, D, E (f)
5.27 - obj = obj2
5.28 + obj = obj2 # obj: D (g)
5.29 obj.g() # D (g)
5.30 # else:
5.31 # ... # obj: C, D, E (f)
5.32 # # (g) ^ (f)
5.33 return obj.f() # C, D, E (f)
5.34 - # obj: C, D, E (f)
5.35
5.36 def test_neither(obj, obj2):
5.37 + # obj:
5.38 + # obj2:
5.39 if 0:
5.40 obj.g() # D (g)
5.41 else:
5.42 obj.f() # C, D, E (f)
5.43 # # (g) ^ (f)
5.44 return 4
5.45 +
5.46 +def test_new_conditional(obj, obj2):
5.47 # obj:
5.48 -
5.49 -def test_three(obj, obj2):
5.50 + # obj2:
5.51 if obj.f(): # C, D, E (f)
5.52 - obj = obj2
5.53 + obj = obj2 # obj: D (g)
5.54 obj.g() # D (g)
5.55 else:
5.56 obj.h() # E (f, h)
5.57 # # (g) ^ (f, h)
5.58 return 5
5.59 - # obj:
5.60
5.61 c = C()
5.62 d = D()
5.63 e = E()
5.64 -result1_3 = test_one(d)
5.65 -result1_2 = test_two(c, d)
5.66 +result1_2 = test_conditional(d)
5.67 result2_2 = test_new(c, d)
5.68 result1_4 = test_neither(c, d)
5.69 -result1_5 = test_three(e, d)
5.70 +result1_5 = test_new_conditional(e, d)
5.71
5.72 # vim: tabstop=4 expandtab shiftwidth=4
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/tests/attribute_access_type_restriction_single.py Mon Nov 16 00:34:47 2009 +0100
6.3 @@ -0,0 +1,31 @@
6.4 +#!/usr/bin/env python
6.5 +
6.6 +class C:
6.7 + def f(self): # unused
6.8 + return 1
6.9 +
6.10 +class D:
6.11 + def f(self):
6.12 + return 2
6.13 +
6.14 + def g(self):
6.15 + return 3
6.16 +
6.17 +class E:
6.18 + def f(self): # unused
6.19 + return 4
6.20 +
6.21 + def h(self): # unused
6.22 + return 5
6.23 +
6.24 +def test_one(obj):
6.25 + # obj: D (f, g)
6.26 + obj.f() # C, D, E (f)
6.27 + return obj.g() # D (f, g)
6.28 +
6.29 +c = C()
6.30 +d = D()
6.31 +e = E()
6.32 +result1_3 = test_one(d)
6.33 +
6.34 +# vim: tabstop=4 expandtab shiftwidth=4
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/tests/failure/attribute_access_type_restriction_single_inappropriate.py Mon Nov 16 00:34:47 2009 +0100
7.3 @@ -0,0 +1,31 @@
7.4 +#!/usr/bin/env python
7.5 +
7.6 +class C:
7.7 + def f(self): # unused
7.8 + return 1
7.9 +
7.10 +class D:
7.11 + def f(self):
7.12 + return 2
7.13 +
7.14 + def g(self):
7.15 + return 3
7.16 +
7.17 +class E:
7.18 + def f(self): # unused
7.19 + return 4
7.20 +
7.21 + def h(self): # unused
7.22 + return 5
7.23 +
7.24 +def test_one(obj):
7.25 + # obj: D (f, g)
7.26 + obj.f() # C, D, E (f)
7.27 + return obj.g() # D (f, g)
7.28 +
7.29 +c = C()
7.30 +d = D()
7.31 +e = E()
7.32 +result1_3 = test_one(c)
7.33 +
7.34 +# vim: tabstop=4 expandtab shiftwidth=4