# HG changeset patch # User Paul Boddie # Date 1283814443 -7200 # Node ID b233b2a4883754064e6310eb8e37d49d34be293c # Parent bd11b8c03676d81b2bbeb4a1cf5dbadadeb87e11 Changed attribute usage tracking to consider branching and separate usage possibilities in a slightly more rigourous way. Attempted to add isinstance support. Added tests of the revised attribute usage support. diff -r bd11b8c03676 -r b233b2a48837 lib/builtins.py --- a/lib/builtins.py Mon Sep 06 01:08:58 2010 +0200 +++ b/lib/builtins.py Tue Sep 07 01:07:23 2010 +0200 @@ -427,7 +427,25 @@ def hex(number): pass def id(obj): pass def input(prompt=None): pass -def isinstance(obj, cls_or_tuple): pass + +def isinstance(obj, cls_or_tuple): + + """ + Return whether 'obj' is an instance of 'cls_or_tuple', where the latter is + either a class or a tuple of classes. + """ + + # NOTE: tuple.__class__ is tuple in micropython! + if cls_or_tuple is not tuple and cls_or_tuple.__class__ is tuple: + for cls in cls_or_tuple: + if _isinstance(obj, cls): + return True + return False + else: + return _isinstance(obj, cls_or_tuple) + +def _isinstance(obj, cls): pass + def issubclass(obj, cls_or_tuple): pass def iter(collection): diff -r bd11b8c03676 -r b233b2a48837 micropython/data.py --- a/micropython/data.py Mon Sep 06 01:08:58 2010 +0200 +++ b/micropython/data.py Tue Sep 07 01:07:23 2010 +0200 @@ -421,17 +421,13 @@ with the given 'attrname'. """ - for users in (self.attribute_users[-1], self.loop_users[-1]): - - # Add the usage to all current users. - - if users.has_key(name): - for user in users[name]: - user._attrnames[name].add(attrname) - users = self.attribute_users[-1] + # Add the usage to all current users. + if users.has_key(name): + for user in users[name]: + user._attrnames[name].add(attrname) return users[name] else: return [] @@ -461,6 +457,8 @@ self._init_attribute_user_for_name(node, name) # Propagate any loop usage forward to any redefinition of a name. + # This models the name acquiring the existing usage as it remains active + # upon starting a new iteration. loop_users = self.loop_users[-1] @@ -494,37 +492,43 @@ self.user_shelves.append([]) self.scope_shelves.append([]) - def _new_branch(self, loop_node=None): + def _new_branch(self, node, loop=0): """ Establish a new control-flow branch, transferring attribute usage to the new branch so that it may be augmented for each name locally. - If the optional 'loop_node' is given, add it as an active user to be - informed of attribute usage. + Add the given 'node' as an active user to be informed of attribute + usage. """ + new_usage = {} + new_users = {} + + # Define usage on this node based on current usage. + + for users in self.attribute_users[-1].values(): + for user in users: + for name, usage in user._attrnames.items(): + if not new_usage.has_key(name): + new_usage[name] = set(usage) + else: + new_usage[name] = new_usage[name].union(set(usage)) + + # Set the usage on this node. + + node._attrnames = {} + + for name, usage in new_usage.items(): + node._attrnames[name] = usage + new_users[name] = [node] + # Retain a record of active users. - new_users = {} - new_users.update(self.attribute_users[-1]) self.attribute_users.append(new_users) - # Where a loop is the cause of the branch, register the loop node as a - # user of each name so that attribute usage is also recorded for the - # loop. - - loop_users = {} - loop_users.update(self.loop_users[-1]) - self.loop_users.append(loop_users) - - if loop_node is not None: - for name in new_users.keys(): - if not loop_users.has_key(name): - loop_users[name] = set([loop_node]) - else: - loop_users[name] = loop_users[name].union([loop_node]) - self._init_attribute_user_for_name(loop_node, name) + if loop: + self.loop_users.append(new_users) # Retain a record of scope usage. @@ -532,6 +536,10 @@ scope_usage.update(self.scope_usage[-1]) self.scope_usage.append(scope_usage) + # Remember this user. + + self.all_attribute_users.add(node) + def _abandon_branch(self): """ @@ -556,7 +564,7 @@ scope_usage = self.scope_usage.pop() self.scope_shelves[-1].append(scope_usage) - def _merge_branches(self): + def _merge_branches(self, loop=0): """ Merge control-flow branches. This should find the users active within @@ -567,6 +575,9 @@ # Combine the attribute users. This ensures that a list of users # affected by attribute usage is maintained for the current branch. + if loop: + self.loop_users.pop() + users = self.attribute_users[-1] new_users = {} diff -r bd11b8c03676 -r b233b2a48837 micropython/inspect.py --- a/micropython/inspect.py Mon Sep 06 01:08:58 2010 +0200 +++ b/micropython/inspect.py Tue Sep 07 01:07:23 2010 +0200 @@ -350,8 +350,8 @@ def new_branchpoint(self): self.get_namespace()._new_branchpoint() - def new_branch(self, loop_node=None): - self.get_namespace()._new_branch(loop_node) + def new_branch(self, node, loop=0): + self.get_namespace()._new_branch(node, loop) def abandon_branch(self): self.get_namespace()._abandon_branch() @@ -359,8 +359,8 @@ def shelve_branch(self): self.get_namespace()._shelve_branch() - def merge_branches(self): - self.get_namespace()._merge_branches() + def merge_branches(self, loop=0): + self.get_namespace()._merge_branches(loop) def define_attribute_user(self, node): @@ -786,21 +786,20 @@ # Enter the loop. # Propagate attribute usage to branches. - self.new_branch(node) + self.new_branch(node, 1) self.dispatch(node.body) self.shelve_branch() self.in_loop = in_loop - # Maintain a branch for the else clause or the current retained usage - # where execution avoids the conditional clauses. + # Maintain a branch for the else clause. - self.new_branch() if node.else_ is not None: + self.new_branch(node.else_) self.dispatch(node.else_) - self.shelve_branch() + self.shelve_branch() - self.merge_branches() + self.merge_branches(1) return None def visitFrom(self, node): @@ -866,17 +865,16 @@ for test, body in node.tests: self.dispatch(test) - self.new_branch() + self.new_branch(body) self.dispatch(body) self.shelve_branch() - # Maintain a branch for the else clause or the current retained usage - # where execution avoids the conditional clauses. + # Maintain a branch for the else clause. - self.new_branch() if node.else_ is not None: + self.new_branch(node.else_) self.dispatch(node.else_) - self.shelve_branch() + self.shelve_branch() self.merge_branches() return None @@ -934,7 +932,7 @@ # Enter the loop. # Propagate attribute usage to branches. - self.new_branch(node) + self.new_branch(node, 1) for if_ in node.ifs: self.dispatch(if_) @@ -942,7 +940,7 @@ self.shelve_branch() self.in_loop = in_loop - self.merge_branches() + self.merge_branches(1) return None visitListCompIf = NOP @@ -1000,7 +998,7 @@ self.new_branchpoint() for name, var, n in node.handlers: - self.new_branch() + self.new_branch(name) # Establish the local for the handler. @@ -1011,10 +1009,10 @@ self.shelve_branch() - self.new_branch() if node.else_ is not None: + self.new_branch(node.else_) self.dispatch(node.else_) - self.shelve_branch() + self.shelve_branch() self.merge_branches() return None @@ -1035,19 +1033,18 @@ in_loop = self.in_loop self.in_loop = 1 self.dispatch(node.test) - self.new_branch(node) + self.new_branch(node, 1) self.dispatch(node.body) self.shelve_branch() self.in_loop = in_loop - # Maintain a branch for the else clause or the current retained usage - # where execution avoids the conditional clauses. + # Maintain a branch for the else clause. # NOTE: Consider merging here before the else clause. - self.new_branch() if node.else_ is not None: + self.new_branch(node.else_) self.dispatch(node.else_) - self.shelve_branch() + self.shelve_branch() self.merge_branches() return None diff -r bd11b8c03676 -r b233b2a48837 rsvplib.py --- a/rsvplib.py Mon Sep 06 01:08:58 2010 +0200 +++ b/rsvplib.py Tue Sep 07 01:07:23 2010 +0200 @@ -453,6 +453,19 @@ def builtins_object_init(self): pass + def builtins_isinstance(self): + frame = self.local_sp_stack[-1] + + # Get the operand addresses. + + obj_value = self.frame_stack[frame] + cls_value = self.frame_stack[frame + 1] + + if self.machine._CheckInstance(obj_value.ref, cls_value.ref): + self.machine.result = DataValue(self.constants[True], self.constants[True]) + else: + self.machine.result = DataValue(self.constants[False], self.constants[False]) + native_functions = { # Native method implementations: @@ -495,6 +508,10 @@ # Native instantiator helpers: "__builtins__.list.__new__" : builtins_list_new, + + # Native helper functions: + + "__builtins__._isinstance" : builtins_isinstance, } # vim: tabstop=4 expandtab shiftwidth=4 diff -r bd11b8c03676 -r b233b2a48837 tests/attribute_access_type_restriction_conditional_choice.py --- a/tests/attribute_access_type_restriction_conditional_choice.py Mon Sep 06 01:08:58 2010 +0200 +++ b/tests/attribute_access_type_restriction_conditional_choice.py Tue Sep 07 01:07:23 2010 +0200 @@ -20,16 +20,15 @@ def test_conditional(obj): # obj: C, D, E (f) - if obj.f(): # C, D, E (f) - obj.g() # D (f, g) + if obj.f(): # C, D, E (f) + return obj.g() # D (f, g) else: - obj.h() # E (f, h) - # # (f, g) ^ (f, h) - return 2 + return obj.h() # E (f, h) + # # (f, g) ^ (f, h) c = C() d = D() e = E() -result1_2 = test_conditional(d) +result1_3 = test_conditional(d) # vim: tabstop=4 expandtab shiftwidth=4 diff -r bd11b8c03676 -r b233b2a48837 tests/attribute_access_type_restriction_conditional_choice_incompatible.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/attribute_access_type_restriction_conditional_choice_incompatible.py Tue Sep 07 01:07:23 2010 +0200 @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +class C: + def f(self): + return 0 + +class D: + def f(self): + return 2 + + def g(self): + return 3 + +def test_conditional(obj): + # obj: C, D (f) + if obj.f(): # C, D (f) + return obj.g() # D (f, g) + else: + return obj.f() # C (f) + # # (f, g) ^ (f) + +c = C() +d = D() +result1_0 = test_conditional(c) + +# vim: tabstop=4 expandtab shiftwidth=4