# HG changeset patch # User paulb@localhost.localdomain # Date 1172364623 -3600 # Node ID 9deeef8dc11a08636e6da9c0b1257eb8bee18ff7 # Parent c413fccc7e518ddf3fbcae4ae4958cf5569f639e Introduced instance annotations on program nodes in order to support more complicated class/instance mechanisms, such as the "prolific" multiple instance class introduced here. Changed InvokeBlock plus LoadRef to just InvokeRef, adding subprogram copying in order to avoid specialised copies of methods all sharing the same internal subprograms. Fixed the copying of argument attributes on nodes. Changed list construction to use an internal subprogram, fixing the list initialiser to be like the standard Python list initialiser. Added "re-running" of invocations in order to prevent deep recursion which crashes Python. diff -r c413fccc7e51 -r 9deeef8dc11a annotate.py --- a/annotate.py Fri Feb 23 01:29:50 2007 +0100 +++ b/annotate.py Sun Feb 25 01:50:23 2007 +0100 @@ -131,7 +131,7 @@ types possible when the means of constructing the namespace may depend on run-time behaviour. - Covered: Assign, CheckType, Conditional, Global, Import, InvokeBlock, + Covered: Assign, CheckType, Conditional, Global, Import, InvokeRef, InvokeFunction, LoadAttr, LoadExc, LoadName, LoadRef, LoadTemp, Module, Not, Pass, Raise, ReleaseTemp, ReturnFromBlock, ReturnFromFunction, StoreAttr, StoreName, StoreTemp, Subprogram, @@ -160,6 +160,7 @@ self.subprograms = [] self.current_subprograms = [] self.current_namespaces = [] + self.rerun_subprograms = {} self.namespace = None self.module = module @@ -188,6 +189,14 @@ mutate nodes in the original program. """ + # Recursion test. + + if node in self.current_subprograms: + if not self.rerun_subprograms.has_key(node): + self.rerun_subprograms[node] = [] + self.rerun_subprograms[node].append(locals) + return node + # Record the current subprogram and namespace. self.current_subprograms.append(node) @@ -215,6 +224,15 @@ node.namespace = self.namespace result = self.dispatch(node) + + while self.rerun_subprograms.has_key(node): + all_rerun_locals = self.rerun_subprograms[node] + del self.rerun_subprograms[node] + for rerun_locals in all_rerun_locals: + print "Re-running", node, "with", rerun_locals + node.namespace = self.namespace = rerun_locals + result = self.dispatch(node) + result.namespace = self.namespace if not hasattr(result, "raises"): result.raises = [] @@ -479,6 +497,10 @@ if attribute is not None: attribute = Attribute(instance, attribute.type) + # Request an instance-specific initialiser. + + attribute = attr.type.get_attribute_for_instance(attribute, instance) + # Skip cases where no callable is found. if attribute is not None: @@ -516,15 +538,25 @@ self.namespace.set_types(getattr(invoke, "types", [])) return invoke - def visitInvokeBlock(self, invoke): + def visitInvokeRef(self, invoke): """ Return the processed 'invoke' node, first finding the callables - indicated by the expression. + indicated by the reference. """ - invoke.expr = self.dispatch(invoke.expr) - invocation_types = self.namespace.types + # Where the invocation belongs to an instance but the invoked subprogram + # does not, request a special copy. + + instance = getattr(invoke, "instance", None) + if instance is not None and getattr(invoke.ref, "instance", None) is None: + if invoke.ref.copies.has_key(instance): + invoke.ref = invoke.ref.copies[instance] + else: + invoke.ref = invoke.ref.copy(instance) + print "Created", invoke.ref, "for", getattr(invoke.ref, "instance", None) + invoke.ref.module.simplifier.subnames[invoke.ref.full_name()] = invoke.ref + invocation_types = [Attribute(None, invoke.ref)] return self._visitInvoke(invoke, invocation_types, have_args=0) def visitInvokeFunction(self, invoke): @@ -1169,12 +1201,18 @@ invocation.stars = {} if not invocation.stars.has_key(subprogram.full_name()): + instance = getattr(invocation, "instance", None) + code=[ StoreTemp( + instance=instance, expr=InvokeFunction( invocation.original, + instance=instance, expr=LoadAttr( + instance=instance, expr=LoadRef( + instance=instance, ref=self.builtins ), name="list", @@ -1191,8 +1229,12 @@ code.append( InvokeFunction( invocation.original, + instance=instance, expr=LoadAttr( - expr=LoadTemp(), + instance=instance, + expr=LoadTemp( + instance=instance + ), name="append" ), args=[arg], @@ -1202,21 +1244,27 @@ ) code += [ - Return(expr=LoadTemp(release=1)) + Return( + instance=instance, + expr=LoadTemp( + instance=instance, + release=1 + ) + ) ] - invocation.stars[subprogram.full_name()] = InvokeBlock( + invocation.stars[subprogram.full_name()] = InvokeRef( invocation.original, + instance=instance, produces_result=1, - expr=LoadRef( - ref=Subprogram( - name=None, - returns_value=1, - params=[], - star=None, - dstar=None, - code=code - ) + ref=Subprogram( + instance=instance, + name=None, + returns_value=1, + params=[], + star=None, + dstar=None, + code=code ) ) @@ -1233,12 +1281,18 @@ invocation.dstars = {} if not invocation.dstars.has_key(subprogram.full_name()): + instance = getattr(invocation, "instance", None) + code=[ StoreTemp( + instance=instance, expr=InvokeFunction( invocation.original, + instance=instance, expr=LoadAttr( + instance=instance, expr=LoadRef( + instance=instance, ref=self.builtins ), name="dict", @@ -1252,22 +1306,31 @@ # NOTE: Constant not added to table. - constant = Constant(name=repr(arg), value=arg, namespace=Namespace()) + constant = Constant( + instance=instance, + name=repr(arg), value=arg, namespace=Namespace() + ) #constant.namespace.store("__class__", self.get_builtin_instances(invocation, constant.typename)) code += [ StoreTemp( + instance=instance, expr=LoadRef( + instance=instance, ref=constant ), index="const" ), StoreAttr( + instance=instance, lvalue=LoadTemp( + instance=instance, index="const" ), name="__class__", expr=LoadAttr( + instance=instance, expr=LoadRef( + instance=instance, ref=self.builtins ), name=constant.typename, @@ -1276,12 +1339,17 @@ ), InvokeFunction( invocation.original, + instance=instance, expr=LoadAttr( - expr=LoadTemp(), + instance=instance, + expr=LoadTemp( + instance=instance + ), name="__setitem__" ), args=[ LoadTemp( + instance=instance, index="const", release=1 ), @@ -1291,21 +1359,27 @@ ] code += [ - Return(expr=LoadTemp(release=1)) + Return( + instance=instance, + expr=LoadTemp( + instance=instance, + release=1 + ) + ) ] - invocation.dstars[subprogram.full_name()] = InvokeBlock( + invocation.dstars[subprogram.full_name()] = InvokeRef( invocation.original, + instance=instance, produces_result=1, - expr=LoadRef( - ref=Subprogram( - name=None, - returns_value=1, - params=[], - star=None, - dstar=None, - code=code - ) + ref=Subprogram( + instance=instance, + name=None, + returns_value=1, + params=[], + star=None, + dstar=None, + code=code ) ) diff -r c413fccc7e51 -r 9deeef8dc11a fixnames.py --- a/fixnames.py Fri Feb 23 01:29:50 2007 +0100 +++ b/fixnames.py Sun Feb 25 01:50:23 2007 +0100 @@ -396,14 +396,14 @@ return self.default(invoke) - def visitInvokeBlock(self, invoke): + def visitInvokeRef(self, invoke): "Transform the 'invoke' node, performing processing on subprograms." # The special case of internal subprogram invocation is addressed by # propagating namespace information to the subprogram and processing it. - subprogram = self.process_node(invoke.expr.ref, self.namespace) + subprogram = self.process_node(invoke.ref, self.namespace) if subprogram is not None: self.subprograms.append(subprogram) return invoke diff -r c413fccc7e51 -r 9deeef8dc11a lib/builtins.py --- a/lib/builtins.py Fri Feb 23 01:29:50 2007 +0100 +++ b/lib/builtins.py Sun Feb 25 01:50:23 2007 +0100 @@ -458,9 +458,10 @@ return self != 0 class list: - def __init__(self, *args): - for arg in args: - self.append(arg) + def __init__(self, args=None): + if args is not None: + for arg in args: + self.append(arg) def __getitem__(self, index): if -len(self) <= index < len(self): diff -r c413fccc7e51 -r 9deeef8dc11a simplified.py --- a/simplified.py Fri Feb 23 01:29:50 2007 +0100 +++ b/simplified.py Sun Feb 25 01:50:23 2007 +0100 @@ -163,7 +163,8 @@ """ common_attributes = "name", "index", "value", "nstype", "internal", "returns_value", "is_method", "ref", "module", "structures", "original" - expression_attributes = "expr", "lvalue", "test", "star", "dstar" + expression_attributes = "expr", "lvalue", "test" + argument_attributes = "star", "dstar" invocation_attributes = "params", # not "args" - see "pos_args", "kw_args" grouping_attributes = "code", "body", "else_", "handler", "finally_", "choices" @@ -177,7 +178,7 @@ self.original = original self.defining = defining - self.copies = [] + self.copies = {} if self.original is not None and defining: self.original._node = self @@ -301,15 +302,16 @@ "Return the active copies of this node or a list containing this node." - return self.copies or [self] + return self.copies.values() or [self] # Node manipulation functions. - def copy(self, new_name=None): + def copy(self, instance=None, new_name=None): """ - Perform a deep copy of the node, optionally specifying a 'new_name', - returning a new unannotated copy. + Perform a deep copy of the node, optionally specifying the 'instance' + for whom the copy has been requested and a 'new_name' for the copied + node. Return new unannotated copies of the node and its descendants. """ # Copy the common attributes of this node. @@ -319,6 +321,10 @@ if hasattr(self, attr): common[attr] = getattr(self, attr) + # Add new attributes specially for copies. + + common["instance"] = instance + if new_name is not None: common["copy_of"] = self common["name"] = new_name @@ -330,7 +336,7 @@ # Add links to copies from originals. - self.copies.append(node) + self.copies[instance] = node # Copy attributes of different types. @@ -340,9 +346,20 @@ if n is None: n2 = n else: - n2 = n.copy() + n2 = n.copy(instance) setattr(node, attr, n2) + for attr in self.argument_attributes: + if hasattr(self, attr): + t = getattr(self, attr) + if t is None: + t2 = t + else: + name, n = t + n2 = n.copy(instance) + t2 = name, n2 + setattr(node, attr, t2) + for attr in self.invocation_attributes: if hasattr(self, attr): l = getattr(self, attr) @@ -351,23 +368,23 @@ if n is None: l2.append((name, n)) else: - l2.append((name, n.copy())) + l2.append((name, n.copy(instance))) setattr(node, attr, l2) for attr in self.grouping_attributes: if hasattr(self, attr): l = getattr(self, attr) - setattr(node, attr, [n.copy() for n in l]) + setattr(node, attr, [n.copy(instance) for n in l]) # Arguments are usually processed further - "args" is useless. if hasattr(self, "pos_args"): - node.pos_args = [n.copy() for n in self.pos_args] + node.pos_args = [n.copy(instance) for n in self.pos_args] if hasattr(self, "kw_args"): node.kw_args = {} for name, n in self.kw_args.items(): - node.kw_args[name] = n.copy() + node.kw_args[name] = n.copy(instance) return node @@ -482,7 +499,7 @@ else: raise TypeError, "Positional argument appears after keyword arguments in '%s'." % self -class InvokeBlock(Invoke): +class InvokeRef(Invoke): "A block or loop invocation." @@ -574,12 +591,23 @@ # Attribute propagation. def get_attribute_for_instance(self, attribute, instance): + + # Create specialised methods. + if isinstance(attribute.type, Subprogram): subprogram = attribute.type + + # Each instance may have its own version of the subprogram. + key = (subprogram, instance) if not self.attributes_for_instances.has_key(key): - self.attributes_for_instances[key] = Attribute(attribute.context, subprogram.copy(subprogram.full_name())) + self.attributes_for_instances[key] = Attribute(attribute.context, subprogram.copy(instance, subprogram.full_name())) + print "New subprogram", self.attributes_for_instances[key].type, "for", key + return self.attributes_for_instances[key] + + # The original nodes are returned for other attributes. + else: return attribute @@ -593,6 +621,44 @@ else: return MultipleInstanceClass._get_key(self, node) +class ProlificMultipleInstanceClass(MultipleInstanceClass): + + """ + A Python class which provides multiple instances for different versions of + methods. In order to avoid unbounded instance production (since new + instances cause new copies of methods which in turn would cause new + instances), + """ + + def __init__(self, *args, **kw): + MultipleInstanceClass.__init__(self, *args, **kw) + self.instance_relations = {} + + def _get_key(self, node): + if self.namespace.has_key("__atomic__"): + return id(self) + else: + return id(node) + + def has_instance(self, node): + requesting_instance = getattr(node, "instance", None) + return requesting_instance is not None and requesting_instance.get_class() is self or \ + self.instance_relations.has_key(requesting_instance) or self.instances.has_key(self._get_key(node)) + + def add_instance(self, node, instance): + requesting_instance = getattr(node, "instance", None) + print "New instance", instance, "for", id(node), requesting_instance + self.instances[self._get_key(node)] = instance + if requesting_instance is not None: + self.instance_relations[requesting_instance] = instance + requesting_instance.get_class().instance_relations[instance] = requesting_instance + + def get_instance(self, node): + requesting_instance = getattr(node, "instance", None) + if requesting_instance is not None and requesting_instance.get_class() is self: + return requesting_instance + return self.instance_relations.get(requesting_instance) or self.instances[self._get_key(node)] + class Instance(Structure): "An instance." @@ -686,4 +752,8 @@ global Class Class = SelectiveMultipleInstanceClass +def set_prolific_multiple_instance_mode(): + global Class + Class = ProlificMultipleInstanceClass + # vim: tabstop=4 expandtab shiftwidth=4 diff -r c413fccc7e51 -r 9deeef8dc11a simplify.py --- a/simplify.py Fri Feb 23 01:29:50 2007 +0100 +++ b/simplify.py Sun Feb 25 01:50:23 2007 +0100 @@ -202,8 +202,7 @@ # Make an invocation of the subprogram. - result = InvokeBlock(and_, 1, produces_result=1) - result.expr = LoadRef(ref=subprogram) + result = InvokeRef(and_, 1, produces_result=1, ref=subprogram) return result def visitAssert(self, assert_): @@ -543,8 +542,7 @@ # Make an invocation of the subprogram. - result = InvokeBlock(bitand, 1, produces_result=1) - result.expr = LoadRef(ref=subprogram) + result = InvokeRef(bitand, 1, produces_result=1, ref=subprogram) return result def visitBreak(self, break_): @@ -597,10 +595,10 @@ name=class_.name, expr=LoadRef(ref=structure) ), - InvokeBlock( + InvokeRef( class_, - share_locals=0, # override the local sharing usually in InvokeBlock - expr=LoadRef(ref=subprogram) + share_locals=0, # override the local sharing usually in InvokeRef + ref=subprogram ) ] ) @@ -631,7 +629,7 @@ ...to: - InvokeBlock -> Subprogram -> Conditional (test) -> (body) + InvokeRef -> Subprogram -> Conditional (test) -> (body) (else) -> Conditional (test) -> (body) (else) -> ... """ @@ -755,8 +753,7 @@ # Make an invocation of the subprogram. - result = InvokeBlock(compare, 1, produces_result=1) - result.expr = LoadRef(ref=subprogram) + result = InvokeRef(compare, 1, produces_result=1, ref=subprogram) return result def visitConst(self, const): @@ -767,9 +764,7 @@ return result def visitContinue(self, continue_): - result = InvokeBlock(continue_, 1, - expr=LoadRef(ref=self.current_subprograms[-1]) - ) + result = InvokeRef(continue_, 1, ref=self.current_subprograms[-1]) return result def visitDict(self, dict): @@ -838,9 +833,9 @@ ReleaseTemp() ]) ] + body_stmt + [ - InvokeBlock( + InvokeRef( node, - expr=LoadRef(ref=subprogram) + ref=subprogram ) ], handler=[ @@ -880,7 +875,7 @@ ) ) ), - InvokeBlock(node, expr=LoadRef(ref=subprogram)), + InvokeRef(node, ref=subprogram), ReleaseTemp() ] ) @@ -1122,7 +1117,61 @@ return LoadRef(lambda_, 1, ref=subprogram) def visitList(self, list): - return self._visitBuiltin(list, "list") + + # Make a subprogram for the list construction and record it outside the + # main tree. + + subprogram = Subprogram(list, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=list) + self.current_subprograms.append(subprogram) + + # Make nice annotations for the viewer. + + list._subprogram = subprogram + + subprogram.code=[ + StoreTemp( + expr=InvokeFunction( + list, + expr=LoadName( + name="list" + ), + args=[], + star=None, + dstar=None + ) + ) + ] + + for node in list.nodes: + subprogram.code.append( + InvokeFunction( + list, + expr=LoadAttr( + expr=LoadTemp(), + name="append" + ), + args=[self.dispatch(node)], + star=None, + dstar=None + ) + ) + + subprogram.code.append( + ReturnFromBlock( + expr=LoadTemp(release=1) + ) + ) + + self.current_subprograms.pop() + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram + + # Make an invocation of the subprogram. + + result = InvokeRef(list, 1, + produces_result=1, + ref=subprogram + ) + return result def visitListComp(self, listcomp): @@ -1164,9 +1213,9 @@ # Make an invocation of the subprogram. - result = InvokeBlock(listcomp, 1, + result = InvokeRef(listcomp, 1, produces_result=1, - expr=LoadRef(ref=subprogram) + ref=subprogram ) return result @@ -1302,9 +1351,9 @@ # Make an invocation of the subprogram. - result = InvokeBlock(or_, 1, + result = InvokeRef(or_, 1, produces_result=1, - expr=LoadRef(ref=subprogram) + ref=subprogram ) return result @@ -1652,9 +1701,9 @@ name="__bool__"), ), body=self.dispatch(while_.body) + [ - InvokeBlock( + InvokeRef( while_, - expr=LoadRef(ref=subprogram) + ref=subprogram ), ReturnFromBlock() ], @@ -1675,9 +1724,7 @@ # Make an invocation of the subprogram. - result = InvokeBlock(while_, 1, - expr=LoadRef(ref=subprogram) - ) + result = InvokeRef(while_, 1, ref=subprogram) # Make nice annotations for the viewer. @@ -1704,7 +1751,7 @@ """ Emulate the current mechanisms by producing nodes as follows: - InvokeBlock -> Subprogram -> StoreTemp (expr) -> x.__lt__(y) + InvokeRef -> Subprogram -> StoreTemp (expr) -> x.__lt__(y) Conditional (test) -> __is__(LoadTemp, NotImplemented) (body) -> ReleaseTemp StoreTemp (expr) -> y.__gt__(x) @@ -1775,10 +1822,10 @@ self.current_subprograms.pop() self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - result = InvokeBlock( + result = InvokeRef( binary, produces_result=1, - expr=LoadRef(ref=subprogram) + ref=subprogram ) # Make nice annotations for the viewer. @@ -1793,7 +1840,7 @@ """ Emulate the current mechanisms by producing nodes as follows: - InvokeBlock -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y) + InvokeRef -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y) (else) (handler) -> Conditional (test) -> CheckType (expr) -> LoadExc (choices) -> LoadName TypeError @@ -1840,10 +1887,10 @@ self.current_subprograms.pop() self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram - result = InvokeBlock( + result = InvokeRef( binary, produces_result=1, - expr=LoadRef(ref=subprogram) + ref=subprogram ) # Make nice annotations for the viewer. diff -r c413fccc7e51 -r 9deeef8dc11a test.py --- a/test.py Fri Feb 23 01:29:50 2007 +0100 +++ b/test.py Sun Feb 25 01:50:23 2007 +0100 @@ -12,6 +12,8 @@ simplified.set_multiple_instance_mode() elif "-ms" in sys.argv: simplified.set_selective_multiple_instance_mode() + elif "-mp" in sys.argv: + simplified.set_prolific_multiple_instance_mode() import viewer from annotate import AnnotationError, Importer, load