# HG changeset patch # User paulb@localhost.localdomain # Date 1165179861 -3600 # Node ID 7bd67aabe8451a02e09bc802588a7385557fc117 # Parent 1fdb16b8828222d0b5b9449e6e36d68dbd439687 Reordered methods, added docstrings. Removed the make_namespace method, inserting its contents in the only place the method was used. diff -r 1fdb16b88282 -r 7bd67aabe845 annotate.py --- a/annotate.py Sun Dec 03 01:36:02 2006 +0100 +++ b/annotate.py Sun Dec 03 22:04:21 2006 +0100 @@ -57,14 +57,25 @@ self.count = 0 def init(self, node): + + "Initialise a 'node' for annotation." + if not hasattr(node, "types"): node.types = [] def annotate(self, node, types): + + "Annotate the given 'node' with the given 'types'." + self.init(node) self.combine(node.types, types) def combine(self, target, types): + + """ + Combine the 'target' list with the given 'types', counting new members. + """ + for type in types: if type not in target: target.append(type) @@ -242,14 +253,48 @@ except AnnotationMessage, exc: raise AnnotationError(exc, node) - # Program structure/control-flow. + # Specific node methods. def visitAssign(self, assign): + + """ + Return the 'assign' node whose contents (merely a group of nodes) have + been processed. + """ + assign.code = self.dispatches(assign.code) return assign + def visitCheckExc(self, checkexc): + + """ + Return the 'checkexc' node, processing the expression to find the + possible types of the exception, and processing each choice to build a + list of checked types for the exception. + """ + + checkexc.expr = self.dispatch(checkexc.expr) + expr_types = self.namespace.types + choice_types = [] + choices = [] + for choice in checkexc.choices: + choices.append(self.dispatch(choice)) + choice_types += self.namespace.types + for expr_type in expr_types: + if expr_type.type.get_class() not in choice_types: + self._prune_non_accesses(checkexc.expr, expr_type) + return checkexc + def visitConditional(self, conditional): + """ + Return the 'conditional' node, processing the test, body and else + clauses and recording their processed forms. The body and else clauses + are processed within their own namespaces, and the test is also + processed in its own namespace if 'isolate_test' is set on the + 'conditional' node. + """ + # Conditionals keep local namespace changes isolated. # With Return nodes inside the body/else sections, the changes are # communicated to the caller. @@ -305,18 +350,285 @@ return conditional + def visitLoadAttr(self, loadattr): + + """ + Return the 'loadattr' node, processing and storing the expression, and + using the expression's types to construct records of accesses and + non-accesses using the stated attribute name. + """ + + loadattr.expr = self.dispatch(loadattr.expr) + types = [] + non_accesses = [] + accesses = {} + for attr in self.namespace.types: + attributes = get_attributes(attr.type, loadattr.name) + if not attributes: + if not attr.type in non_accesses: + non_accesses.append(attr) + + # Revoke this type from any name involved. + + self._prune_non_accesses(loadattr.expr, attr) + + for attribute, accessor in attributes: + if attribute is not None: + types.append(attribute) + if not accesses.has_key(attr.type): + accesses[attr.type] = [] + if not (attribute, accessor) in accesses[attr.type]: + accesses[attr.type].append((attribute, accessor)) + else: + if not attr in non_accesses: + non_accesses.append(attr) + + # Revoke this type from any name involved. + + self._prune_non_accesses(loadattr.expr, attr) + + if not types: + print "No attribute found for", loadattr.name, "given", self.namespace.types + self.namespace.set_types(types) + loadattr.non_accesses = non_accesses + loadattr.accesses = accesses + self.annotate(loadattr) + return loadattr + + def _prune_non_accesses(self, expr, attr): + + """ + Prune type information from 'expr' where the given 'attr' has been + shown to be a non-access. + """ + + if isinstance(expr, LoadName): + self.namespace.revoke(expr.name, attr) + elif isinstance(expr, LoadAttr): + for expr_attr in expr.expr.types: + if hasattr(expr_attr.type, "namespace"): + expr_attr.type.namespace.revoke(expr.name, attr) + elif isinstance(expr, LoadExc): + self.namespace.revoke_exception_type(attr) + + def visitLoadExc(self, loadexc): + + """ + Return the 'loadexc' node, discovering the possible exception types + raised. + """ + + self.namespace.types = self.namespace.raises[:] + self.annotate(loadexc) + return loadexc + + def visitLoadName(self, loadname): + + """ + Return the 'loadname' node, processing the name information on the node + to determine which types are involved with the name. + """ + + self.namespace.set_types(self.namespace.load(loadname.name)) + result = loadname + self.annotate(result) + return result + + def visitLoadRef(self, loadref): + + """ + Return the 'loadref' node, obtaining type information about the + reference stated on the node. + """ + + self.namespace.set_types([Attribute(None, loadref.ref)]) + self.annotate(loadref) + return loadref + + def visitLoadTemp(self, loadtemp): + + """ + Return the 'loadtemp' node, obtaining type information about the + temporary variable accessed, and removing variable information where the + 'release' attribute has been set on the node. + """ + + index = getattr(loadtemp, "index", None) + try: + if getattr(loadtemp, "release", 0): + self.namespace.set_types(self.namespace.temp[index].pop()) + else: + self.namespace.set_types(self.namespace.temp[index][-1]) + except KeyError: + raise AnnotationMessage, "Temporary store index '%s' not defined." % index + self.annotate(loadtemp) + return loadtemp + def visitModule(self, module): + + """ + Return the processed 'module' whose contents (merely a group of nodes) + are processed. + """ + module.code = self.dispatches(module.code) return module + def visitNot(self, not_): + + "Return the 'not_' node whose expression is processed." + + not_.expr = self.dispatch(not_.expr) + return not_ + def visitPass(self, pass_): + + "Return the unprocessed 'pass_' node." + return pass_ + def visitRaise(self, raise_): + + """ + Return the 'raise_' node, processing any traceback information along + with the raised exception expression, converting the node into a kind of + invocation where the expression is found not to be an invocation itself. + This node affects the namespace, adding exception types to the list of + those raised in the namespace. + """ + + if getattr(raise_, "traceback", None) is not None: + raise_.traceback = self.dispatch(raise_.traceback) + raise_.expr = self.dispatch(raise_.expr) + + # Handle bare name exceptions by converting any classes to instances. + + if not isinstance(raise_.expr, InvokeFunction): + raise_.pos_args = [] + raise_.kw_args = {} + raise_.star = None + raise_.dstar = None + types = [] + for attr in self.namespace.types: + if isinstance(attr.type, Class): + self._visitInvoke(raise_, [attr], have_args=0) + types += self.namespace.types + else: + types = self.namespace.types + + combine(self.namespace.raises, types) + return raise_ + + def visitReleaseTemp(self, releasetemp): + + """ + Return the 'releasetemp' node, removing temporary variable information + from the current namespace. + """ + + index = getattr(releasetemp, "index", None) + try: + self.namespace.temp[index].pop() + except KeyError: + raise AnnotationMessage, "Temporary store index '%s' not defined." % index + except IndexError: + pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index + return releasetemp + + def visitReturn(self, return_): + + """ + Return the 'return_' node, processing any expression and obtaining type + information to be accumulated in the current namespace's list of return + types. A snapshot of the namespace is taken for the purposes of + reconciling or merging namespaces where subprograms actually share + locals with their callers. + """ + + if hasattr(return_, "expr"): + return_.expr = self.dispatch(return_.expr) + combine(self.namespace.returns, self.namespace.types) + self.annotate(return_) + self.namespace.snapshot() + return return_ + + visitReturnFromBlock = visitReturn + visitReturnFromFunction = visitReturn + + def visitStoreAttr(self, storeattr): + + """ + Return the 'storeattr' node, processing the expression and target, and + using the type information obtained to build records of legitimate + writes to the stated attribute, along with "impossible" non-writes to + the attribute. + """ + + storeattr.expr = self.dispatch(storeattr.expr) + expr = self.namespace.types + storeattr.lvalue = self.dispatch(storeattr.lvalue) + writes = {} + non_writes = [] + for attr in self.namespace.types: + if attr is None: + if not attr in non_writes: + non_writes.append(attr) + continue + attr.type.namespace.add(storeattr.name, expr) + writes[attr.type] = attr.type.namespace.load(storeattr.name) + if not writes: + print "Unable to store attribute", storeattr.name, "given", self.namespace.types + storeattr.writes = writes + storeattr.non_writes = non_writes + return storeattr + + def visitStoreName(self, storename): + + """ + Return the 'storename' node, processing the expression on the node and + associating the type information obtained with the stated name in the + current namespace. + """ + + storename.expr = self.dispatch(storename.expr) + self.namespace.store(storename.name, self.namespace.types) + return storename + + def visitStoreTemp(self, storetemp): + + """ + Return the 'storetemp' node, processing the expression on the node and + associating the type information obtained with a temporary variable in + the current namespace. + """ + + storetemp.expr = self.dispatch(storetemp.expr) + index = getattr(storetemp, "index", None) + if not self.namespace.temp.has_key(index): + self.namespace.temp[index] = [] + self.namespace.temp[index].append(self.namespace.types) + return storetemp + def visitSubprogram(self, subprogram): + + """ + Return the 'subprogram' node, processing its contents (a group of nodes + comprising the subprogram). + """ + subprogram.code = self.dispatches(subprogram.code) return subprogram def visitTry(self, try_): + + """ + Return the 'try_' node, processing the body clause in its own namespace + derived from the current namespace, processing any handler clause using + the namespace information accumulated in the body, and processing any + else and finally clauses, attempting to supply each with appropriate + namespace information. + """ + is_module = self.namespace is self.module.namespace try_.body = self.dispatches(try_.body) @@ -369,177 +681,6 @@ try_.finally_ = self.dispatches(try_.finally_) return try_ - # Namespace operations. - - def visitCheckExc(self, checkexc): - checkexc.expr = self.dispatch(checkexc.expr) - expr_types = self.namespace.types - choice_types = [] - choices = [] - for choice in checkexc.choices: - choices.append(self.dispatch(choice)) - choice_types += self.namespace.types - for expr_type in expr_types: - if expr_type.type.get_class() not in choice_types: - self._prune_non_accesses(checkexc.expr, expr_type) - return checkexc - - def visitLoadAttr(self, loadattr): - loadattr.expr = self.dispatch(loadattr.expr) - types = [] - non_accesses = [] - accesses = {} - for attr in self.namespace.types: - attributes = get_attributes(attr.type, loadattr.name) - if not attributes: - if not attr.type in non_accesses: - non_accesses.append(attr) - - # Revoke this type from any name involved. - - self._prune_non_accesses(loadattr.expr, attr) - - for attribute, accessor in attributes: - if attribute is not None: - types.append(attribute) - if not accesses.has_key(attr.type): - accesses[attr.type] = [] - if not (attribute, accessor) in accesses[attr.type]: - accesses[attr.type].append((attribute, accessor)) - else: - if not attr in non_accesses: - non_accesses.append(attr) - - # Revoke this type from any name involved. - - self._prune_non_accesses(loadattr.expr, attr) - - if not types: - print "No attribute found for", loadattr.name, "given", self.namespace.types - self.namespace.set_types(types) - loadattr.non_accesses = non_accesses - loadattr.accesses = accesses - self.annotate(loadattr) - return loadattr - - def _prune_non_accesses(self, expr, attr): - if isinstance(expr, LoadName): - self.namespace.revoke(expr.name, attr) - elif isinstance(expr, LoadAttr): - for expr_attr in expr.expr.types: - if hasattr(expr_attr.type, "namespace"): - expr_attr.type.namespace.revoke(expr.name, attr) - elif isinstance(expr, LoadExc): - self.namespace.revoke_exception_type(attr) - - def visitLoadExc(self, loadexc): - self.namespace.types = self.namespace.raises[:] - self.annotate(loadexc) - return loadexc - - def visitLoadName(self, loadname): - self.namespace.set_types(self.namespace.load(loadname.name)) - result = loadname - self.annotate(result) - return result - - def visitLoadRef(self, loadref): - self.namespace.set_types([Attribute(None, loadref.ref)]) - self.annotate(loadref) - return loadref - - def visitLoadTemp(self, loadtemp): - index = getattr(loadtemp, "index", None) - try: - if getattr(loadtemp, "release", 0): - self.namespace.set_types(self.namespace.temp[index].pop()) - else: - self.namespace.set_types(self.namespace.temp[index][-1]) - except KeyError: - raise AnnotationMessage, "Temporary store index '%s' not defined." % index - self.annotate(loadtemp) - return loadtemp - - def visitNot(self, not_): - not_.expr = self.dispatch(not_.expr) - return not_ - - def visitRaise(self, raise_): - if getattr(raise_, "traceback", None) is not None: - raise_.traceback = self.dispatch(raise_.traceback) - raise_.expr = self.dispatch(raise_.expr) - - # Handle bare name exceptions by converting any classes to instances. - - if not isinstance(raise_.expr, InvokeFunction): - raise_.pos_args = [] - raise_.kw_args = {} - raise_.star = None - raise_.dstar = None - types = [] - for attr in self.namespace.types: - if isinstance(attr.type, Class): - self._visitInvoke(raise_, [attr], have_args=0) - types += self.namespace.types - else: - types = self.namespace.types - - combine(self.namespace.raises, types) - return raise_ - - def visitReleaseTemp(self, releasetemp): - index = getattr(releasetemp, "index", None) - try: - self.namespace.temp[index].pop() - except KeyError: - raise AnnotationMessage, "Temporary store index '%s' not defined." % index - except IndexError: - pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index - return releasetemp - - def visitReturn(self, return_): - if hasattr(return_, "expr"): - return_.expr = self.dispatch(return_.expr) - combine(self.namespace.returns, self.namespace.types) - self.annotate(return_) - self.namespace.snapshot() - return return_ - - visitReturnFromBlock = visitReturn - visitReturnFromFunction = visitReturn - - def visitStoreAttr(self, storeattr): - storeattr.expr = self.dispatch(storeattr.expr) - expr = self.namespace.types - storeattr.lvalue = self.dispatch(storeattr.lvalue) - writes = {} - non_writes = [] - for attr in self.namespace.types: - if attr is None: - if not attr in non_writes: - non_writes.append(attr) - continue - attr.type.namespace.add(storeattr.name, expr) - writes[attr.type] = attr.type.namespace.load(storeattr.name) - if not writes: - print "Unable to store attribute", storeattr.name, "given", self.namespace.types - storeattr.writes = writes - storeattr.non_writes = non_writes - return storeattr - - def visitStoreName(self, storename): - storename.expr = self.dispatch(storename.expr) - self.namespace.store(storename.name, self.namespace.types) - return storename - - def visitStoreTemp(self, storetemp): - storetemp.expr = self.dispatch(storetemp.expr) - index = getattr(storetemp, "index", None) - if not self.namespace.temp.has_key(index): - self.namespace.temp[index] = [] - self.namespace.temp[index].append(self.namespace.types) - return storetemp - # Invocations are a chapter of their own. def visitInvokeBlock(self, invoke): @@ -700,7 +841,8 @@ using_module_namespace = 0 else: items = self.make_items(invoke, target, context) - namespace = self.make_namespace(items) + namespace = Namespace() + namespace.merge_items(items) using_module_namespace = 0 # Test to see if anything has changed. @@ -954,11 +1096,6 @@ return invocation.stars[subprogram.full_name()] - def make_namespace(self, items): - namespace = Namespace() - namespace.merge_items(items) - return namespace - # Namespace-related abstractions. class Namespace: