# HG changeset patch # User paulb@localhost.localdomain # Date 1169425755 -3600 # Node ID 81c7b970881801fac2382f2b4f39194173667ac2 # Parent e5bf6f260215f3b1fbe1d226f0706769ed063e60 Added node copying, used when accessing class attributes for instances where such attributes refer to subprograms (ie. methods); added the acquisition of such copied method attributes in the get_attributes function. Added the original_def attribute for certain kinds of subprograms (functions, lambda expressions) which is used to "paint" copied sections of the simplified node tree with an AST node which can help identify distinct sites for object instantiation. Moved the Self and Attribute classes to the simplified module. diff -r e5bf6f260215 -r 81c7b9708818 annotate.py --- a/annotate.py Sun Jan 21 17:56:01 2007 +0100 +++ b/annotate.py Mon Jan 22 01:29:15 2007 +0100 @@ -5,7 +5,7 @@ which are produced when simplifying AST node trees originating from the compiler module. -Copyright (C) 2006 Paul Boddie +Copyright (C) 2006, 2007 Paul Boddie This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -1416,34 +1416,6 @@ def __repr__(self): return repr(self.names) -class Attribute: - - """ - An attribute abstraction, indicating the type of the attribute along with - its context or origin. - """ - - def __init__(self, context, type): - self.context = context - self.type = type - - def __eq__(self, other): - return hasattr(other, "type") and other.type == self.type or other == self.type - - def __repr__(self): - return "Attribute(%s, %s)" % (repr(self.context), repr(self.type)) - -class Self: - - """ - A program node encapsulating object/context information in an argument list. - This is not particularly like Attribute, Class, Instance or other such - things, since it actually appears in the program representation. - """ - - def __init__(self, attribute): - self.types = [attribute] - class Importer: "An import machine, searching for and loading modules." @@ -1654,10 +1626,19 @@ structure = structure.type results = [] for attribute, accessor in find_attributes(structure, name): + + # Detect class attribute access via instances. + + if attribute is not None and isinstance(structure, Instance) and isinstance(accessor, Class): + attribute = accessor.get_attribute_for_instance(attribute, structure) + + # Produce an attribute with the appropriate context. + if attribute is not None and isinstance(structure, Structure): results.append((Attribute(structure, attribute.type), accessor)) else: results.append((attribute, accessor)) + return results # Convenience functions. diff -r e5bf6f260215 -r 81c7b9708818 simplified.py --- a/simplified.py Sun Jan 21 17:56:01 2007 +0100 +++ b/simplified.py Mon Jan 22 01:29:15 2007 +0100 @@ -5,7 +5,7 @@ contains nodes representing program instructions or operations, program structure or organisation, and abstract program data. -Copyright (C) 2006 Paul Boddie +Copyright (C) 2006, 2007 Paul Boddie This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -151,6 +151,11 @@ choices Any choices which may be included in the final program. """ + common_attributes = "name", "index", "value", "nstype", "internal", "returns_value", "is_method", "ref", "module", "structures", "original_def" + expression_attributes = "expr", "lvalue", "test", "star", "dstar" + invocation_attributes = "params", # not "args" - see "pos_args", "kw_args" + grouping_attributes = "code", "body", "else_", "handler", "finally_", "choices" + def __init__(self, original=None, defining=0, **kw): """ @@ -258,7 +263,7 @@ if hasattr(self, "test"): self.test.pprint(indent + 2, "? ", stream=stream) - for attr in "code", "body", "else_", "handler", "finally_", "choices": + for attr in self.grouping_attributes: if hasattr(self, attr) and getattr(self, attr): self._pprint(indent, "", "%s {" % attr, stream=stream) for node in getattr(self, attr): @@ -278,6 +283,87 @@ self._pprint(indent + 2, "| ", "when %s: %s" % (ref, attribute), stream=stream) self._pprint(indent, "", "--------", stream=stream) + # Node manipulation functions. + + def copy(self, new_name=None, original_def=None): + + """ + Perform a deep copy of the node, optionally specifying a 'new_name', + returning a new unannotated copy. + + The 'original_def' parameter is used to assign a particular AST node to + copied regions of the simplified node tree. + """ + + # Obtain an AST node to be assigned to the copied nodes. + + original_def = getattr(self, "original", None) or original_def or getattr(self, "original_def", None) + + # Copy the common attributes of this node. + + common = {} + for attr in self.common_attributes: + if hasattr(self, attr): + common[attr] = getattr(self, attr) + + if new_name is not None: + common["name"] = new_name + + if original_def is not None: + common["original_def"] = original_def + + # Instantiate the copy, avoiding side-effects with original and defining. + + node = self.__class__(**common) + node.original = self.original + node.defining = self.defining + + # Add links to copied nodes from original AST nodes. + + if node.original is not None: + if not hasattr(node.original, "_nodes"): + node.original._nodes = [] + node.original._nodes.append(node) + + # Copy attributes of different types. + + for attr in self.expression_attributes: + if hasattr(self, attr): + n = getattr(self, attr) + if n is None: + n2 = n + else: + n2 = n.copy(original_def=original_def) + setattr(node, attr, n2) + + for attr in self.invocation_attributes: + if hasattr(self, attr): + l = getattr(self, attr) + l2 = [] + for name, n in l: + if n is None: + l2.append((name, n)) + else: + l2.append((name, n.copy(original_def=original_def))) + setattr(node, attr, l2) + + for attr in self.grouping_attributes: + if hasattr(self, attr): + l = getattr(self, attr) + setattr(node, attr, [n.copy(original_def=original_def) for n in l]) + + # Arguments are usually processed further - "args" is useless. + + if hasattr(self, "pos_args"): + node.pos_args = [n.copy(original_def=original_def) 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(original_def=original_def) + + return node + # These are the supported "operations" described by simplified program nodes. class Pass(Node): "A placeholder node corresponding to pass." @@ -436,6 +522,11 @@ def get_instance_name(self, instance): return self._full_name + # Attribute propagation. + + def get_attribute_for_instance(self, attribute, instance): + return attribute + class MultipleInstanceClass(_Class): "A Python class." @@ -443,22 +534,35 @@ def __init__(self, *args, **kw): _Class.__init__(self, *args, **kw) self.instances = {} + self.attributes_for_instances = {} + + def _get_key(self, node): + return getattr(node, "original_def", node) def has_instance(self, node): - key = id(node) - return self.instances.has_key(key) + return self.instances.has_key(self._get_key(node)) def add_instance(self, node, instance): - key = id(node) - self.instances[key] = instance + self.instances[self._get_key(node)] = instance def get_instance(self, node): - key = id(node) - return self.instances[key] + return self.instances[self._get_key(node)] def get_instance_name(self, instance): return name(instance, self._full_name) + # Attribute propagation. + + def get_attribute_for_instance(self, attribute, instance): + if isinstance(attribute.type, Subprogram): + subprogram = attribute.type + 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())) + return self.attributes_for_instances[key] + else: + return attribute + class Instance(Structure): "An instance." @@ -488,6 +592,34 @@ Instance.__init__(self, *args, **kw) self.typename = self.value.__class__.__name__ +class Attribute: + + """ + An attribute abstraction, indicating the type of the attribute along with + its context or origin. + """ + + def __init__(self, context, type): + self.context = context + self.type = type + + def __eq__(self, other): + return hasattr(other, "type") and other.type == self.type or other == self.type + + def __repr__(self): + return "Attribute(%s, %s)" % (repr(self.context), repr(self.type)) + +class Self: + + """ + A program node encapsulating object/context information in an argument list. + This is not particularly like Attribute, Class, Instance or other such + things, since it actually appears in the program representation. + """ + + def __init__(self, attribute): + self.types = [attribute] + # Configuration setting. Class = SingleInstanceClass diff -r e5bf6f260215 -r 81c7b9708818 simplify.py --- a/simplify.py Sun Jan 21 17:56:01 2007 +0100 +++ b/simplify.py Mon Jan 22 01:29:15 2007 +0100 @@ -5,7 +5,7 @@ this module processes AST trees originating from the compiler module and produces a result tree consisting of instruction-oriented program nodes. -Copyright (C) 2006 Paul Boddie +Copyright (C) 2006, 2007 Paul Boddie This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -823,7 +823,7 @@ """ subprogram = Subprogram(name=function.name, module=self.module, structures=self.current_structures[:], - internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class) + internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class, original_def=function) self.current_subprograms.append(subprogram) within_class = self.within_class @@ -934,7 +934,7 @@ # Make a subprogram for the function and record it outside the main # tree. - subprogram = Subprogram(name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None) + subprogram = Subprogram(name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=lambda_) self.current_subprograms.append(subprogram) subprogram.code = [ReturnFromFunction(expr=self.dispatch(lambda_.code))] self.current_subprograms.pop()