1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/simplify/annotate.py Sun May 27 18:25:25 2007 +0200
1.3 @@ -0,0 +1,1789 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Annotate program node structures. The code in this module operates upon nodes
1.8 +which are produced when simplifying AST node trees originating from the compiler
1.9 +module.
1.10 +
1.11 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
1.12 +
1.13 +This software is free software; you can redistribute it and/or
1.14 +modify it under the terms of the GNU General Public License as
1.15 +published by the Free Software Foundation; either version 2 of
1.16 +the License, or (at your option) any later version.
1.17 +
1.18 +This software is distributed in the hope that it will be useful,
1.19 +but WITHOUT ANY WARRANTY; without even the implied warranty of
1.20 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.21 +GNU General Public License for more details.
1.22 +
1.23 +You should have received a copy of the GNU General Public
1.24 +License along with this library; see the file LICENCE.txt
1.25 +If not, write to the Free Software Foundation, Inc.,
1.26 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
1.27 +
1.28 +--------
1.29 +
1.30 +To use this module, the easiest approach is to use the load function:
1.31 +
1.32 +load(filename, builtins)
1.33 +
1.34 +To control module importing, an importer should be constructed and employed.
1.35 +Here, the standard path for module searching is used:
1.36 +
1.37 +importer = Importer(sys.path)
1.38 +load(filename, builtins, importer)
1.39 +
1.40 +Underneath the load function, the annotate function provides support for
1.41 +annotating modules already processed by simplify and fixnames:
1.42 +
1.43 +annotate(module, builtins)
1.44 +
1.45 +And at the most basic level, the most intricate approach involves obtaining an
1.46 +Annotator object:
1.47 +
1.48 +annotator = Annotator()
1.49 +
1.50 +Then, processing an existing module with it:
1.51 +
1.52 +annotator.process(module)
1.53 +
1.54 +If a module containing built-in classes and functions has already been
1.55 +annotated, such a module should be passed in as an additional argument:
1.56 +
1.57 +annotator.process(module, builtins)
1.58 +"""
1.59 +
1.60 +from simplify.simplified import *
1.61 +import simplify, simplify.fixnames # for the load function
1.62 +import compiler
1.63 +import os
1.64 +
1.65 +class System:
1.66 +
1.67 + """
1.68 + A class maintaining the state of the annotation system. When the system
1.69 + counter can no longer be incremented by any annotation operation, the
1.70 + system may be considered stable and fully annotated.
1.71 + """
1.72 +
1.73 + def __init__(self):
1.74 + self.count = 0
1.75 +
1.76 + def init(self, node, attr="types"):
1.77 +
1.78 + "Initialise a 'node' for annotation."
1.79 +
1.80 + if not hasattr(node, attr):
1.81 + setattr(node, attr, set())
1.82 +
1.83 + def annotate(self, node, types, attr="types"):
1.84 +
1.85 + "Annotate the given 'node' with the given 'types'."
1.86 +
1.87 + self.init(node, attr)
1.88 + self.combine(getattr(node, attr), types)
1.89 +
1.90 + def combine(self, target, types):
1.91 +
1.92 + """
1.93 + Combine the 'target' list with the given 'types', counting new members.
1.94 + """
1.95 +
1.96 + for type in types:
1.97 + if type not in target:
1.98 + target.add(type)
1.99 + self.count += 1
1.100 +
1.101 +system = System()
1.102 +
1.103 +# Exceptions.
1.104 +
1.105 +class AnnotationError(SimplifiedError):
1.106 +
1.107 + "An error in the annotation process."
1.108 +
1.109 + pass
1.110 +
1.111 +class AnnotationMessage(Exception):
1.112 +
1.113 + "A lesser annotation error."
1.114 +
1.115 + pass
1.116 +
1.117 +# Annotation.
1.118 +
1.119 +class Annotator(Visitor):
1.120 +
1.121 + """
1.122 + The type annotator which traverses the program nodes, typically depth-first,
1.123 + and maintains a record of the current set of types applying to the currently
1.124 + considered operation. Such types are also recorded on the nodes, and a
1.125 + special "system" record is maintained to monitor the level of annotation
1.126 + activity with a view to recognising when no more annotations are possible.
1.127 +
1.128 + Throughout the annotation activity, type information consists of lists of
1.129 + Attribute objects where such objects retain information about the context of
1.130 + the type (since a value in the program may be associated with an object or
1.131 + class) and the actual type of the value being manipulated. Upon accessing
1.132 + attribute information on namespaces, additional accessor information is also
1.133 + exchanged - this provides a means of distinguishing between the different
1.134 + types possible when the means of constructing the namespace may depend on
1.135 + run-time behaviour.
1.136 +
1.137 + Covered: Assign, CheckType, Conditional, Global, Import, InvokeRef,
1.138 + InvokeFunction, LoadAttr, LoadExc, LoadName, LoadRef, LoadTemp,
1.139 + Module, Not, Pass, Raise, ReleaseTemp, ReturnFromBlock,
1.140 + ReturnFromFunction, StoreAttr, StoreName, StoreTemp, Subprogram,
1.141 + Try.
1.142 + """
1.143 +
1.144 + def __init__(self, importer=None):
1.145 +
1.146 + "Initialise the visitor with an optional 'importer'."
1.147 +
1.148 + Visitor.__init__(self)
1.149 + self.system = system
1.150 + self.importer = importer or Importer()
1.151 +
1.152 + # Satisfy visitor issues.
1.153 +
1.154 + self.visitor = self
1.155 +
1.156 + def process(self, module, builtins=None):
1.157 +
1.158 + """
1.159 + Process the given 'module', using the optional 'builtins' to access
1.160 + built-in classes and functions.
1.161 + """
1.162 +
1.163 + self.subprograms = []
1.164 + self.current_subprograms = []
1.165 + self.current_namespaces = []
1.166 + self.rerun_subprograms = {}
1.167 + self.namespace = None
1.168 + self.module = module
1.169 +
1.170 + # Process the module, supplying builtins if possible.
1.171 +
1.172 + self.builtins = builtins
1.173 + self.global_namespace = Namespace()
1.174 +
1.175 + if builtins is not None:
1.176 + self.builtins_namespace = builtins.namespace
1.177 + else:
1.178 + self.builtins_namespace = self.global_namespace
1.179 +
1.180 + # NOTE: Not declaring module namespace usage, even though it is used.
1.181 +
1.182 + self.process_node(module, self.global_namespace, 0)
1.183 +
1.184 + def process_node(self, node, locals, using_module_namespace):
1.185 +
1.186 + """
1.187 + Process a subprogram or module 'node', indicating the initial 'locals'.
1.188 + Note that this method may mutate nodes in the original program.
1.189 + """
1.190 +
1.191 + # Recursion test.
1.192 +
1.193 + if node in self.current_subprograms:
1.194 + if not self.rerun_subprograms.has_key(node):
1.195 + self.rerun_subprograms[node] = []
1.196 + self.rerun_subprograms[node].append(locals)
1.197 + return
1.198 +
1.199 + # Record the current subprogram and namespace.
1.200 +
1.201 + self.current_subprograms.append(node)
1.202 +
1.203 + # Determine the namespace.
1.204 +
1.205 + self.current_namespaces.append(self.namespace)
1.206 + self.namespace = locals
1.207 +
1.208 + # Add namespace details to any structure involved.
1.209 +
1.210 + if getattr(node, "structure", None) is not None:
1.211 + node.structure.namespace = Namespace()
1.212 +
1.213 + # Initialise bases where appropriate.
1.214 +
1.215 + if hasattr(node.structure, "bases"):
1.216 + base_refs = []
1.217 + for base in node.structure.bases:
1.218 + self.dispatch(base)
1.219 + base_refs.append(self.namespace.types)
1.220 + node.structure.base_refs = base_refs
1.221 +
1.222 + # Dispatch to the code itself.
1.223 +
1.224 + node.namespace = self.namespace
1.225 + self.set_module_namespace(using_module_namespace)
1.226 +
1.227 + self.dispatch(node)
1.228 + self.extract_results(node)
1.229 +
1.230 + while self.rerun_subprograms.has_key(node):
1.231 + all_rerun_locals = self.rerun_subprograms[node]
1.232 + del self.rerun_subprograms[node]
1.233 + for rerun_locals in all_rerun_locals:
1.234 + #print "Re-running", node, "with", rerun_locals
1.235 +
1.236 + self.namespace = rerun_locals
1.237 + node.namespace = rerun_locals
1.238 + self.set_module_namespace(using_module_namespace)
1.239 +
1.240 + self.dispatch(node)
1.241 + self.extract_results(node)
1.242 +
1.243 + # Restore the previous subprogram and namespace.
1.244 +
1.245 + self.namespace = self.current_namespaces.pop()
1.246 + self.current_subprograms.pop()
1.247 + self.reset_module_namespace(using_module_namespace)
1.248 +
1.249 + def set_module_namespace(self, using_module_namespace):
1.250 +
1.251 + """
1.252 + In order to keep global accesses working, the module namespace must be
1.253 + adjusted.
1.254 + """
1.255 +
1.256 + if using_module_namespace:
1.257 + self.module.namespace = self.namespace
1.258 +
1.259 + def reset_module_namespace(self, using_module_namespace):
1.260 +
1.261 + """
1.262 + In order to keep global accesses working, the module namespace must be
1.263 + reset.
1.264 + """
1.265 +
1.266 + if using_module_namespace:
1.267 + self.module.namespace = self.namespace
1.268 +
1.269 + def extract_results(self, node):
1.270 +
1.271 + "Extract results from the namespace."
1.272 +
1.273 + node.namespace = self.namespace
1.274 + self.system.annotate(node, self.namespace.raises, "raises")
1.275 + self.system.annotate(node, self.namespace.returns, "returns")
1.276 + if hasattr(node, "return_locals"):
1.277 + node.return_locals.update(self.namespace.return_locals)
1.278 +
1.279 + def annotate(self, node, types=None):
1.280 +
1.281 + """
1.282 + Annotate the given 'node' in the system, using either the optional
1.283 + 'types' or the namespace's current type information.
1.284 + """
1.285 +
1.286 + if types is None:
1.287 + self.system.annotate(node, self.namespace.types)
1.288 + else:
1.289 + self.system.annotate(node, types)
1.290 +
1.291 + def annotate_parameters(self, node, items):
1.292 +
1.293 + """
1.294 + Annotate the given 'node' using the given 'items' and updating the
1.295 + system's annotation counter.
1.296 + """
1.297 +
1.298 + if not hasattr(node, "paramtypes"):
1.299 + node.paramtypes = {}
1.300 +
1.301 + for param, types in items:
1.302 + if not node.paramtypes.has_key(param):
1.303 + node.paramtypes[param] = set()
1.304 + self.system.combine(node.paramtypes[param], types)
1.305 +
1.306 + # Visitor methods.
1.307 +
1.308 + def default(self, node):
1.309 +
1.310 + """
1.311 + Process the given 'node', given that it does not have a specific
1.312 + handler.
1.313 + """
1.314 +
1.315 + raise AnnotationMessage, "Node '%s' not supported." % node
1.316 +
1.317 + def dispatch(self, node, *args):
1.318 + try:
1.319 + Visitor.dispatch(self, node, *args)
1.320 + except AnnotationError, exc:
1.321 + exc.add(node)
1.322 + raise
1.323 + except AnnotationMessage, exc:
1.324 + raise AnnotationError(exc, node)
1.325 +
1.326 + # Specific node methods.
1.327 +
1.328 + def visitAssign(self, assign):
1.329 +
1.330 + """
1.331 + Process the 'assign' node and its contents.
1.332 + """
1.333 +
1.334 + self.dispatches(assign.code)
1.335 +
1.336 + def visitCheckType(self, checktype):
1.337 +
1.338 + """
1.339 + Process the 'checktype' node, finding the possible types of the
1.340 + exception, and processing each choice to build a list of checked types
1.341 + for the exception.
1.342 + """
1.343 +
1.344 + inverted = getattr(checktype, "inverted", 0)
1.345 + self.dispatch(checktype.expr)
1.346 +
1.347 + expr_types = self.namespace.types
1.348 + choice_types = set()
1.349 + choices = []
1.350 +
1.351 + for choice in checktype.choices:
1.352 + choices.append(self.dispatch(choice))
1.353 + choice_types.update(self.namespace.types)
1.354 +
1.355 + for expr_type in expr_types:
1.356 + in_choices = expr_type.type.get_class() in choice_types
1.357 +
1.358 + # Filter out types not in the choices list unless the operation is
1.359 + # inverted; in which case, filter out types in the choices list.
1.360 +
1.361 + if not inverted and not in_choices or inverted and in_choices:
1.362 + self._prune_non_accesses(checktype.expr, expr_type)
1.363 +
1.364 + def visitConditional(self, conditional):
1.365 +
1.366 + """
1.367 + Process the 'conditional' node, processing the test, body and else
1.368 + clauses and recording their processed forms. The body and else clauses
1.369 + are processed within their own namespaces, and the test is also
1.370 + processed in its own namespace if 'isolate_test' is set on the
1.371 + 'conditional' node.
1.372 + """
1.373 +
1.374 + # Conditionals keep local namespace changes isolated.
1.375 + # With Return nodes inside the body/else sections, the changes are
1.376 + # communicated to the caller.
1.377 +
1.378 + is_module = self.namespace is self.module.namespace
1.379 +
1.380 + # Where the test is closely associated with the body, save the namespace
1.381 + # before entering the test.
1.382 +
1.383 + if conditional.isolate_test:
1.384 + saved_namespace = self.namespace
1.385 + self.namespace = Namespace()
1.386 + if is_module:
1.387 + self.module.namespace = self.namespace
1.388 + self.namespace.merge_namespace(saved_namespace)
1.389 +
1.390 + self.dispatch(conditional.test)
1.391 +
1.392 + # Where the test may affect the body and the else clause, save the
1.393 + # namespace after processing the test.
1.394 +
1.395 + if not conditional.isolate_test:
1.396 + saved_namespace = self.namespace
1.397 + self.namespace = Namespace()
1.398 + if is_module:
1.399 + self.module.namespace = self.namespace
1.400 + self.namespace.merge_namespace(saved_namespace)
1.401 +
1.402 + # NOTE: Exception recording.
1.403 +
1.404 + else:
1.405 + test_raises = set()
1.406 + test_raises.update(self.namespace.raises)
1.407 +
1.408 + # Process the body clause.
1.409 +
1.410 + self.dispatches(conditional.body)
1.411 + body_namespace = self.namespace
1.412 +
1.413 + # Use the saved namespace as a template for the else clause.
1.414 +
1.415 + self.namespace = Namespace()
1.416 + if is_module:
1.417 + self.module.namespace = self.namespace
1.418 + self.namespace.merge_namespace(saved_namespace)
1.419 +
1.420 + # Process the else clause.
1.421 +
1.422 + self.dispatches(conditional.else_)
1.423 + else_namespace = self.namespace
1.424 +
1.425 + # Merge the body and else namespaces.
1.426 +
1.427 + self.namespace = Namespace()
1.428 + if is_module:
1.429 + self.module.namespace = self.namespace
1.430 + self.namespace.merge_namespace(body_namespace)
1.431 + self.namespace.merge_namespace(else_namespace)
1.432 +
1.433 + # NOTE: Test of exception type pruning based on the test/body.
1.434 + # Note that the checked exceptions are tested for re-raising.
1.435 +
1.436 + if conditional.isolate_test:
1.437 + for exc_type in test_raises:
1.438 + if exc_type not in body_namespace.raises:
1.439 + self.namespace.revoke_exception_type(exc_type)
1.440 +
1.441 + def visitGlobal(self, global_):
1.442 +
1.443 + """
1.444 + Leave the 'global_' node unprocessed since namespaces should have
1.445 + already been altered to take global names into consideration.
1.446 + """
1.447 +
1.448 + pass
1.449 +
1.450 + def visitImport(self, import_):
1.451 +
1.452 + """
1.453 + Process the 'import_' node, importing the module with the stated name
1.454 + and storing details on the node.
1.455 + """
1.456 +
1.457 + module = self.importer.load(import_.name, self.builtins, getattr(import_, "alias", None))
1.458 + if module is not None:
1.459 + self.namespace.set_types(set([module]))
1.460 + else:
1.461 + self.namespace.set_types(set())
1.462 + self.annotate(import_) # mainly for viewing purposes
1.463 +
1.464 + def _visitInvoke(self, invoke, invocation_types, have_args):
1.465 +
1.466 + """
1.467 + Process the 'invoke' node, using the given 'invocation_types' as the
1.468 + list of callables to be investigated for instantiation or for the
1.469 + invocation of functions or blocks. If 'have_args' is a true value, any
1.470 + invocation or instantiation will involve arguments.
1.471 + """
1.472 +
1.473 + # Now locate and invoke the subprogram. This can be complicated because
1.474 + # the target may be a class or object, and there may be many different
1.475 + # related subprograms.
1.476 +
1.477 + invocations = []
1.478 +
1.479 + # Visit each callable in turn, finding subprograms.
1.480 +
1.481 + for attr in invocation_types:
1.482 +
1.483 + # Deal with class invocations by providing instance objects.
1.484 + # Here, each class is queried for the __init__ method, which may
1.485 + # exist for some combinations of classes in a hierarchy but not for
1.486 + # others.
1.487 +
1.488 + if isinstance(attr.type, Class):
1.489 + attributes = get_attributes(attr.type, "__init__")
1.490 +
1.491 + # Deal with object invocations by using __call__ methods.
1.492 +
1.493 + elif isinstance(attr.type, Instance):
1.494 + attributes = get_attributes(attr.type, "__call__")
1.495 +
1.496 + # Normal functions or methods are more straightforward.
1.497 + # Here, we model them using an attribute with no context and with
1.498 + # no associated accessor.
1.499 +
1.500 + else:
1.501 + attributes = [(attr, None)]
1.502 +
1.503 + # Inspect each attribute and extract the subprogram.
1.504 +
1.505 + for attribute, accessor in attributes:
1.506 +
1.507 + # If a class is involved, presume that it must create a new
1.508 + # object.
1.509 +
1.510 + if isinstance(attr.type, Class):
1.511 +
1.512 + # Instantiate the class.
1.513 +
1.514 + instance = self.new_instance(invoke, attr.type)
1.515 +
1.516 + # For instantiations, switch the context.
1.517 +
1.518 + if attribute is not None:
1.519 + attribute = Attribute(instance, attribute.type)
1.520 +
1.521 + # Request an instance-specific initialiser.
1.522 +
1.523 + attribute = attr.type.get_attribute_for_instance(attribute, instance)
1.524 +
1.525 + # Skip cases where no callable is found.
1.526 +
1.527 + if attribute is not None:
1.528 +
1.529 + # If a subprogram is defined, invoke it.
1.530 +
1.531 + self.invoke_subprogram(invoke, attribute)
1.532 + if attribute.type not in invocations:
1.533 + invocations.append(attribute.type)
1.534 +
1.535 + elif not isinstance(attr.type, Class):
1.536 + print "Invocation type is None for", accessor
1.537 +
1.538 + else:
1.539 +
1.540 + # Test to see if no arguments were supplied in cases where no
1.541 + # initialiser was found.
1.542 +
1.543 + if have_args:
1.544 + raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type
1.545 +
1.546 + # Special case: initialisation.
1.547 +
1.548 + if isinstance(attr.type, Class):
1.549 +
1.550 + # Associate the instance with the result of this invocation.
1.551 +
1.552 + self.namespace.set_types(set([Attribute(None, instance)]))
1.553 + self.annotate(invoke)
1.554 +
1.555 + # Remember the invocations that were found, along with the return type
1.556 + # information.
1.557 +
1.558 + invoke.invocations = invocations
1.559 + self.namespace.set_types(getattr(invoke, "types", set()))
1.560 +
1.561 + def visitInvokeRef(self, invoke):
1.562 +
1.563 + """
1.564 + Process the 'invoke' node, first finding the callables indicated by the
1.565 + reference.
1.566 + """
1.567 +
1.568 + # Where the invocation belongs to an instance but the invoked subprogram
1.569 + # does not, request a special copy.
1.570 +
1.571 + instance = getattr(invoke, "instance", None)
1.572 + if instance is not None and getattr(invoke.ref, "instance", None) is None:
1.573 + if invoke.ref.copies.has_key(instance):
1.574 + invoke.ref = invoke.ref.copies[instance]
1.575 + else:
1.576 + invoke.ref = invoke.ref.copy(instance)
1.577 + #print "Created", invoke.ref, "for", getattr(invoke.ref, "instance", None)
1.578 + invoke.ref.module.simplifier.subnames[invoke.ref.full_name()] = invoke.ref
1.579 + invocation_types = [Attribute(None, invoke.ref)]
1.580 + self._visitInvoke(invoke, invocation_types, have_args=0)
1.581 +
1.582 + def visitInvokeFunction(self, invoke):
1.583 +
1.584 + """
1.585 + Process the 'invoke' node, first finding the callables indicated by the
1.586 + expression.
1.587 + """
1.588 +
1.589 + self.dispatch(invoke.expr)
1.590 + invocation_types = self.namespace.types
1.591 +
1.592 + # Invocation processing starts with making sure that the arguments have
1.593 + # been processed.
1.594 +
1.595 + self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke))
1.596 +
1.597 + def visitLoadAttr(self, loadattr):
1.598 +
1.599 + """
1.600 + Process the 'loadattr' node, processing and storing the expression, and
1.601 + using the expression's types to construct records of accesses and
1.602 + non-accesses using the stated attribute name.
1.603 + """
1.604 +
1.605 + self.dispatch(loadattr.expr)
1.606 + types = set()
1.607 + raises = set()
1.608 + non_accesses = []
1.609 + accesses = {}
1.610 +
1.611 + # For each expression type...
1.612 +
1.613 + for attr in self.namespace.types:
1.614 +
1.615 + # Find types for the named attribute.
1.616 +
1.617 + attributes = get_attributes(attr.type, loadattr.name)
1.618 +
1.619 + # Where no attributes exist...
1.620 +
1.621 + if not attributes:
1.622 +
1.623 + # Register new invalid accesses and mark a possible exception.
1.624 +
1.625 + if not attr in non_accesses:
1.626 + non_accesses.append(attr)
1.627 + exc = self.get_builtin_instances(loadattr, "AttributeError")
1.628 + raises.update(exc)
1.629 + self.namespace.raises.update(exc)
1.630 +
1.631 + # Revoke this type from any name involved.
1.632 +
1.633 + self._prune_non_accesses(loadattr.expr, attr)
1.634 +
1.635 + # For each type found...
1.636 +
1.637 + for attribute, accessor in attributes:
1.638 +
1.639 + # For actual attributes, register the type and remember the
1.640 + # access.
1.641 +
1.642 + if attribute is not None:
1.643 + types.add(attribute)
1.644 + if not accesses.has_key(attr.type):
1.645 + accesses[attr.type] = []
1.646 + if not (attribute, accessor) in accesses[attr.type]:
1.647 + accesses[attr.type].append((attribute, accessor))
1.648 +
1.649 + # Otherwise, register new invalid accesses and note a possible
1.650 + # exception.
1.651 +
1.652 + else:
1.653 + if not attr in non_accesses:
1.654 + non_accesses.append(attr)
1.655 + exc = self.get_builtin_instances(loadattr, "AttributeError")
1.656 + raises.update(exc)
1.657 + self.namespace.raises.update(exc)
1.658 +
1.659 + # Revoke this type from any name involved.
1.660 +
1.661 + self._prune_non_accesses(loadattr.expr, attr)
1.662 +
1.663 + if not types:
1.664 + print "No attribute found for", loadattr.name, "given", self.namespace.types
1.665 +
1.666 + # Remember the result types.
1.667 +
1.668 + self.namespace.set_types(types)
1.669 + loadattr.non_accesses = non_accesses
1.670 + loadattr.accesses = accesses
1.671 + loadattr.raises = raises
1.672 + self.annotate(loadattr)
1.673 +
1.674 + def _prune_non_accesses(self, expr, attr):
1.675 +
1.676 + """
1.677 + Prune type information from 'expr' where the given 'attr' has been
1.678 + shown to be a non-access.
1.679 + """
1.680 +
1.681 + if isinstance(expr, LoadName):
1.682 + self.namespace.revoke(expr.name, attr)
1.683 + elif isinstance(expr, LoadExc):
1.684 + self.namespace.revoke_exception_type(attr)
1.685 + elif isinstance(expr, LoadTemp):
1.686 + self.namespace.revoke_temp_type(getattr(expr, "index", None), attr)
1.687 +
1.688 + # LoadAttr cannot be pruned since this might unintentionally prune
1.689 + # legitimate types from other applications of the referenced type, it
1.690 + # almost certainly doesn't take "concurrent" mutation into
1.691 + # consideration (where in a running program, the pruned type is actually
1.692 + # reintroduced, making the pruning invalid), and there is no easy way of
1.693 + # preserving the meaning of a namespace without either creating lots of
1.694 + # specialised instances, and even then...
1.695 +
1.696 + #elif isinstance(expr, LoadAttr):
1.697 + # for expr_attr in expr.expr.types:
1.698 + # if hasattr(expr_attr.type, "namespace"):
1.699 + # expr_attr.type.namespace.revoke(expr.name, attr)
1.700 +
1.701 + def visitLoadExc(self, loadexc):
1.702 +
1.703 + """
1.704 + Process the 'loadexc' node, discovering the possible exception types
1.705 + raised.
1.706 + """
1.707 +
1.708 + self.namespace.set_types(self.namespace.raises)
1.709 + self.annotate(loadexc)
1.710 +
1.711 + def visitLoadName(self, loadname):
1.712 +
1.713 + """
1.714 + Process the 'loadname' node, processing the name information on the node
1.715 + to determine which types are involved with the name.
1.716 + """
1.717 +
1.718 + self.namespace.set_types(self.namespace.load(loadname.name))
1.719 + self.annotate(loadname)
1.720 +
1.721 + def visitLoadRef(self, loadref):
1.722 +
1.723 + """
1.724 + Process the 'loadref' node, obtaining type information about the
1.725 + reference stated on the node.
1.726 + """
1.727 +
1.728 + self.namespace.set_types(set([Attribute(None, loadref.ref)]))
1.729 + self.annotate(loadref)
1.730 +
1.731 + def visitLoadTemp(self, loadtemp):
1.732 +
1.733 + """
1.734 + Process the 'loadtemp' node, obtaining type information about the
1.735 + temporary variable accessed, and removing variable information where the
1.736 + 'release' attribute has been set on the node.
1.737 + """
1.738 +
1.739 + index = getattr(loadtemp, "index", None)
1.740 + try:
1.741 + if getattr(loadtemp, "release", 0):
1.742 + self.namespace.set_types(self.namespace.temp[index].pop())
1.743 + else:
1.744 + self.namespace.set_types(self.namespace.temp[index][-1])
1.745 + except KeyError:
1.746 + raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.747 + self.annotate(loadtemp)
1.748 +
1.749 + def visitMakeTuple(self, maketuple):
1.750 +
1.751 + """
1.752 + Process the 'maketuple' node and its contents.
1.753 + """
1.754 +
1.755 + # Get a tuple and populate it with type information for the contents.
1.756 +
1.757 + tuples = self.get_builtin_instances(maketuple, "tuple")
1.758 +
1.759 + # NOTE: This is dependent on the tuple definition in the builtins.
1.760 +
1.761 + for node in maketuple.nodes:
1.762 + self.dispatch(node)
1.763 + for t in tuples:
1.764 + t.type.namespace.add("value", self.namespace.types)
1.765 +
1.766 + self.namespace.set_types(tuples)
1.767 + self.annotate(maketuple)
1.768 +
1.769 + def visitModule(self, module):
1.770 +
1.771 + """
1.772 + Process the 'module' and its contents.
1.773 + """
1.774 +
1.775 + self.dispatches(module.code)
1.776 +
1.777 + def visitNot(self, not_):
1.778 +
1.779 + "Process the 'not_' node and its expression."
1.780 +
1.781 + self.dispatch(not_.expr)
1.782 +
1.783 + def visitPass(self, pass_):
1.784 +
1.785 + "Leave the 'pass_' node unprocessed."
1.786 +
1.787 + pass
1.788 +
1.789 + def visitRaise(self, raise_):
1.790 +
1.791 + """
1.792 + Process the 'raise_' node, processing any traceback information along
1.793 + with the raised exception expression, converting the node into a kind of
1.794 + invocation where the expression is found not to be an invocation itself.
1.795 + This node affects the namespace, adding exception types to the list of
1.796 + those raised in the namespace.
1.797 + """
1.798 +
1.799 + if getattr(raise_, "traceback", None) is not None:
1.800 + self.dispatch(raise_.traceback)
1.801 + self.dispatch(raise_.expr)
1.802 +
1.803 + # Handle bare name exceptions by converting any classes to instances.
1.804 +
1.805 + if not isinstance(raise_.expr, InvokeFunction):
1.806 + raise_.pos_args = []
1.807 + raise_.kw_args = {}
1.808 + raise_.star = None
1.809 + raise_.dstar = None
1.810 + types = set()
1.811 + for attr in self.namespace.types:
1.812 + if isinstance(attr.type, Class):
1.813 + self._visitInvoke(raise_, [attr], have_args=0)
1.814 + types.update(self.namespace.types)
1.815 + else:
1.816 + types = self.namespace.types
1.817 +
1.818 + self.namespace.raises.update(types)
1.819 +
1.820 + def visitReleaseTemp(self, releasetemp):
1.821 +
1.822 + """
1.823 + Process the 'releasetemp' node, removing temporary variable information
1.824 + from the current namespace.
1.825 + """
1.826 +
1.827 + index = getattr(releasetemp, "index", None)
1.828 + try:
1.829 + self.namespace.temp[index].pop()
1.830 + except KeyError:
1.831 + raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.832 + except IndexError:
1.833 + pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index
1.834 +
1.835 + def visitResetExc(self, resetexc):
1.836 + self.namespace.raises = set()
1.837 +
1.838 + def visitReturn(self, return_):
1.839 +
1.840 + """
1.841 + Process the 'return_' node, processing any expression and obtaining type
1.842 + information to be accumulated in the current namespace's list of return
1.843 + types. A snapshot of the namespace is taken for the purposes of
1.844 + reconciling or merging namespaces where subprograms actually share
1.845 + locals with their callers.
1.846 + """
1.847 +
1.848 + if hasattr(return_, "expr"):
1.849 + self.dispatch(return_.expr)
1.850 + self.namespace.returns.update(self.namespace.types)
1.851 + self.annotate(return_)
1.852 + self.namespace.snapshot()
1.853 +
1.854 + visitReturnFromBlock = visitReturn
1.855 + visitReturnFromFunction = visitReturn
1.856 +
1.857 + def visitStoreAttr(self, storeattr):
1.858 +
1.859 + """
1.860 + Process the 'storeattr' node, processing the expression and target, and
1.861 + using the type information obtained to build records of legitimate
1.862 + writes to the stated attribute, along with "impossible" non-writes to
1.863 + the attribute.
1.864 + """
1.865 +
1.866 + self.dispatch(storeattr.expr)
1.867 + expr = self.namespace.types
1.868 + self.dispatch(storeattr.lvalue)
1.869 + writes = {}
1.870 + non_writes = []
1.871 + for attr in self.namespace.types:
1.872 + # NOTE: Impose "atomic" constraints on certain types.
1.873 + if attr is None:
1.874 + if not attr in non_writes:
1.875 + non_writes.append(attr)
1.876 + continue
1.877 + attr.type.namespace.add(storeattr.name, expr)
1.878 + writes[attr.type] = attr.type.namespace.load(storeattr.name)
1.879 + if not writes:
1.880 + print "Unable to store attribute", storeattr.name, "given", self.namespace.types
1.881 + storeattr.writes = writes
1.882 + storeattr.non_writes = non_writes
1.883 +
1.884 + def visitStoreName(self, storename):
1.885 +
1.886 + """
1.887 + Process the 'storename' node, processing the expression on the node and
1.888 + associating the type information obtained with the stated name in the
1.889 + current namespace.
1.890 + """
1.891 +
1.892 + self.dispatch(storename.expr)
1.893 + self.namespace.store(storename.name, self.namespace.types)
1.894 + self.annotate(storename)
1.895 +
1.896 + def visitStoreTemp(self, storetemp):
1.897 +
1.898 + """
1.899 + Process the 'storetemp' node, processing the expression on the node and
1.900 + associating the type information obtained with a temporary variable in
1.901 + the current namespace.
1.902 + """
1.903 +
1.904 + self.dispatch(storetemp.expr)
1.905 + index = getattr(storetemp, "index", None)
1.906 + if not self.namespace.temp.has_key(index):
1.907 + self.namespace.temp[index] = []
1.908 + self.namespace.temp[index].append(self.namespace.types)
1.909 +
1.910 + def visitSubprogram(self, subprogram):
1.911 +
1.912 + """
1.913 + Process the 'subprogram' node, processing its contents (a group of nodes
1.914 + comprising the subprogram).
1.915 + """
1.916 +
1.917 + self.dispatches(subprogram.code)
1.918 +
1.919 + def visitTry(self, try_):
1.920 +
1.921 + """
1.922 + Process the 'try_' node, processing the body clause in its own namespace
1.923 + derived from the current namespace, processing any handler clause using
1.924 + the namespace information accumulated in the body, and processing any
1.925 + else and finally clauses, attempting to supply each with appropriate
1.926 + namespace information.
1.927 + """
1.928 +
1.929 + is_module = self.namespace is self.module.namespace
1.930 +
1.931 + self.dispatches(try_.body)
1.932 +
1.933 + # Save the namespace from the body.
1.934 +
1.935 + body_namespace = Namespace()
1.936 + body_namespace.merge_namespace(self.namespace)
1.937 +
1.938 + # Process the handler.
1.939 +
1.940 + if hasattr(try_, "handler"):
1.941 + self.dispatches(try_.handler)
1.942 +
1.943 + # Save the namespace from the handler.
1.944 +
1.945 + handler_namespace = Namespace()
1.946 + handler_namespace.merge_namespace(self.namespace)
1.947 +
1.948 + # Remember the raised exceptions encountered so far.
1.949 +
1.950 + raises = self.namespace.raises
1.951 +
1.952 + # Process the else clause.
1.953 +
1.954 + if hasattr(try_, "else_"):
1.955 +
1.956 + # Restore the body namespace for the else clause.
1.957 +
1.958 + self.namespace = body_namespace
1.959 + if is_module:
1.960 + self.module.namespace = self.namespace
1.961 +
1.962 + # Empty the raised exceptions for the else clause.
1.963 +
1.964 + self.namespace.raises = set()
1.965 + self.dispatches(try_.else_)
1.966 + self.namespace.raises = raises
1.967 +
1.968 + # Merge the namespaces.
1.969 +
1.970 + self.namespace = Namespace()
1.971 + if is_module:
1.972 + self.module.namespace = self.namespace
1.973 + self.namespace.merge_namespace(body_namespace)
1.974 + self.namespace.merge_namespace(handler_namespace)
1.975 +
1.976 + # Process the finally clause, if any.
1.977 +
1.978 + self.dispatches(try_.finally_)
1.979 +
1.980 + def visitYield(self, yield_):
1.981 + raise NotImplementedError, "The yield statement is not currently supported."
1.982 +
1.983 + # Utility methods.
1.984 +
1.985 + def get_builtin_instances(self, node, name):
1.986 + return set([Attribute(None, self.new_instance(node, attr.type)) for attr in self.builtins.namespace[name]])
1.987 +
1.988 + def new_instance(self, node, type):
1.989 +
1.990 + "For the given 'node', obtain an instance from the given 'type'."
1.991 +
1.992 + if not type.has_instance(node):
1.993 + instance = Instance()
1.994 + instance.namespace = Namespace()
1.995 + instance.namespace.store("__class__", set([Attribute(None, type)]))
1.996 + type.add_instance(node, instance)
1.997 + else:
1.998 + instance = type.get_instance(node)
1.999 +
1.1000 + return instance
1.1001 +
1.1002 + def invoke_subprogram(self, invoke, attribute):
1.1003 +
1.1004 + """
1.1005 + Invoke using the given 'invoke' node the subprogram represented by the
1.1006 + given 'attribute'.
1.1007 + """
1.1008 +
1.1009 + # Test for context information, making it into a real attribute.
1.1010 +
1.1011 + if attribute.context is not None:
1.1012 + context = Attribute(None, attribute.context)
1.1013 + target = attribute.type
1.1014 + else:
1.1015 + context = None
1.1016 + target = attribute.type
1.1017 +
1.1018 + # Test to see if anything has changed.
1.1019 +
1.1020 + if hasattr(invoke, "syscount") and invoke.syscount.has_key(target) and invoke.syscount[target] == self.system.count:
1.1021 + return
1.1022 +
1.1023 + # Remember the state of the system.
1.1024 +
1.1025 + else:
1.1026 + if not hasattr(invoke, "syscount"):
1.1027 + invoke.syscount = {}
1.1028 + invoke.syscount[target] = self.system.count
1.1029 +
1.1030 + # Provide the correct namespace for the invocation.
1.1031 + # This may be a "shared" namespace...
1.1032 +
1.1033 + if getattr(invoke, "share_locals", 0):
1.1034 + namespace = Namespace()
1.1035 + namespace.merge_namespace(self.namespace, everything=0)
1.1036 + using_module_namespace = self.namespace is self.module.namespace
1.1037 +
1.1038 + # Or it may be a structure...
1.1039 +
1.1040 + elif getattr(target, "structure", None):
1.1041 + namespace = Namespace()
1.1042 + using_module_namespace = 0
1.1043 +
1.1044 + # Or it may be a new namespace populated with the supplied parameters.
1.1045 +
1.1046 + else:
1.1047 + items = self.make_items(invoke, target, context)
1.1048 + namespace = Namespace()
1.1049 + namespace.merge_items(items)
1.1050 + using_module_namespace = 0
1.1051 +
1.1052 + # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a
1.1053 + # NOTE: subprogram within itself. Do not define the name of the function
1.1054 + # NOTE: within a method definition.
1.1055 +
1.1056 + if getattr(target, "name", None) is not None and not getattr(target, "is_method", 0):
1.1057 + namespace.store(target.name, set([Attribute(None, target)]))
1.1058 +
1.1059 + # Process the subprogram.
1.1060 +
1.1061 + self.process_node(target, namespace, using_module_namespace)
1.1062 +
1.1063 + # NOTE: Improve and verify this.
1.1064 + # If the invocation returns a value, acquire the return types.
1.1065 +
1.1066 + if getattr(target, "returns_value", 0):
1.1067 + self.namespace.set_types(target.returns)
1.1068 + self.annotate(invoke)
1.1069 +
1.1070 + # If it is a normal block, merge the locals.
1.1071 + # This can happen in addition to the above because for things like
1.1072 + # logical expressions, the namespace can be modified whilst values are
1.1073 + # returned as results.
1.1074 +
1.1075 + if getattr(invoke, "share_locals", 0):
1.1076 + self.namespace.reset()
1.1077 +
1.1078 + # Merge the locals snapshots.
1.1079 +
1.1080 + for locals in target.return_locals:
1.1081 +
1.1082 + # For blocks returning values (such as operations), do not merge
1.1083 + # snapshots or results.
1.1084 +
1.1085 + if getattr(target, "returns_value", 0):
1.1086 + self.namespace.merge_namespace(locals, everything=0)
1.1087 +
1.1088 + # For blocks not returning values (such as loops), merge
1.1089 + # snapshots and results since they contain details of genuine
1.1090 + # returns.
1.1091 +
1.1092 + else:
1.1093 + self.namespace.merge_namespace(locals)
1.1094 +
1.1095 + # Incorporate any raised exceptions.
1.1096 +
1.1097 + if not hasattr(invoke, "raises"):
1.1098 + invoke.raises = set()
1.1099 + invoke.raises.update(target.raises)
1.1100 + self.namespace.raises.update(target.raises)
1.1101 +
1.1102 + def process_args(self, invocation):
1.1103 +
1.1104 + """
1.1105 + Process the arguments associated with an 'invocation'. Return whether
1.1106 + any arguments were processed.
1.1107 + """
1.1108 +
1.1109 + self.dispatches(invocation.pos_args)
1.1110 + self.dispatch_dict(invocation.kw_args)
1.1111 +
1.1112 + # Get type information for star and dstar arguments.
1.1113 +
1.1114 + if invocation.star is not None:
1.1115 + param, default = invocation.star
1.1116 + self.dispatch(default)
1.1117 + invocation.star = param, default
1.1118 +
1.1119 + if invocation.dstar is not None:
1.1120 + param, default = invocation.dstar
1.1121 + self.dispatch(default)
1.1122 + invocation.dstar = param, default
1.1123 +
1.1124 + if invocation.pos_args or invocation.kw_args or invocation.star or invocation.dstar:
1.1125 + return 1
1.1126 + else:
1.1127 + return 0
1.1128 +
1.1129 + def make_items(self, invocation, subprogram, context):
1.1130 +
1.1131 + """
1.1132 + Make an items mapping for the 'invocation' of the 'subprogram' using the
1.1133 + given 'context' (which may be None).
1.1134 + """
1.1135 +
1.1136 + # NOTE: Support class methods!
1.1137 +
1.1138 + if context is not None and isinstance(context.type, Instance):
1.1139 + pos_args = [Self(context)] + invocation.pos_args
1.1140 + else:
1.1141 + pos_args = invocation.pos_args
1.1142 +
1.1143 + # Duplicate the keyword arguments - we remove them in processing below.
1.1144 +
1.1145 + kw_args = {}
1.1146 + kw_args.update(invocation.kw_args)
1.1147 +
1.1148 + # Sort the arguments into positional and keyword arguments.
1.1149 +
1.1150 + params = subprogram.params
1.1151 + items = []
1.1152 + star_args = []
1.1153 +
1.1154 + # Match each positional argument, taking excess arguments as star args.
1.1155 +
1.1156 + for arg in pos_args:
1.1157 + if params:
1.1158 + param, default = params[0]
1.1159 + if arg is None:
1.1160 + arg = default
1.1161 + if hasattr(arg, "types"):
1.1162 + items.append((param, arg.types))
1.1163 + else:
1.1164 + items.append((param, set())) # Annotation has not succeeded.
1.1165 + params = params[1:]
1.1166 + else:
1.1167 + star_args.append(arg)
1.1168 +
1.1169 + # Collect the remaining defaults.
1.1170 +
1.1171 + while params:
1.1172 + param, default = params[0]
1.1173 + if kw_args.has_key(param):
1.1174 + arg = kw_args[param]
1.1175 + del kw_args[param]
1.1176 + elif default is not None:
1.1177 + self.dispatch(default)
1.1178 + arg = default
1.1179 + else:
1.1180 + raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param)
1.1181 + if hasattr(arg, "types"):
1.1182 + items.append((param, arg.types))
1.1183 + else:
1.1184 + items.append((param, set())) # Annotation has not succeeded.
1.1185 + params = params[1:]
1.1186 +
1.1187 + dstar_args = kw_args.items()
1.1188 +
1.1189 + # Construct temporary objects.
1.1190 +
1.1191 + if star_args:
1.1192 + star_invocation = self.make_star_args(invocation, subprogram, star_args)
1.1193 + self.dispatch(star_invocation)
1.1194 + star_types = star_invocation.types
1.1195 + else:
1.1196 + star_types = None
1.1197 +
1.1198 + if dstar_args:
1.1199 + dstar_invocation = self.make_dstar_args(invocation, subprogram, dstar_args)
1.1200 + self.dispatch(dstar_invocation)
1.1201 + dstar_types = dstar_invocation.types
1.1202 + else:
1.1203 + dstar_types = None
1.1204 +
1.1205 + # NOTE: Merge the objects properly.
1.1206 +
1.1207 + star_types = star_types or invocation.star and invocation.star.types
1.1208 + dstar_types = dstar_types or invocation.dstar and invocation.dstar.types
1.1209 +
1.1210 + # Add star and dstar.
1.1211 +
1.1212 + if star_types is not None:
1.1213 + if subprogram.star is not None:
1.1214 + param, default = subprogram.star
1.1215 + items.append((param, star_types))
1.1216 + else:
1.1217 + raise AnnotationMessage, "Invocation provides unwanted *args."
1.1218 + elif subprogram.star is not None:
1.1219 + param, default = subprogram.star
1.1220 + if not hasattr(default, "types"):
1.1221 + subprogram.star = param, self.dispatch(default) # NOTE: Review reprocessing.
1.1222 + items.append((param, default.types))
1.1223 +
1.1224 + if dstar_types is not None:
1.1225 + if subprogram.dstar is not None:
1.1226 + param, default = subprogram.dstar
1.1227 + items.append((param, dstar_types))
1.1228 + else:
1.1229 + raise AnnotationMessage, "Invocation provides unwanted **args."
1.1230 + elif subprogram.dstar is not None:
1.1231 + param, default = subprogram.dstar
1.1232 + if not hasattr(default, "types"):
1.1233 + subprogram.dstar = param, self.dispatch(default) # NOTE: Review reprocessing.
1.1234 + items.append((param, default.types))
1.1235 +
1.1236 + # Record the parameter types.
1.1237 +
1.1238 + self.annotate_parameters(subprogram, items)
1.1239 + return subprogram.paramtypes.items()
1.1240 +
1.1241 + def make_star_args(self, invocation, subprogram, star_args):
1.1242 +
1.1243 + "Make a subprogram which initialises a list containing 'star_args'."
1.1244 +
1.1245 + if not hasattr(invocation, "stars"):
1.1246 + invocation.stars = {}
1.1247 +
1.1248 + if not invocation.stars.has_key(subprogram.full_name()):
1.1249 + instance = getattr(invocation, "instance", None)
1.1250 +
1.1251 + code = [
1.1252 + Return(
1.1253 + instance=instance,
1.1254 + expr=MakeTuple(
1.1255 + instance=instance,
1.1256 + nodes=star_args
1.1257 + )
1.1258 + )
1.1259 + ]
1.1260 +
1.1261 + new_subprogram = Subprogram(
1.1262 + instance=instance,
1.1263 + name=None,
1.1264 + returns_value=1,
1.1265 + params=[],
1.1266 + star=None,
1.1267 + dstar=None,
1.1268 + code=code
1.1269 + )
1.1270 +
1.1271 + subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
1.1272 +
1.1273 + invocation.stars[subprogram.full_name()] = InvokeRef(
1.1274 + invocation.original,
1.1275 + instance=instance,
1.1276 + produces_result=1,
1.1277 + ref=new_subprogram
1.1278 + )
1.1279 +
1.1280 + return invocation.stars[subprogram.full_name()]
1.1281 +
1.1282 + def make_dstar_args(self, invocation, subprogram, dstar_args):
1.1283 +
1.1284 + """
1.1285 + Make a subprogram which initialises a dictionary built from the given
1.1286 + 'dstar_args'.
1.1287 + """
1.1288 +
1.1289 + if not hasattr(invocation, "dstars"):
1.1290 + invocation.dstars = {}
1.1291 +
1.1292 + if not invocation.dstars.has_key(subprogram.full_name()):
1.1293 + instance = getattr(invocation, "instance", None)
1.1294 +
1.1295 + code=[
1.1296 + StoreTemp(
1.1297 + instance=instance,
1.1298 + expr=InvokeFunction(
1.1299 + invocation.original,
1.1300 + instance=instance,
1.1301 + expr=LoadAttr(
1.1302 + instance=instance,
1.1303 + expr=LoadRef(
1.1304 + instance=instance,
1.1305 + ref=self.builtins
1.1306 + ),
1.1307 + name="dict",
1.1308 + nstype="module",
1.1309 + )
1.1310 + )
1.1311 + )
1.1312 + ]
1.1313 +
1.1314 + for arg, value in dstar_args:
1.1315 +
1.1316 + # NOTE: Constant not added to table.
1.1317 +
1.1318 + constant = Constant(name=repr(arg), value=arg)
1.1319 + code += [
1.1320 + StoreTemp(
1.1321 + instance=instance,
1.1322 + expr=InvokeFunction(
1.1323 + instance=instance,
1.1324 + expr=LoadName(
1.1325 + instance=instance,
1.1326 + name=constant.typename
1.1327 + )
1.1328 + ),
1.1329 + index="const"
1.1330 + ),
1.1331 + InvokeFunction(
1.1332 + invocation.original,
1.1333 + instance=instance,
1.1334 + expr=LoadAttr(
1.1335 + instance=instance,
1.1336 + expr=LoadTemp(
1.1337 + instance=instance
1.1338 + ),
1.1339 + name="__setitem__"
1.1340 + ),
1.1341 + args=[
1.1342 + LoadTemp(
1.1343 + instance=instance,
1.1344 + index="const",
1.1345 + release=1
1.1346 + ),
1.1347 + value
1.1348 + ]
1.1349 + )
1.1350 + ]
1.1351 +
1.1352 + code += [
1.1353 + Return(
1.1354 + instance=instance,
1.1355 + expr=LoadTemp(
1.1356 + instance=instance,
1.1357 + release=1
1.1358 + )
1.1359 + )
1.1360 + ]
1.1361 +
1.1362 + new_subprogram = Subprogram(
1.1363 + instance=instance,
1.1364 + name=None,
1.1365 + returns_value=1,
1.1366 + params=[],
1.1367 + star=None,
1.1368 + dstar=None,
1.1369 + code=code
1.1370 + )
1.1371 + subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
1.1372 +
1.1373 + invocation.dstars[subprogram.full_name()] = InvokeRef(
1.1374 + invocation.original,
1.1375 + instance=instance,
1.1376 + produces_result=1,
1.1377 + ref=new_subprogram
1.1378 + )
1.1379 +
1.1380 + return invocation.dstars[subprogram.full_name()]
1.1381 +
1.1382 +# Namespace-related abstractions.
1.1383 +
1.1384 +class Namespace:
1.1385 +
1.1386 + """
1.1387 + A local namespace which may either relate to a genuine set of function
1.1388 + locals or the initialisation of a structure or module.
1.1389 + """
1.1390 +
1.1391 + def __init__(self):
1.1392 +
1.1393 + """
1.1394 + Initialise the namespace with a mapping of local names to possible
1.1395 + types, a list of return values and of possible returned local
1.1396 + namespaces. The namespace also tracks the "current" types and a mapping
1.1397 + of temporary value names to types.
1.1398 + """
1.1399 +
1.1400 + self.names = {}
1.1401 + self.returns = set()
1.1402 + self.return_locals = set()
1.1403 + self.raises = set()
1.1404 + self.temp = {}
1.1405 + self.types = set()
1.1406 +
1.1407 + def set_types(self, types):
1.1408 +
1.1409 + "Set the current collection of 'types'."
1.1410 +
1.1411 + self.types = types.copy()
1.1412 +
1.1413 + def add(self, name, types):
1.1414 +
1.1415 + "Add to the entry with the given 'name' the specified 'types'."
1.1416 +
1.1417 + if self.names.has_key(name):
1.1418 + self.names[name].update(types)
1.1419 + else:
1.1420 + self.store(name, types)
1.1421 +
1.1422 + def store(self, name, types):
1.1423 +
1.1424 + "Store in (or associate with) the given 'name' the specified 'types'."
1.1425 +
1.1426 + self.names[name] = types.copy()
1.1427 +
1.1428 + __setitem__ = store
1.1429 +
1.1430 + def load(self, name):
1.1431 +
1.1432 + "Load the types associated with the given 'name'."
1.1433 +
1.1434 + return self.names[name]
1.1435 +
1.1436 + __getitem__ = load
1.1437 +
1.1438 + def has_key(self, name):
1.1439 + return self.names.has_key(name)
1.1440 +
1.1441 + def revoke(self, name, type):
1.1442 +
1.1443 + "Revoke from the entry for the given 'name' the specified 'type'."
1.1444 +
1.1445 + new_types = self.names[name].copy()
1.1446 + new_types.remove(type)
1.1447 + self.names[name] = new_types
1.1448 +
1.1449 + def revoke_exception_type(self, type):
1.1450 +
1.1451 + "Revoke the given 'type' from the collection of exception types."
1.1452 +
1.1453 + self.raises.remove(type)
1.1454 +
1.1455 + def revoke_temp_type(self, index, type):
1.1456 +
1.1457 + "Revoke from the temporary variable 'index' the given 'type'."
1.1458 +
1.1459 + new_types = self.temp[index][-1].copy()
1.1460 + new_types.remove(type)
1.1461 + self.temp[index][-1] = new_types
1.1462 +
1.1463 + def merge_namespace(self, namespace, everything=1):
1.1464 +
1.1465 + """
1.1466 + Merge items from the given 'namespace' with this namespace. When the
1.1467 + optional 'everything' parameter is set to a false value (unlike the
1.1468 + default), return values and locals snapshots will not be copied to this
1.1469 + namespace.
1.1470 + """
1.1471 +
1.1472 + self.merge_items(namespace.names.items())
1.1473 + self.raises.update(namespace.raises)
1.1474 + if everything:
1.1475 + self.returns.update(namespace.returns)
1.1476 + self.return_locals.update(namespace.return_locals)
1.1477 + for name, values in namespace.temp.items():
1.1478 + if values:
1.1479 + if not self.temp.has_key(name) or not self.temp[name]:
1.1480 + self.temp[name] = [set()]
1.1481 + self.temp[name][-1].update(values[-1])
1.1482 +
1.1483 + def merge_items(self, items):
1.1484 +
1.1485 + "Merge the given 'items' with this namespace."
1.1486 +
1.1487 + for name, types in items:
1.1488 + self.merge(name, types)
1.1489 +
1.1490 + def merge(self, name, types):
1.1491 +
1.1492 + "Merge the entry for the given 'name' and 'types' with this namespace."
1.1493 +
1.1494 + if not self.names.has_key(name):
1.1495 + self.names[name] = types.copy()
1.1496 + else:
1.1497 + existing = self.names[name]
1.1498 + existing.update(types)
1.1499 +
1.1500 + def snapshot(self):
1.1501 +
1.1502 + "Make a snapshot of the locals and remember them."
1.1503 +
1.1504 + namespace = Namespace()
1.1505 + namespace.merge_namespace(self)
1.1506 + self.return_locals.add(namespace)
1.1507 +
1.1508 + def reset(self):
1.1509 +
1.1510 + "Reset a namespace in preparation for merging with returned locals."
1.1511 +
1.1512 + self.names = {}
1.1513 +
1.1514 + def __repr__(self):
1.1515 + return repr(self.names) + " (temp) " + repr(self.temp)
1.1516 +
1.1517 +class Importer:
1.1518 +
1.1519 + "An import machine, searching for and loading modules."
1.1520 +
1.1521 + def __init__(self, path=None):
1.1522 +
1.1523 + """
1.1524 + Initialise the importer with the given search 'path' - a list of
1.1525 + directories to search for Python modules.
1.1526 + """
1.1527 +
1.1528 + self.path = path or [os.getcwd()]
1.1529 + self.path.append(libdir)
1.1530 + self.modules = {}
1.1531 +
1.1532 + def find_in_path(self, name):
1.1533 +
1.1534 + """
1.1535 + Find the given module 'name' in the search path, returning None where no
1.1536 + such module could be found, or a 2-tuple from the 'find' method
1.1537 + otherwise.
1.1538 + """
1.1539 +
1.1540 + for d in self.path:
1.1541 + m = self.find(d, name)
1.1542 + if m: return m
1.1543 + return None
1.1544 +
1.1545 + def find(self, d, name):
1.1546 +
1.1547 + """
1.1548 + In the directory 'd', find the given module 'name', where 'name' can
1.1549 + either refer to a single file module or to a package. Return None if the
1.1550 + 'name' cannot be associated with either a file or a package directory,
1.1551 + or a 2-tuple from '_find_package' or '_find_module' otherwise.
1.1552 + """
1.1553 +
1.1554 + m = self._find_package(d, name)
1.1555 + if m: return m
1.1556 + m = self._find_module(d, name)
1.1557 + if m: return m
1.1558 + return None
1.1559 +
1.1560 + def _find_module(self, d, name):
1.1561 +
1.1562 + """
1.1563 + In the directory 'd', find the given module 'name', returning None where
1.1564 + no suitable file exists in the directory, or a 2-tuple consisting of
1.1565 + None (indicating that no package directory is involved) and a filename
1.1566 + indicating the location of the module.
1.1567 + """
1.1568 +
1.1569 + name_py = name + os.extsep + "py"
1.1570 + filename = self._find_file(d, name_py)
1.1571 + if filename:
1.1572 + return None, filename
1.1573 + return None
1.1574 +
1.1575 + def _find_package(self, d, name):
1.1576 +
1.1577 + """
1.1578 + In the directory 'd', find the given package 'name', returning None
1.1579 + where no suitable package directory exists, or a 2-tuple consisting of
1.1580 + a directory (indicating the location of the package directory itself)
1.1581 + and a filename indicating the location of the __init__.py module which
1.1582 + declares the package's top-level contents.
1.1583 + """
1.1584 +
1.1585 + filename = self._find_file(d, name)
1.1586 + if filename:
1.1587 + init_py = "__init__" + os.path.extsep + "py"
1.1588 + init_py_filename = self._find_file(filename, init_py)
1.1589 + if init_py_filename:
1.1590 + return filename, init_py_filename
1.1591 + return None
1.1592 +
1.1593 + def _find_file(self, d, filename):
1.1594 +
1.1595 + """
1.1596 + Return the filename obtained when searching the directory 'd' for the
1.1597 + given 'filename', or None if no actual file exists for the filename.
1.1598 + """
1.1599 +
1.1600 + filename = os.path.join(d, filename)
1.1601 + if os.path.exists(filename):
1.1602 + return filename
1.1603 + else:
1.1604 + return None
1.1605 +
1.1606 + def load(self, name, builtins, alias=None):
1.1607 +
1.1608 + """
1.1609 + Load the module or package with the given 'name' and using the specified
1.1610 + 'builtins'. Return an Attribute object referencing the loaded module or
1.1611 + package, or None if no such module or package exists.
1.1612 + """
1.1613 +
1.1614 + if self.modules.has_key(name):
1.1615 + return Attribute(None, self.modules[name])
1.1616 +
1.1617 + path = name.split(".")
1.1618 + m = self.find_in_path(path[0])
1.1619 + if not m:
1.1620 + return None # NOTE: Import error.
1.1621 + d, filename = m
1.1622 +
1.1623 + if self.modules.has_key(path[0]):
1.1624 + top = module = self.modules[path[0]]
1.1625 + else:
1.1626 + top = module = self.modules[path[0]] = load(filename, builtins, path[0], self, no_annotate=1)
1.1627 + annotate(module, builtins, self)
1.1628 +
1.1629 + if len(path) > 1:
1.1630 + path_so_far = path[:1]
1.1631 + for p in path[1:]:
1.1632 + path_so_far.append(p)
1.1633 + m = self.find(d, p)
1.1634 + if not m:
1.1635 + return None # NOTE: Import error.
1.1636 + d, filename = m
1.1637 + module_name = ".".join(path_so_far)
1.1638 +
1.1639 + if self.modules.has_key(module_name):
1.1640 + submodule = self.modules[module_name]
1.1641 + else:
1.1642 + submodule = self.modules[module_name] = load(filename, builtins, module_name, self, no_annotate=1)
1.1643 + annotate(submodule, builtins, self)
1.1644 +
1.1645 + # Store the submodule within its parent module.
1.1646 +
1.1647 + module.namespace[p] = [Attribute(None, submodule)]
1.1648 + module = submodule
1.1649 +
1.1650 + if alias:
1.1651 + return Attribute(None, module)
1.1652 + else:
1.1653 + return Attribute(None, top)
1.1654 +
1.1655 +def combine(target, additions):
1.1656 +
1.1657 + """
1.1658 + Merge into the 'target' sequence the given 'additions', preventing duplicate
1.1659 + items.
1.1660 + """
1.1661 +
1.1662 + for addition in additions:
1.1663 + if addition not in target:
1.1664 + target.append(addition)
1.1665 +
1.1666 +def find_attributes(structure, name):
1.1667 +
1.1668 + """
1.1669 + Find for the given 'structure' all attributes for the given 'name', visiting
1.1670 + base classes where appropriate and returning the attributes in order of
1.1671 + descending precedence for all possible base classes.
1.1672 +
1.1673 + The elements in the result list are 2-tuples which contain the attribute and
1.1674 + the structure involved in accessing the attribute.
1.1675 + """
1.1676 +
1.1677 + # First attempt to search the instance/class namespace.
1.1678 +
1.1679 + try:
1.1680 + l = structure.namespace.load(name)
1.1681 + attributes = []
1.1682 + for attribute in l:
1.1683 + attributes.append((attribute, structure))
1.1684 +
1.1685 + # If that does not work, attempt to investigate any class or base classes.
1.1686 +
1.1687 + except KeyError:
1.1688 + attributes = []
1.1689 +
1.1690 + # Investigate any instance's implementing class.
1.1691 +
1.1692 + if isinstance(structure, Instance):
1.1693 + for attr in structure.namespace.load("__class__"):
1.1694 + cls = attr.type
1.1695 + l = get_attributes(cls, name)
1.1696 + combine(attributes, l)
1.1697 +
1.1698 + # Investigate any class's base classes.
1.1699 +
1.1700 + elif isinstance(structure, Class):
1.1701 +
1.1702 + # If no base classes exist, return an indicator that no attribute
1.1703 + # exists.
1.1704 +
1.1705 + if not structure.base_refs:
1.1706 + return [(None, structure)]
1.1707 +
1.1708 + # Otherwise, find all possible base classes.
1.1709 +
1.1710 + for base_refs in structure.base_refs:
1.1711 + base_attributes = []
1.1712 +
1.1713 + # For each base class, find attributes either in the base
1.1714 + # class or its own base classes.
1.1715 +
1.1716 + for base_ref in base_refs:
1.1717 + l = get_attributes(base_ref, name)
1.1718 + combine(base_attributes, l)
1.1719 +
1.1720 + combine(attributes, base_attributes)
1.1721 +
1.1722 + return attributes
1.1723 +
1.1724 +def get_attributes(structure, name):
1.1725 +
1.1726 + """
1.1727 + Return all possible attributes for the given 'structure' having the given
1.1728 + 'name', wrapping each attribute in an Attribute object which includes
1.1729 + context information for the attribute access.
1.1730 +
1.1731 + The elements in the result list are 2-tuples which contain the attribute and
1.1732 + the structure involved in accessing the attribute.
1.1733 + """
1.1734 +
1.1735 + if isinstance(structure, Attribute):
1.1736 + structure = structure.type
1.1737 + results = []
1.1738 + for attribute, accessor in find_attributes(structure, name):
1.1739 +
1.1740 + # Detect class attribute access via instances.
1.1741 +
1.1742 + if attribute is not None and isinstance(structure, Instance) and isinstance(accessor, Class):
1.1743 + attribute = accessor.get_attribute_for_instance(attribute, structure)
1.1744 +
1.1745 + # Produce an attribute with the appropriate context.
1.1746 +
1.1747 + if attribute is not None and isinstance(structure, Structure):
1.1748 + results.append((Attribute(structure, attribute.type), accessor))
1.1749 + else:
1.1750 + results.append((attribute, accessor))
1.1751 +
1.1752 + return results
1.1753 +
1.1754 +def prompt(vars):
1.1755 + try:
1.1756 + while 1:
1.1757 + s = raw_input("> ")
1.1758 + print eval(s, vars)
1.1759 + except EOFError:
1.1760 + pass
1.1761 +
1.1762 +# Convenience functions.
1.1763 +
1.1764 +def load(name, builtins=None, module_name=None, importer=None, no_annotate=0):
1.1765 +
1.1766 + """
1.1767 + Load the module with the given 'name' (which may be a full module path),
1.1768 + using the optional 'builtins' to resolve built-in names, and using the
1.1769 + optional 'importer' to provide a means of finding and loading modules.
1.1770 + """
1.1771 +
1.1772 + module = simplify.simplify(name, builtins is None, module_name)
1.1773 + simplify.fixnames.fix(module, builtins)
1.1774 + if not no_annotate:
1.1775 + annotate(module, builtins, importer)
1.1776 + return module
1.1777 +
1.1778 +def annotate(module, builtins=None, importer=None):
1.1779 +
1.1780 + """
1.1781 + Annotate the given 'module', also employing the optional 'builtins' module,
1.1782 + if specified. If the optional 'importer' is given, use that to find and load
1.1783 + modules.
1.1784 + """
1.1785 +
1.1786 + annotator = Annotator(importer)
1.1787 + if builtins is not None:
1.1788 + annotator.process(module, builtins)
1.1789 + else:
1.1790 + annotator.process(module)
1.1791 +
1.1792 +# vim: tabstop=4 expandtab shiftwidth=4