1.1 --- a/annotate.py Sun Dec 03 01:36:02 2006 +0100
1.2 +++ b/annotate.py Sun Dec 03 22:04:21 2006 +0100
1.3 @@ -57,14 +57,25 @@
1.4 self.count = 0
1.5
1.6 def init(self, node):
1.7 +
1.8 + "Initialise a 'node' for annotation."
1.9 +
1.10 if not hasattr(node, "types"):
1.11 node.types = []
1.12
1.13 def annotate(self, node, types):
1.14 +
1.15 + "Annotate the given 'node' with the given 'types'."
1.16 +
1.17 self.init(node)
1.18 self.combine(node.types, types)
1.19
1.20 def combine(self, target, types):
1.21 +
1.22 + """
1.23 + Combine the 'target' list with the given 'types', counting new members.
1.24 + """
1.25 +
1.26 for type in types:
1.27 if type not in target:
1.28 target.append(type)
1.29 @@ -242,14 +253,48 @@
1.30 except AnnotationMessage, exc:
1.31 raise AnnotationError(exc, node)
1.32
1.33 - # Program structure/control-flow.
1.34 + # Specific node methods.
1.35
1.36 def visitAssign(self, assign):
1.37 +
1.38 + """
1.39 + Return the 'assign' node whose contents (merely a group of nodes) have
1.40 + been processed.
1.41 + """
1.42 +
1.43 assign.code = self.dispatches(assign.code)
1.44 return assign
1.45
1.46 + def visitCheckExc(self, checkexc):
1.47 +
1.48 + """
1.49 + Return the 'checkexc' node, processing the expression to find the
1.50 + possible types of the exception, and processing each choice to build a
1.51 + list of checked types for the exception.
1.52 + """
1.53 +
1.54 + checkexc.expr = self.dispatch(checkexc.expr)
1.55 + expr_types = self.namespace.types
1.56 + choice_types = []
1.57 + choices = []
1.58 + for choice in checkexc.choices:
1.59 + choices.append(self.dispatch(choice))
1.60 + choice_types += self.namespace.types
1.61 + for expr_type in expr_types:
1.62 + if expr_type.type.get_class() not in choice_types:
1.63 + self._prune_non_accesses(checkexc.expr, expr_type)
1.64 + return checkexc
1.65 +
1.66 def visitConditional(self, conditional):
1.67
1.68 + """
1.69 + Return the 'conditional' node, processing the test, body and else
1.70 + clauses and recording their processed forms. The body and else clauses
1.71 + are processed within their own namespaces, and the test is also
1.72 + processed in its own namespace if 'isolate_test' is set on the
1.73 + 'conditional' node.
1.74 + """
1.75 +
1.76 # Conditionals keep local namespace changes isolated.
1.77 # With Return nodes inside the body/else sections, the changes are
1.78 # communicated to the caller.
1.79 @@ -305,18 +350,285 @@
1.80
1.81 return conditional
1.82
1.83 + def visitLoadAttr(self, loadattr):
1.84 +
1.85 + """
1.86 + Return the 'loadattr' node, processing and storing the expression, and
1.87 + using the expression's types to construct records of accesses and
1.88 + non-accesses using the stated attribute name.
1.89 + """
1.90 +
1.91 + loadattr.expr = self.dispatch(loadattr.expr)
1.92 + types = []
1.93 + non_accesses = []
1.94 + accesses = {}
1.95 + for attr in self.namespace.types:
1.96 + attributes = get_attributes(attr.type, loadattr.name)
1.97 + if not attributes:
1.98 + if not attr.type in non_accesses:
1.99 + non_accesses.append(attr)
1.100 +
1.101 + # Revoke this type from any name involved.
1.102 +
1.103 + self._prune_non_accesses(loadattr.expr, attr)
1.104 +
1.105 + for attribute, accessor in attributes:
1.106 + if attribute is not None:
1.107 + types.append(attribute)
1.108 + if not accesses.has_key(attr.type):
1.109 + accesses[attr.type] = []
1.110 + if not (attribute, accessor) in accesses[attr.type]:
1.111 + accesses[attr.type].append((attribute, accessor))
1.112 + else:
1.113 + if not attr in non_accesses:
1.114 + non_accesses.append(attr)
1.115 +
1.116 + # Revoke this type from any name involved.
1.117 +
1.118 + self._prune_non_accesses(loadattr.expr, attr)
1.119 +
1.120 + if not types:
1.121 + print "No attribute found for", loadattr.name, "given", self.namespace.types
1.122 + self.namespace.set_types(types)
1.123 + loadattr.non_accesses = non_accesses
1.124 + loadattr.accesses = accesses
1.125 + self.annotate(loadattr)
1.126 + return loadattr
1.127 +
1.128 + def _prune_non_accesses(self, expr, attr):
1.129 +
1.130 + """
1.131 + Prune type information from 'expr' where the given 'attr' has been
1.132 + shown to be a non-access.
1.133 + """
1.134 +
1.135 + if isinstance(expr, LoadName):
1.136 + self.namespace.revoke(expr.name, attr)
1.137 + elif isinstance(expr, LoadAttr):
1.138 + for expr_attr in expr.expr.types:
1.139 + if hasattr(expr_attr.type, "namespace"):
1.140 + expr_attr.type.namespace.revoke(expr.name, attr)
1.141 + elif isinstance(expr, LoadExc):
1.142 + self.namespace.revoke_exception_type(attr)
1.143 +
1.144 + def visitLoadExc(self, loadexc):
1.145 +
1.146 + """
1.147 + Return the 'loadexc' node, discovering the possible exception types
1.148 + raised.
1.149 + """
1.150 +
1.151 + self.namespace.types = self.namespace.raises[:]
1.152 + self.annotate(loadexc)
1.153 + return loadexc
1.154 +
1.155 + def visitLoadName(self, loadname):
1.156 +
1.157 + """
1.158 + Return the 'loadname' node, processing the name information on the node
1.159 + to determine which types are involved with the name.
1.160 + """
1.161 +
1.162 + self.namespace.set_types(self.namespace.load(loadname.name))
1.163 + result = loadname
1.164 + self.annotate(result)
1.165 + return result
1.166 +
1.167 + def visitLoadRef(self, loadref):
1.168 +
1.169 + """
1.170 + Return the 'loadref' node, obtaining type information about the
1.171 + reference stated on the node.
1.172 + """
1.173 +
1.174 + self.namespace.set_types([Attribute(None, loadref.ref)])
1.175 + self.annotate(loadref)
1.176 + return loadref
1.177 +
1.178 + def visitLoadTemp(self, loadtemp):
1.179 +
1.180 + """
1.181 + Return the 'loadtemp' node, obtaining type information about the
1.182 + temporary variable accessed, and removing variable information where the
1.183 + 'release' attribute has been set on the node.
1.184 + """
1.185 +
1.186 + index = getattr(loadtemp, "index", None)
1.187 + try:
1.188 + if getattr(loadtemp, "release", 0):
1.189 + self.namespace.set_types(self.namespace.temp[index].pop())
1.190 + else:
1.191 + self.namespace.set_types(self.namespace.temp[index][-1])
1.192 + except KeyError:
1.193 + raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.194 + self.annotate(loadtemp)
1.195 + return loadtemp
1.196 +
1.197 def visitModule(self, module):
1.198 +
1.199 + """
1.200 + Return the processed 'module' whose contents (merely a group of nodes)
1.201 + are processed.
1.202 + """
1.203 +
1.204 module.code = self.dispatches(module.code)
1.205 return module
1.206
1.207 + def visitNot(self, not_):
1.208 +
1.209 + "Return the 'not_' node whose expression is processed."
1.210 +
1.211 + not_.expr = self.dispatch(not_.expr)
1.212 + return not_
1.213 +
1.214 def visitPass(self, pass_):
1.215 +
1.216 + "Return the unprocessed 'pass_' node."
1.217 +
1.218 return pass_
1.219
1.220 + def visitRaise(self, raise_):
1.221 +
1.222 + """
1.223 + Return the 'raise_' node, processing any traceback information along
1.224 + with the raised exception expression, converting the node into a kind of
1.225 + invocation where the expression is found not to be an invocation itself.
1.226 + This node affects the namespace, adding exception types to the list of
1.227 + those raised in the namespace.
1.228 + """
1.229 +
1.230 + if getattr(raise_, "traceback", None) is not None:
1.231 + raise_.traceback = self.dispatch(raise_.traceback)
1.232 + raise_.expr = self.dispatch(raise_.expr)
1.233 +
1.234 + # Handle bare name exceptions by converting any classes to instances.
1.235 +
1.236 + if not isinstance(raise_.expr, InvokeFunction):
1.237 + raise_.pos_args = []
1.238 + raise_.kw_args = {}
1.239 + raise_.star = None
1.240 + raise_.dstar = None
1.241 + types = []
1.242 + for attr in self.namespace.types:
1.243 + if isinstance(attr.type, Class):
1.244 + self._visitInvoke(raise_, [attr], have_args=0)
1.245 + types += self.namespace.types
1.246 + else:
1.247 + types = self.namespace.types
1.248 +
1.249 + combine(self.namespace.raises, types)
1.250 + return raise_
1.251 +
1.252 + def visitReleaseTemp(self, releasetemp):
1.253 +
1.254 + """
1.255 + Return the 'releasetemp' node, removing temporary variable information
1.256 + from the current namespace.
1.257 + """
1.258 +
1.259 + index = getattr(releasetemp, "index", None)
1.260 + try:
1.261 + self.namespace.temp[index].pop()
1.262 + except KeyError:
1.263 + raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.264 + except IndexError:
1.265 + pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index
1.266 + return releasetemp
1.267 +
1.268 + def visitReturn(self, return_):
1.269 +
1.270 + """
1.271 + Return the 'return_' node, processing any expression and obtaining type
1.272 + information to be accumulated in the current namespace's list of return
1.273 + types. A snapshot of the namespace is taken for the purposes of
1.274 + reconciling or merging namespaces where subprograms actually share
1.275 + locals with their callers.
1.276 + """
1.277 +
1.278 + if hasattr(return_, "expr"):
1.279 + return_.expr = self.dispatch(return_.expr)
1.280 + combine(self.namespace.returns, self.namespace.types)
1.281 + self.annotate(return_)
1.282 + self.namespace.snapshot()
1.283 + return return_
1.284 +
1.285 + visitReturnFromBlock = visitReturn
1.286 + visitReturnFromFunction = visitReturn
1.287 +
1.288 + def visitStoreAttr(self, storeattr):
1.289 +
1.290 + """
1.291 + Return the 'storeattr' node, processing the expression and target, and
1.292 + using the type information obtained to build records of legitimate
1.293 + writes to the stated attribute, along with "impossible" non-writes to
1.294 + the attribute.
1.295 + """
1.296 +
1.297 + storeattr.expr = self.dispatch(storeattr.expr)
1.298 + expr = self.namespace.types
1.299 + storeattr.lvalue = self.dispatch(storeattr.lvalue)
1.300 + writes = {}
1.301 + non_writes = []
1.302 + for attr in self.namespace.types:
1.303 + if attr is None:
1.304 + if not attr in non_writes:
1.305 + non_writes.append(attr)
1.306 + continue
1.307 + attr.type.namespace.add(storeattr.name, expr)
1.308 + writes[attr.type] = attr.type.namespace.load(storeattr.name)
1.309 + if not writes:
1.310 + print "Unable to store attribute", storeattr.name, "given", self.namespace.types
1.311 + storeattr.writes = writes
1.312 + storeattr.non_writes = non_writes
1.313 + return storeattr
1.314 +
1.315 + def visitStoreName(self, storename):
1.316 +
1.317 + """
1.318 + Return the 'storename' node, processing the expression on the node and
1.319 + associating the type information obtained with the stated name in the
1.320 + current namespace.
1.321 + """
1.322 +
1.323 + storename.expr = self.dispatch(storename.expr)
1.324 + self.namespace.store(storename.name, self.namespace.types)
1.325 + return storename
1.326 +
1.327 + def visitStoreTemp(self, storetemp):
1.328 +
1.329 + """
1.330 + Return the 'storetemp' node, processing the expression on the node and
1.331 + associating the type information obtained with a temporary variable in
1.332 + the current namespace.
1.333 + """
1.334 +
1.335 + storetemp.expr = self.dispatch(storetemp.expr)
1.336 + index = getattr(storetemp, "index", None)
1.337 + if not self.namespace.temp.has_key(index):
1.338 + self.namespace.temp[index] = []
1.339 + self.namespace.temp[index].append(self.namespace.types)
1.340 + return storetemp
1.341 +
1.342 def visitSubprogram(self, subprogram):
1.343 +
1.344 + """
1.345 + Return the 'subprogram' node, processing its contents (a group of nodes
1.346 + comprising the subprogram).
1.347 + """
1.348 +
1.349 subprogram.code = self.dispatches(subprogram.code)
1.350 return subprogram
1.351
1.352 def visitTry(self, try_):
1.353 +
1.354 + """
1.355 + Return the 'try_' node, processing the body clause in its own namespace
1.356 + derived from the current namespace, processing any handler clause using
1.357 + the namespace information accumulated in the body, and processing any
1.358 + else and finally clauses, attempting to supply each with appropriate
1.359 + namespace information.
1.360 + """
1.361 +
1.362 is_module = self.namespace is self.module.namespace
1.363
1.364 try_.body = self.dispatches(try_.body)
1.365 @@ -369,177 +681,6 @@
1.366 try_.finally_ = self.dispatches(try_.finally_)
1.367 return try_
1.368
1.369 - # Namespace operations.
1.370 -
1.371 - def visitCheckExc(self, checkexc):
1.372 - checkexc.expr = self.dispatch(checkexc.expr)
1.373 - expr_types = self.namespace.types
1.374 - choice_types = []
1.375 - choices = []
1.376 - for choice in checkexc.choices:
1.377 - choices.append(self.dispatch(choice))
1.378 - choice_types += self.namespace.types
1.379 - for expr_type in expr_types:
1.380 - if expr_type.type.get_class() not in choice_types:
1.381 - self._prune_non_accesses(checkexc.expr, expr_type)
1.382 - return checkexc
1.383 -
1.384 - def visitLoadAttr(self, loadattr):
1.385 - loadattr.expr = self.dispatch(loadattr.expr)
1.386 - types = []
1.387 - non_accesses = []
1.388 - accesses = {}
1.389 - for attr in self.namespace.types:
1.390 - attributes = get_attributes(attr.type, loadattr.name)
1.391 - if not attributes:
1.392 - if not attr.type in non_accesses:
1.393 - non_accesses.append(attr)
1.394 -
1.395 - # Revoke this type from any name involved.
1.396 -
1.397 - self._prune_non_accesses(loadattr.expr, attr)
1.398 -
1.399 - for attribute, accessor in attributes:
1.400 - if attribute is not None:
1.401 - types.append(attribute)
1.402 - if not accesses.has_key(attr.type):
1.403 - accesses[attr.type] = []
1.404 - if not (attribute, accessor) in accesses[attr.type]:
1.405 - accesses[attr.type].append((attribute, accessor))
1.406 - else:
1.407 - if not attr in non_accesses:
1.408 - non_accesses.append(attr)
1.409 -
1.410 - # Revoke this type from any name involved.
1.411 -
1.412 - self._prune_non_accesses(loadattr.expr, attr)
1.413 -
1.414 - if not types:
1.415 - print "No attribute found for", loadattr.name, "given", self.namespace.types
1.416 - self.namespace.set_types(types)
1.417 - loadattr.non_accesses = non_accesses
1.418 - loadattr.accesses = accesses
1.419 - self.annotate(loadattr)
1.420 - return loadattr
1.421 -
1.422 - def _prune_non_accesses(self, expr, attr):
1.423 - if isinstance(expr, LoadName):
1.424 - self.namespace.revoke(expr.name, attr)
1.425 - elif isinstance(expr, LoadAttr):
1.426 - for expr_attr in expr.expr.types:
1.427 - if hasattr(expr_attr.type, "namespace"):
1.428 - expr_attr.type.namespace.revoke(expr.name, attr)
1.429 - elif isinstance(expr, LoadExc):
1.430 - self.namespace.revoke_exception_type(attr)
1.431 -
1.432 - def visitLoadExc(self, loadexc):
1.433 - self.namespace.types = self.namespace.raises[:]
1.434 - self.annotate(loadexc)
1.435 - return loadexc
1.436 -
1.437 - def visitLoadName(self, loadname):
1.438 - self.namespace.set_types(self.namespace.load(loadname.name))
1.439 - result = loadname
1.440 - self.annotate(result)
1.441 - return result
1.442 -
1.443 - def visitLoadRef(self, loadref):
1.444 - self.namespace.set_types([Attribute(None, loadref.ref)])
1.445 - self.annotate(loadref)
1.446 - return loadref
1.447 -
1.448 - def visitLoadTemp(self, loadtemp):
1.449 - index = getattr(loadtemp, "index", None)
1.450 - try:
1.451 - if getattr(loadtemp, "release", 0):
1.452 - self.namespace.set_types(self.namespace.temp[index].pop())
1.453 - else:
1.454 - self.namespace.set_types(self.namespace.temp[index][-1])
1.455 - except KeyError:
1.456 - raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.457 - self.annotate(loadtemp)
1.458 - return loadtemp
1.459 -
1.460 - def visitNot(self, not_):
1.461 - not_.expr = self.dispatch(not_.expr)
1.462 - return not_
1.463 -
1.464 - def visitRaise(self, raise_):
1.465 - if getattr(raise_, "traceback", None) is not None:
1.466 - raise_.traceback = self.dispatch(raise_.traceback)
1.467 - raise_.expr = self.dispatch(raise_.expr)
1.468 -
1.469 - # Handle bare name exceptions by converting any classes to instances.
1.470 -
1.471 - if not isinstance(raise_.expr, InvokeFunction):
1.472 - raise_.pos_args = []
1.473 - raise_.kw_args = {}
1.474 - raise_.star = None
1.475 - raise_.dstar = None
1.476 - types = []
1.477 - for attr in self.namespace.types:
1.478 - if isinstance(attr.type, Class):
1.479 - self._visitInvoke(raise_, [attr], have_args=0)
1.480 - types += self.namespace.types
1.481 - else:
1.482 - types = self.namespace.types
1.483 -
1.484 - combine(self.namespace.raises, types)
1.485 - return raise_
1.486 -
1.487 - def visitReleaseTemp(self, releasetemp):
1.488 - index = getattr(releasetemp, "index", None)
1.489 - try:
1.490 - self.namespace.temp[index].pop()
1.491 - except KeyError:
1.492 - raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.493 - except IndexError:
1.494 - pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index
1.495 - return releasetemp
1.496 -
1.497 - def visitReturn(self, return_):
1.498 - if hasattr(return_, "expr"):
1.499 - return_.expr = self.dispatch(return_.expr)
1.500 - combine(self.namespace.returns, self.namespace.types)
1.501 - self.annotate(return_)
1.502 - self.namespace.snapshot()
1.503 - return return_
1.504 -
1.505 - visitReturnFromBlock = visitReturn
1.506 - visitReturnFromFunction = visitReturn
1.507 -
1.508 - def visitStoreAttr(self, storeattr):
1.509 - storeattr.expr = self.dispatch(storeattr.expr)
1.510 - expr = self.namespace.types
1.511 - storeattr.lvalue = self.dispatch(storeattr.lvalue)
1.512 - writes = {}
1.513 - non_writes = []
1.514 - for attr in self.namespace.types:
1.515 - if attr is None:
1.516 - if not attr in non_writes:
1.517 - non_writes.append(attr)
1.518 - continue
1.519 - attr.type.namespace.add(storeattr.name, expr)
1.520 - writes[attr.type] = attr.type.namespace.load(storeattr.name)
1.521 - if not writes:
1.522 - print "Unable to store attribute", storeattr.name, "given", self.namespace.types
1.523 - storeattr.writes = writes
1.524 - storeattr.non_writes = non_writes
1.525 - return storeattr
1.526 -
1.527 - def visitStoreName(self, storename):
1.528 - storename.expr = self.dispatch(storename.expr)
1.529 - self.namespace.store(storename.name, self.namespace.types)
1.530 - return storename
1.531 -
1.532 - def visitStoreTemp(self, storetemp):
1.533 - storetemp.expr = self.dispatch(storetemp.expr)
1.534 - index = getattr(storetemp, "index", None)
1.535 - if not self.namespace.temp.has_key(index):
1.536 - self.namespace.temp[index] = []
1.537 - self.namespace.temp[index].append(self.namespace.types)
1.538 - return storetemp
1.539 -
1.540 # Invocations are a chapter of their own.
1.541
1.542 def visitInvokeBlock(self, invoke):
1.543 @@ -700,7 +841,8 @@
1.544 using_module_namespace = 0
1.545 else:
1.546 items = self.make_items(invoke, target, context)
1.547 - namespace = self.make_namespace(items)
1.548 + namespace = Namespace()
1.549 + namespace.merge_items(items)
1.550 using_module_namespace = 0
1.551
1.552 # Test to see if anything has changed.
1.553 @@ -954,11 +1096,6 @@
1.554
1.555 return invocation.stars[subprogram.full_name()]
1.556
1.557 - def make_namespace(self, items):
1.558 - namespace = Namespace()
1.559 - namespace.merge_items(items)
1.560 - return namespace
1.561 -
1.562 # Namespace-related abstractions.
1.563
1.564 class Namespace: