# HG changeset patch # User paulb@jeremy # Date 1154643227 -7200 # Node ID d0aa40e2a7b40953398948ea818e1748244a0d21 # Parent cc6abe59ef053461732cb32349e00bdbbc71421f Changed attribute access to provide attribute type and context information in an Attribute object, along with the accessor involved. Improved invocation and attribute access somewhat. diff -r cc6abe59ef05 -r d0aa40e2a7b4 annotate.py --- a/annotate.py Thu Aug 03 01:02:47 2006 +0200 +++ b/annotate.py Fri Aug 04 00:13:47 2006 +0200 @@ -102,33 +102,61 @@ """ Find for the given 'structure' all attributes for the given 'name', visiting - base classes where appropriate and returning the methods in order of + base classes where appropriate and returning the attributes in order of descending precedence for all possible base classes. + + The elements in the result list are 2-tuples which contain the attribute and + the structure involved in accessing the attribute. """ + # First attempt to search the instance/class namespace. + try: - return structure.namespace.load(name) + l = structure.namespace.load(name) + attributes = [] + for attribute in l: + attributes.append((attribute, structure)) + + # If that does not work, attempt to investigate any class or base classes. + except KeyError: attributes = [] + + # Investigate any instance's implementing class. + if isinstance(structure, Instance): for cls in structure.namespace.load("__class__"): l = find_attributes(cls, name) for attribute in l: if attribute not in attributes: attributes.append(attribute) + + # Investigate any class's base classes. + elif isinstance(structure, Class): + + # If no base classes exist, return an indicator that no attribute + # exists. + + if not structure.base_refs: + return [(None, structure)] + + # Otherwise, find all possible base classes. + for base_refs in structure.base_refs: base_attributes = [] + + # For each base class, find attributes either in the base + # class or its own base classes. + for base_ref in base_refs: l = find_attributes(base_ref, name) - if l: - for attribute in l: - if attribute not in base_attributes: - base_attributes.append(attribute) - elif None not in base_attributes: - base_attributes.append(None) - if base_attributes != [None]: - attributes += base_attributes + for attribute in l: + if attribute not in base_attributes: + base_attributes.append(attribute) + + attributes += base_attributes + return attributes def get_attributes(structure, name): @@ -137,14 +165,16 @@ Return all possible attributes for the given 'structure' having the given 'name', wrapping each attribute in an Attribute object which includes context information for the attribute access. + + The elements in the result list are 2-tuples which contain the attribute and + the structure involved in accessing the attribute. """ if isinstance(structure, Attribute): structure = structure.type attributes = find_attributes(structure, name) - for i, attribute in enumerate(attributes): - if attribute is not None: - attributes[i] = Attribute(structure, attribute) + for i, (attribute, accessor) in enumerate(attributes): + attributes[i] = Attribute(structure, attribute), accessor return attributes # Annotation. @@ -318,10 +348,16 @@ def visitLoadAttr(self, loadattr): loadattr.expr = self.dispatch(loadattr.expr) types = [] + accesses = {} for ref in self.types: - for type in get_attributes(ref, loadattr.name): - types.append(type) + if not accesses.has_key(ref): + accesses[ref] = [] + for attribute, accessor in get_attributes(ref, loadattr.name): + if attribute.type is not None: + types.append(type) + accesses[ref].append((attribute, accessor)) self.types = types + loadattr.accesses = accesses self.annotate(loadattr) return loadattr @@ -329,8 +365,11 @@ storeattr.expr = self.dispatch(storeattr.expr) expr = self.types storeattr.lvalue = self.dispatch(storeattr.lvalue) + accesses = {} for ref in self.types: ref.namespace.store(storeattr.name, expr) + accesses[ref] = ref.namespace.load(storeattr.name) + storeattr.accesses = accesses return storeattr def visitConditional(self, conditional): @@ -401,7 +440,7 @@ invocations = {} - # Visit each callable in turn + # Visit each callable in turn, finding subprograms. for callable in expr: @@ -411,23 +450,38 @@ # others. if isinstance(callable, Class): - subprograms = get_attributes(callable, "__init__") + attributes = get_attributes(callable, "__init__") # Deal with object invocations by using __call__ methods. elif isinstance(callable, Instance): - subprograms = get_attributes(callable, "__call__") + attributes = get_attributes(callable, "__call__") # Normal functions or methods are more straightforward. + # Here, we model them using an attribute with no context and with + # no associated accessor. else: - subprograms = [callable] + attributes = [(Attribute(None, callable), None)] + + # Inspect each attribute and extract the subprogram. - for subprogram in subprograms: + for attribute, accessor in attributes: + subprogram = attribute.type + + # If a subprogram is defined, invoke it. + if subprogram is not None: self.invoke_subprogram(invoke, subprogram) invocations[callable] = subprogram + # If a class is involved, presume that it must create a new + # object. + + if isinstance(callable, Class): + self.types = [attribute.context] + self.annotate(invoke) + invoke.invocations = invocations return invoke diff -r cc6abe59ef05 -r d0aa40e2a7b4 simplified.py --- a/simplified.py Thu Aug 03 01:02:47 2006 +0200 +++ b/simplified.py Fri Aug 04 00:13:47 2006 +0200 @@ -144,6 +144,14 @@ if hasattr(self, "dstar") and self.dstar: self.dstar.pprint(indent + 2, "( ") + # Annotations. + + if hasattr(self, "accesses"): + self._pprint(indent, "", "--------") + for ref, attributes in self.accesses.items(): + self._pprint(indent + 2, "| ", "%s: %s" % (ref, attributes)) + self._pprint(indent, "", "--------") + class Module(Node): "A Python module." class Subprogram(Node): "A subprogram: functions, methods and loops." class Pass(Node): "A placeholder node corresponding to pass."