# HG changeset patch # User Paul Boddie # Date 1265587523 -3600 # Node ID feccb2b0967ea6834fe46d44397dfb282f66a600 # Parent a72712268c10791f5913cd8e730129987a474e75 Added control-flow branch abandonment for statements like break, continue, raise and return, preventing attribute usage from such branches from contributing to subsequent usage observations. Split the initial attribute usage test into smaller tests. Added notes about testing guard suitability. diff -r a72712268c10 -r feccb2b0967e micropython/data.py --- a/micropython/data.py Sun Feb 07 03:02:39 2010 +0100 +++ b/micropython/data.py Mon Feb 08 01:05:23 2010 +0100 @@ -3,7 +3,7 @@ """ Data classes. -Copyright (C) 2007, 2008, 2009 Paul Boddie +Copyright (C) 2007, 2008, 2009, 2010 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -126,6 +126,7 @@ self.attributes_used = [{}] # stack of usage self.attribute_shelves = [] # stack of unmerged definitions self.attribute_users = [{}] # stack of assignments + self.abandon_attributes = 0 # used when a block will never contribute self.all_attributes_used = [] # Attribute/name definition and access. @@ -353,10 +354,6 @@ self.all_attributes_used.append(defs[name]) - def _reset_all_attributes(self): - self.attributes_used[-1] = {} - self.attribute_users[-1] = {} - def _new_branchpoint(self): self.attribute_shelves.append([]) @@ -367,9 +364,14 @@ self.attributes_used.append(d) self.attribute_users.append({}) + def _abandon_branch(self): + self.abandon_attributes = 1 + def _shelve_branch(self): - self.attribute_shelves[-1].append(self.attributes_used.pop()) + if not self.abandon_attributes: + self.attribute_shelves[-1].append(self.attributes_used.pop()) self.attribute_users.pop() + self.abandon_attributes = 0 def _merge_branches(self): active = self.attributes_used[-1] @@ -378,6 +380,13 @@ # intersection of their contributions for each name. shelved_defs = self.attribute_shelves.pop() + + # Where branches contribute attribute usage observations, process these + # as described above. Otherwise, preserve the previous observations. + + if not shelved_defs: + return + defs = dict(shelved_defs[0]) for next_defs in shelved_defs[1:]: diff -r a72712268c10 -r feccb2b0967e micropython/inspect.py --- a/micropython/inspect.py Sun Feb 07 03:02:39 2010 +0100 +++ b/micropython/inspect.py Mon Feb 08 01:05:23 2010 +0100 @@ -3,7 +3,7 @@ """ Inspect source files, obtaining details of classes and attributes. -Copyright (C) 2007, 2008, 2009 Paul Boddie +Copyright (C) 2007, 2008, 2009, 2010 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -332,6 +332,9 @@ def new_branch(self): self.get_namespace()._new_branch() + def abandon_branch(self): + self.get_namespace()._abandon_branch() + def shelve_branch(self): self.get_namespace()._shelve_branch() @@ -347,9 +350,6 @@ self.get_namespace()._define_attribute_user(node) - def reset_all_attributes(self): - self.get_namespace()._reset_all_attributes() - def use_attribute(self, name, attrname): return self.get_namespace()._use_attribute(name, attrname) @@ -374,6 +374,10 @@ self.dispatch(n) return None + def NOP_ABANDON(self, node): + self.abandon_branch() + self.NOP(node) + def OP(self, node): for n in node.getChildNodes(): self.dispatch(n) @@ -558,8 +562,7 @@ visitBitxor = _visitBinary - def visitBreak(self, node): - self.reset_all_attributes() + visitBreak = NOP_ABANDON visitCallFunc = OP @@ -639,8 +642,7 @@ self.use_name(self.importer.get_constant_type_name(node.value)) return self.importer.make_constant(node.value) - def visitContinue(self, node): - self.reset_all_attributes() + visitContinue = NOP_ABANDON visitDecorators = NOP @@ -895,9 +897,9 @@ visitPrintnl = NOP - visitRaise = NOP + visitRaise = NOP_ABANDON - visitReturn = NOP + visitReturn = NOP_ABANDON visitRightShift = _visitBinary diff -r a72712268c10 -r feccb2b0967e micropython/trans.py --- a/micropython/trans.py Sun Feb 07 03:02:39 2010 +0100 +++ b/micropython/trans.py Mon Feb 08 01:05:23 2010 +0100 @@ -3,7 +3,7 @@ """ Translate the AST of a Python program into a more interpretable representation. -Copyright (C) 2007, 2008, 2009 Paul Boddie +Copyright (C) 2007, 2008, 2009, 2010 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -330,7 +330,7 @@ # NOTE: for class equivalence tests. try: - attr = self.objtable.access(target_name, target_name) + obj = self.objtable.access(target_name, target_name) # Where no attribute entry exists, the target could be a module. @@ -338,9 +338,17 @@ print "Possible guard for", target_name, "not enforceable." continue + # NOTE: Could test the correctness of the guard where the nature + # NOTE: of the name is known. + # NOTE: The known value would be retrieved from the unit's + # NOTE: locals and tested as being a class or an instance of a + # NOTE: particular class. + + # Generate the guard by loading a reference to the class. + after_test_block = self.new_block() - self.new_op(LoadClass(attr)) + self.new_op(LoadClass(obj)) temp_target = self.optimiser.optimise_temp_storage() # For only static attributes, classes are acceptable. diff -r a72712268c10 -r feccb2b0967e tests/attribute_access_type_restriction.py --- a/tests/attribute_access_type_restriction.py Sun Feb 07 03:02:39 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -#!/usr/bin/env python - -class C: - def f(self): - return 1 - -class D: - def f(self): - return 2 - - def g(self): - return 3 - -class E: - def f(self): - return 4 - - def h(self): - return 5 - -def test_new(obj, obj2): - # obj: C, D, E (f) - # obj2: - if obj.f(): # C, D, E (f) - obj = obj2 # obj: D (g) - obj.g() # D (g) - # else: - # ... # obj: C, D, E (f) - # # (g) ^ (f) - return obj.f() # 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: - # obj2: - if obj.f(): # C, D, E (f) - obj = obj2 # obj: D (g) - obj.g() # D (g) - else: - obj.h() # E (f, h) - # # (g) ^ (f, h) - return 5 - -c = C() -d = D() -e = E() -result2_2 = test_new(c, d) -result1_4 = test_neither(c, d) -result1_5 = test_new_conditional(e, d) - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r a72712268c10 -r feccb2b0967e tests/attribute_access_type_restriction_neither.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/attribute_access_type_restriction_neither.py Mon Feb 08 01:05:23 2010 +0100 @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +class C: + def f(self): + return 1 + +class D: + def f(self): + return 2 + + def g(self): + return 3 + +class E: # unused + def f(self): + return 4 + + def h(self): + return 5 + +def test_neither(obj, obj2): + # obj: + # obj2: + if 0: + obj.g() # D (g) + else: + obj.f() # C, D, E (f) + # # (g) ^ (f) + return 4 + +c = C() +d = D() +result1_4 = test_neither(c, d) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r a72712268c10 -r feccb2b0967e tests/attribute_access_type_restriction_new.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/attribute_access_type_restriction_new.py Mon Feb 08 01:05:23 2010 +0100 @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +class C: + def f(self): + return 1 + +class D: + def f(self): + return 2 + + def g(self): + return 3 + +class E: + def f(self): + return 4 + + def h(self): # unused + return 5 + +def test_new(obj, obj2): + # obj: C, D, E (f) + # obj2: + if obj.f(): # C, D, E (f) + obj = obj2 # obj: D (g) + obj.g() # D (g) + # else: + # ... # obj: C, D, E (f) + # # (g) ^ (f) + return obj.f() # C, D, E (f) + +c = C() +d = D() +e = E() +result2_2 = test_new(c, d) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r a72712268c10 -r feccb2b0967e tests/attribute_access_type_restriction_new_conditional.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/attribute_access_type_restriction_new_conditional.py Mon Feb 08 01:05:23 2010 +0100 @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +class C: + def f(self): + return 1 + +class D: + def f(self): + return 2 + + def g(self): + return 3 + +class E: + def f(self): + return 4 + + def h(self): + return 5 + +def test_new_conditional(obj, obj2): + # obj: + # obj2: + if obj.f(): # C, D, E (f) + obj = obj2 # obj: D (g) + obj.g() # D (g) + else: + obj.h() # E (f, h) + # # (g) ^ (f, h) + return 5 + +c = C() +d = D() +e = E() +result1_5 = test_new_conditional(e, d) + +# vim: tabstop=4 expandtab shiftwidth=4