# HG changeset patch # User Paul Boddie # Date 1258328087 -3600 # Node ID dd6568a7c46e5f72c4971b64a7aaf89483c8af8e # Parent 625d2aef3edd561339be2746d6bbce4f00160468 Changed the _attrnames annotation to map a number of names to attributes, thus permitting such annotations for many names on Function nodes. Added type guard generation, preventing inappropriate types from being supplied to optimised instructions. Separated some of the testing into more specific success and failure tests. diff -r 625d2aef3edd -r dd6568a7c46e micropython/ast.py --- a/micropython/ast.py Sat Nov 07 02:36:33 2009 +0100 +++ b/micropython/ast.py Mon Nov 16 00:34:47 2009 +0100 @@ -507,6 +507,10 @@ self._visitName(node, self.name_store_instructions) self.set_source() + # Add any attribute usage guards. + + self._generateGuards(node) + visitAssTuple = visitAssList def visitAugAssign(self, node): @@ -629,8 +633,17 @@ self.new_op(CheckExtra(nparams)) self.new_op(StoreTemp(nparams)) + # Add any attribute usage guards. + + if self.optimiser.should_optimise_accesses_by_attribute_usage() and hasattr(node, "_attrnames"): + self._generateGuards(node) + + # Visit the actual code. + self.dispatch(node.code) + # Add a return statement where one is not already produced. + if not isinstance(self.last_op(), Return): self.dispatch(compiler.ast.Name("None")) self.new_op(StoreResult()) diff -r 625d2aef3edd -r dd6568a7c46e micropython/data.py --- a/micropython/data.py Sat Nov 07 02:36:33 2009 +0100 +++ b/micropython/data.py Mon Nov 16 00:34:47 2009 +0100 @@ -286,11 +286,23 @@ return defs[name] def _define_attribute_user(self, node): + + """ + Define 'node' as the user of attributes, indicating the point where the + user is defined. + """ + name = node.name + self._define_attribute_user_for_name(node, name) + + def _define_attribute_user_for_name(self, node, name): defs = self.attributes_used[-1] users = self.attribute_users[-1] users[name] = node - users[name]._attrnames = defs[name] = set() + defs[name] = set() + if not hasattr(node, "_attrnames"): + node._attrnames = {} + node._attrnames[name] = defs[name] def _reset_all_attributes(self): self.attributes_used[-1] = {} @@ -945,7 +957,16 @@ # Initialise attribute usage. for arg in argnames: - self.attributes_used[-1][arg] = set() + + # Define attribute users. + + if node is not None: + self._define_attribute_user_for_name(node, arg) + + # Or just record the usage. + + else: + self.attributes_used[-1][arg] = set() # Caches. diff -r 625d2aef3edd -r dd6568a7c46e micropython/inspect.py --- a/micropython/inspect.py Sat Nov 07 02:36:33 2009 +0100 +++ b/micropython/inspect.py Mon Nov 16 00:34:47 2009 +0100 @@ -306,6 +306,10 @@ unit = self.get_namespace() self.importer.use_name(name, unit.name) + # Attribute usage methods. + # These are convenience methods which refer to the specific namespace's + # implementation of these operations. + def new_branchpoint(self): self.get_namespace()._new_branchpoint() @@ -319,6 +323,12 @@ self.get_namespace()._merge_branches() def define_attribute_user(self, node): + + """ + Define 'node' as the user of attributes, indicating the point where the + user is defined. + """ + self.get_namespace()._define_attribute_user(node) def reset_all_attributes(self): diff -r 625d2aef3edd -r dd6568a7c46e micropython/trans.py --- a/micropython/trans.py Sat Nov 07 02:36:33 2009 +0100 +++ b/micropython/trans.py Mon Nov 16 00:34:47 2009 +0100 @@ -306,6 +306,56 @@ # Common methods. + def _generateGuards(self, node): + + if not (self.optimiser.should_optimise_accesses_by_attribute_usage() and hasattr(node, "_attrnames")): + return + + # For each name, attempt to restrict the type employed. + + for name, names_used in node._attrnames.items(): + + # Get the names of all object types supporting these names. + + target_names = self.objtable.all_possible_objects(names_used) + + # Where only one object type is suggested, produce a guard. + # NOTE: This only supports classes as types, not modules. + + if len(target_names) == 1: + target_name = target_names[0] + + # Access the object table to get the attribute. + # NOTE: This depends on the special entry in the table + # NOTE: for class equivalence tests. + + try: + attr = self.objtable.access(target_name, target_name) + except TableError, exc: + raise TranslateError(self.module.full_name(), node, exc.args[0]) + + after_test_block = self.new_block() + + # Generate isinstance(name, target). + + self.new_op(LoadClass(attr)) + temp_target = self.optimiser.optimise_temp_storage() + self.dispatch(compiler.ast.Name(name)) + self.new_op(CheckSelf()) # NOTE: Should be CheckInstance. + self.optimiser.set_source(temp_target) + + # Jump to the next guard or the code if successful. + + self.new_op(JumpIfTrue(after_test_block)) + + # Where the type is inappropriate, raise an exception. + + self.make_exception("TypeError", node) + self.new_op(StoreException()) + self.new_op(RaiseException()) + + self.set_block(after_test_block) + def _visitAttr(self, node, classes): """ @@ -424,11 +474,15 @@ except KeyError: pass + # Attempt to deduce the target of an attribute access by searching for a + # unique type providing the names associated with the accessed object. + # NOTE: This should re-use type information defined at assignment + # NOTE: locations. + elif self.optimiser.should_optimise_accesses_by_attribute_usage(): if hasattr(node, "_attrnames"): target_names = self.objtable.all_possible_objects(node._attrnames) - print self.expr_temp, node._attrnames, "->", target_names if len(target_names) == 1: target_name = target_names[0] diff -r 625d2aef3edd -r dd6568a7c46e tests/attribute_access_type_restriction.py --- a/tests/attribute_access_type_restriction.py Sat Nov 07 02:36:33 2009 +0100 +++ b/tests/attribute_access_type_restriction.py Mon Nov 16 00:34:47 2009 +0100 @@ -18,56 +18,53 @@ def h(self): return 5 -def test_one(obj): - obj.f() # C, D, E -> D - return obj.g() # D - # obj: D - -def test_two(obj, obj2): +def test_conditional(obj): + # obj: C, D, E (f) if obj.f(): # C, D, E (f) obj.g() # D (f, g) # else: # ... # obj: C, D, E (f) # # (f, g) ^ (f) return 2 - # obj: C, D, E (f) def test_new(obj, obj2): + # obj: C, D, E (f) + # obj2: if obj.f(): # C, D, E (f) - obj = obj2 + obj = obj2 # obj: D (g) obj.g() # D (g) # else: # ... # obj: C, D, E (f) # # (g) ^ (f) return obj.f() # C, D, E (f) - # obj: C, D, E (f) def test_neither(obj, obj2): + # obj: + # obj2: if 0: obj.g() # D (g) else: obj.f() # C, D, E (f) # # (g) ^ (f) return 4 + +def test_new_conditional(obj, obj2): # obj: - -def test_three(obj, obj2): + # obj2: if obj.f(): # C, D, E (f) - obj = obj2 + obj = obj2 # obj: D (g) obj.g() # D (g) else: obj.h() # E (f, h) # # (g) ^ (f, h) return 5 - # obj: c = C() d = D() e = E() -result1_3 = test_one(d) -result1_2 = test_two(c, d) +result1_2 = test_conditional(d) result2_2 = test_new(c, d) result1_4 = test_neither(c, d) -result1_5 = test_three(e, d) +result1_5 = test_new_conditional(e, d) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 625d2aef3edd -r dd6568a7c46e tests/attribute_access_type_restriction_single.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/attribute_access_type_restriction_single.py Mon Nov 16 00:34:47 2009 +0100 @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +class C: + def f(self): # unused + return 1 + +class D: + def f(self): + return 2 + + def g(self): + return 3 + +class E: + def f(self): # unused + return 4 + + def h(self): # unused + return 5 + +def test_one(obj): + # obj: D (f, g) + obj.f() # C, D, E (f) + return obj.g() # D (f, g) + +c = C() +d = D() +e = E() +result1_3 = test_one(d) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 625d2aef3edd -r dd6568a7c46e tests/failure/attribute_access_type_restriction_single_inappropriate.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/failure/attribute_access_type_restriction_single_inappropriate.py Mon Nov 16 00:34:47 2009 +0100 @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +class C: + def f(self): # unused + return 1 + +class D: + def f(self): + return 2 + + def g(self): + return 3 + +class E: + def f(self): # unused + return 4 + + def h(self): # unused + return 5 + +def test_one(obj): + # obj: D (f, g) + obj.f() # C, D, E (f) + return obj.g() # D (f, g) + +c = C() +d = D() +e = E() +result1_3 = test_one(c) + +# vim: tabstop=4 expandtab shiftwidth=4