PageRenderTime 219ms CodeModel.GetById 153ms app.highlight 58ms RepoModel.GetById 2ms app.codeStats 0ms

/pypy/annotation/annrpython.py

https://bitbucket.org/pypy/pypy/
Python | 784 lines | 766 code | 6 blank | 12 comment | 6 complexity | 58105527228e5da522aa3f337e768539 MD5 | raw file
  1import sys
  2import types
  3from pypy.tool.ansi_print import ansi_log, raise_nicer_exception
  4from pypy.tool.pairtype import pair
  5from pypy.tool.error import (format_blocked_annotation_error,
  6                             format_someobject_error, AnnotatorError)
  7from pypy.objspace.flow.model import (Variable, Constant, FunctionGraph,
  8                                      c_last_exception, checkgraph)
  9from pypy.translator import simplify, transform
 10from pypy.annotation import model as annmodel, signature
 11from pypy.annotation.bookkeeper import Bookkeeper
 12import py
 13log = py.log.Producer("annrpython")
 14py.log.setconsumer("annrpython", ansi_log)
 15
 16
 17FAIL = object()
 18
 19class RPythonAnnotator(object):
 20    """Block annotator for RPython.
 21    See description in doc/translation.txt."""
 22
 23    def __init__(self, translator=None, policy=None, bookkeeper=None):
 24        import pypy.rpython.ootypesystem.ooregistry # has side effects
 25        import pypy.rpython.extfuncregistry # has side effects
 26        import pypy.rlib.nonconst # has side effects
 27
 28        if translator is None:
 29            # interface for tests
 30            from pypy.translator.translator import TranslationContext
 31            translator = TranslationContext()
 32            translator.annotator = self
 33        self.translator = translator
 34        self.pendingblocks = {}  # map {block: graph-containing-it}
 35        self.bindings = {}       # map Variables to SomeValues
 36        self.annotated = {}      # set of blocks already seen
 37        self.added_blocks = None # see processblock() below
 38        self.links_followed = {} # set of links that have ever been followed
 39        self.notify = {}        # {block: {positions-to-reflow-from-when-done}}
 40        self.fixed_graphs = {}  # set of graphs not to annotate again
 41        self.blocked_blocks = {} # set of {blocked_block: graph}
 42        # --- the following information is recorded for debugging only ---
 43        # --- and only if annotation.model.DEBUG is kept to True
 44        self.why_not_annotated = {} # {block: (exc_type, exc_value, traceback)}
 45                                    # records the location of BlockedInference
 46                                    # exceptions that blocked some blocks.
 47        self.blocked_graphs = {} # set of graphs that have blocked blocks
 48        self.bindingshistory = {}# map Variables to lists of SomeValues
 49        self.binding_caused_by = {}     # map Variables to position_keys
 50               # records the caller position that caused bindings of inputargs
 51               # to be updated
 52        self.binding_cause_history = {} # map Variables to lists of positions
 53                # history of binding_caused_by, kept in sync with
 54                # bindingshistory
 55        self.reflowcounter = {}
 56        self.return_bindings = {} # map return Variables to their graphs
 57        # --- end of debugging information ---
 58        self.frozen = False
 59        if policy is None:
 60            from pypy.annotation.policy import AnnotatorPolicy
 61            self.policy = AnnotatorPolicy()
 62        else:
 63            self.policy = policy
 64        if bookkeeper is None:
 65            bookkeeper = Bookkeeper(self)
 66        self.bookkeeper = bookkeeper
 67
 68    def __getstate__(self):
 69        attrs = """translator pendingblocks bindings annotated links_followed
 70        notify bookkeeper frozen policy added_blocks""".split()
 71        ret = self.__dict__.copy()
 72        for key, value in ret.items():
 73            if key not in attrs:
 74                assert type(value) is dict, (
 75                    "%r is not dict. please update %s.__getstate__" %
 76                    (key, self.__class__.__name__))
 77                ret[key] = {}
 78        return ret
 79
 80    def _register_returnvar(self, flowgraph):
 81        if annmodel.DEBUG:
 82            self.return_bindings[flowgraph.getreturnvar()] = flowgraph
 83
 84    #___ convenience high-level interface __________________
 85
 86    def build_types(self, function, input_arg_types, complete_now=True,
 87                    main_entry_point=False):
 88        """Recursively build annotations about the specific entry point."""
 89        assert isinstance(function, types.FunctionType), "fix that!"
 90
 91        from pypy.annotation.policy import AnnotatorPolicy
 92        policy = AnnotatorPolicy()
 93        # make input arguments and set their type
 94        args_s = [self.typeannotation(t) for t in input_arg_types]
 95
 96        # XXX hack
 97        annmodel.TLS.check_str_without_nul = (
 98            self.translator.config.translation.check_str_without_nul)
 99
100        flowgraph, inputcells = self.get_call_parameters(function, args_s, policy)
101        if not isinstance(flowgraph, FunctionGraph):
102            assert isinstance(flowgraph, annmodel.SomeObject)
103            return flowgraph
104
105        if main_entry_point:
106            self.translator.entry_point_graph = flowgraph
107        return self.build_graph_types(flowgraph, inputcells, complete_now=complete_now)
108
109    def get_call_parameters(self, function, args_s, policy):
110        desc = self.bookkeeper.getdesc(function)
111        args = self.bookkeeper.build_args("simple_call", args_s[:])
112        result = []
113        def schedule(graph, inputcells):
114            result.append((graph, inputcells))
115            return annmodel.s_ImpossibleValue
116
117        prevpolicy = self.policy
118        self.policy = policy
119        self.bookkeeper.enter(None)
120        try:
121            desc.pycall(schedule, args, annmodel.s_ImpossibleValue)
122        finally:
123            self.bookkeeper.leave()
124            self.policy = prevpolicy
125        [(graph, inputcells)] = result
126        return graph, inputcells
127
128    def annotate_helper(self, function, args_s, policy=None):
129        if policy is None:
130            from pypy.annotation.policy import AnnotatorPolicy
131            policy = AnnotatorPolicy()
132        graph, inputcells = self.get_call_parameters(function, args_s, policy)
133        self.build_graph_types(graph, inputcells, complete_now=False)
134        self.complete_helpers(policy)
135        return graph
136    
137    def annotate_helper_method(self, _class, attr, args_s, policy=None):
138        """ Warning! this method is meant to be used between
139        annotation and rtyping
140        """
141        if policy is None:
142            from pypy.annotation.policy import AnnotatorPolicy
143            policy = AnnotatorPolicy()
144        
145        assert attr != '__class__'
146        classdef = self.bookkeeper.getuniqueclassdef(_class)
147        attrdef = classdef.find_attribute(attr)
148        s_result = attrdef.getvalue()
149        classdef.add_source_for_attribute(attr, classdef.classdesc)
150        self.bookkeeper
151        assert isinstance(s_result, annmodel.SomePBC)
152        olddesc = s_result.any_description()
153        desc = olddesc.bind_self(classdef)
154        args = self.bookkeeper.build_args("simple_call", args_s[:])
155        desc.consider_call_site(self.bookkeeper, desc.getcallfamily(), [desc],
156            args, annmodel.s_ImpossibleValue, None)
157        result = []
158        def schedule(graph, inputcells):
159            result.append((graph, inputcells))
160            return annmodel.s_ImpossibleValue
161
162        prevpolicy = self.policy
163        self.policy = policy
164        self.bookkeeper.enter(None)
165        try:
166            desc.pycall(schedule, args, annmodel.s_ImpossibleValue)
167        finally:
168            self.bookkeeper.leave()
169            self.policy = prevpolicy
170        [(graph, inputcells)] = result
171        self.build_graph_types(graph, inputcells, complete_now=False)
172        self.complete_helpers(policy)
173        return graph
174
175    def complete_helpers(self, policy):
176        saved = self.policy, self.added_blocks
177        self.policy = policy
178        try:
179            self.added_blocks = {}
180            self.complete()
181            # invoke annotation simplifications for the new blocks
182            self.simplify(block_subset=self.added_blocks)
183        finally:
184            self.policy, self.added_blocks = saved
185
186    def build_graph_types(self, flowgraph, inputcells, complete_now=True):
187        checkgraph(flowgraph)
188
189        nbarg = len(flowgraph.getargs())
190        if len(inputcells) != nbarg: 
191            raise TypeError("%s expects %d args, got %d" %(       
192                            flowgraph, nbarg, len(inputcells)))
193        
194        # register the entry point
195        self.addpendinggraph(flowgraph, inputcells)
196        # recursively proceed until no more pending block is left
197        if complete_now:
198            self.complete()
199        return self.binding(flowgraph.getreturnvar(), None)
200
201    def gettype(self, variable):
202        """Return the known type of a control flow graph variable,
203        defaulting to 'object'."""
204        if isinstance(variable, Constant):
205            return type(variable.value)
206        elif isinstance(variable, Variable):
207            cell = self.bindings.get(variable)
208            if cell:
209                return cell.knowntype
210            else:
211                return object
212        else:
213            raise TypeError, ("Variable or Constant instance expected, "
214                              "got %r" % (variable,))
215
216    def getuserclassdefinitions(self):
217        """Return a list of ClassDefs."""
218        return self.bookkeeper.classdefs
219
220    #___ medium-level interface ____________________________
221
222    def addpendinggraph(self, flowgraph, inputcells):
223        self._register_returnvar(flowgraph)
224        self.addpendingblock(flowgraph, flowgraph.startblock, inputcells)
225
226    def addpendingblock(self, graph, block, cells, called_from_graph=None):
227        """Register an entry point into block with the given input cells."""
228        if graph in self.fixed_graphs:
229            # special case for annotating/rtyping in several phases: calling
230            # a graph that has already been rtyped.  Safety-check the new
231            # annotations that are passed in, and don't annotate the old
232            # graph -- it's already low-level operations!
233            for a, s_newarg in zip(graph.getargs(), cells):
234                s_oldarg = self.binding(a)
235                assert annmodel.unionof(s_oldarg, s_newarg) == s_oldarg
236        else:
237            assert not self.frozen
238            for a in cells:
239                assert isinstance(a, annmodel.SomeObject)
240            if block not in self.annotated:
241                self.bindinputargs(graph, block, cells, called_from_graph)
242            else:
243                self.mergeinputargs(graph, block, cells, called_from_graph)
244            if not self.annotated[block]:
245                self.pendingblocks[block] = graph
246
247    def complete(self):
248        """Process pending blocks until none is left."""
249        while True:
250            while self.pendingblocks:
251                block, graph = self.pendingblocks.popitem()
252                if annmodel.DEBUG:
253                    self.flowin_block = block # we need to keep track of block
254                self.processblock(graph, block)
255            self.policy.no_more_blocks_to_annotate(self)
256            if not self.pendingblocks:
257                break   # finished
258        # make sure that the return variables of all graphs is annotated
259        if self.added_blocks is not None:
260            newgraphs = [self.annotated[block] for block in self.added_blocks]
261            newgraphs = dict.fromkeys(newgraphs)
262            got_blocked_blocks = False in newgraphs
263        else:
264            newgraphs = self.translator.graphs  #all of them
265            got_blocked_blocks = False in self.annotated.values()
266        if got_blocked_blocks:
267            for graph in self.blocked_graphs.values():
268                self.blocked_graphs[graph] = True
269
270            blocked_blocks = [block for block, done in self.annotated.items()
271                                    if done is False]
272            assert len(blocked_blocks) == len(self.blocked_blocks)
273
274            text = format_blocked_annotation_error(self, self.blocked_blocks)
275            #raise SystemExit()
276            raise AnnotatorError(text)
277        for graph in newgraphs:
278            v = graph.getreturnvar()
279            if v not in self.bindings:
280                self.setbinding(v, annmodel.s_ImpossibleValue)
281        # policy-dependent computation
282        self.bookkeeper.compute_at_fixpoint()
283
284    def binding(self, arg, default=FAIL):
285        "Gives the SomeValue corresponding to the given Variable or Constant."
286        if isinstance(arg, Variable):
287            try:
288                return self.bindings[arg]
289            except KeyError:
290                if default is not FAIL:
291                    return default
292                else:
293                    raise
294        elif isinstance(arg, Constant):
295            #if arg.value is undefined_value:   # undefined local variables
296            #    return annmodel.s_ImpossibleValue
297            return self.bookkeeper.immutableconstant(arg)
298        else:
299            raise TypeError, 'Variable or Constant expected, got %r' % (arg,)
300
301    def typeannotation(self, t):
302        return signature.annotation(t, self.bookkeeper)
303
304    def ondegenerated(self, what, s_value, where=None, called_from_graph=None):
305        if self.policy.allow_someobjects:
306            return
307        # is the function itself tagged with allow_someobjects?
308        position_key = where or getattr(self.bookkeeper, 'position_key', None)
309        if position_key is not None:
310            graph, block, i = position_key
311            try:
312                if graph.func.allow_someobjects:
313                    return
314            except AttributeError:
315                pass
316
317        msgstr = format_someobject_error(self, position_key, what, s_value,
318                                         called_from_graph,
319                                         self.bindings.get(what, "(none)"))
320
321        raise AnnotatorError(msgstr)
322
323    def setbinding(self, arg, s_value, called_from_graph=None, where=None):
324        if arg in self.bindings:
325            assert s_value.contains(self.bindings[arg])
326            # for debugging purposes, record the history of bindings that
327            # have been given to this variable
328            if annmodel.DEBUG:
329                history = self.bindingshistory.setdefault(arg, [])
330                history.append(self.bindings[arg])
331                cause_history = self.binding_cause_history.setdefault(arg, [])
332                cause_history.append(self.binding_caused_by[arg])
333
334        degenerated = annmodel.isdegenerated(s_value)
335
336        if degenerated:
337            self.ondegenerated(arg, s_value, where=where,
338                               called_from_graph=called_from_graph)
339
340        self.bindings[arg] = s_value
341        if annmodel.DEBUG:
342            if arg in self.return_bindings:
343                log.event("%s -> %s" % 
344                    (self.whereami((self.return_bindings[arg], None, None)), 
345                     s_value)) 
346
347            if arg in self.return_bindings and degenerated:
348                self.warning("result degenerated to SomeObject",
349                             (self.return_bindings[arg],None, None))
350                
351            self.binding_caused_by[arg] = called_from_graph
352
353    def transfer_binding(self, v_target, v_source):
354        assert v_source in self.bindings
355        self.bindings[v_target] = self.bindings[v_source]
356        if annmodel.DEBUG:
357            self.binding_caused_by[v_target] = None
358
359    def warning(self, msg, pos=None):
360        if pos is None:
361            try:
362                pos = self.bookkeeper.position_key
363            except AttributeError:
364                pos = '?'
365        if pos != '?':
366            pos = self.whereami(pos)
367 
368        log.WARNING("%s/ %s" % (pos, msg))
369
370
371    #___ interface for annotator.bookkeeper _______
372
373    def recursivecall(self, graph, whence, inputcells): # whence = position_key|callback taking the annotator, graph 
374        if isinstance(whence, tuple):
375            parent_graph, parent_block, parent_index = position_key = whence
376            tag = parent_block, parent_index
377            self.translator.update_call_graph(parent_graph, graph, tag)
378        else:
379            position_key = None
380        self._register_returnvar(graph)
381        # self.notify[graph.returnblock] is a dictionary of call
382        # points to this func which triggers a reflow whenever the
383        # return block of this graph has been analysed.
384        callpositions = self.notify.setdefault(graph.returnblock, {})
385        if whence is not None:
386            if callable(whence):
387                def callback():
388                    whence(self, graph)
389            else:
390                callback = whence
391            callpositions[callback] = True
392
393        # generalize the function's input arguments
394        self.addpendingblock(graph, graph.startblock, inputcells,
395                             position_key)
396
397        # get the (current) return value
398        v = graph.getreturnvar()
399        try:
400            return self.bindings[v]
401        except KeyError: 
402            # the function didn't reach any return statement so far.
403            # (some functions actually never do, they always raise exceptions)
404            return annmodel.s_ImpossibleValue
405
406    def reflowfromposition(self, position_key):
407        graph, block, index = position_key
408        self.reflowpendingblock(graph, block)
409
410
411    #___ simplification (should be moved elsewhere?) _______
412
413    def simplify(self, block_subset=None, extra_passes=None):
414        # Generic simplifications
415        transform.transform_graph(self, block_subset=block_subset,
416                                  extra_passes=extra_passes)
417        if block_subset is None:
418            graphs = self.translator.graphs
419        else:
420            graphs = {}
421            for block in block_subset:
422                graph = self.annotated.get(block)
423                if graph:
424                    graphs[graph] = True
425        for graph in graphs:
426            simplify.eliminate_empty_blocks(graph)
427
428
429    #___ flowing annotations in blocks _____________________
430
431    def processblock(self, graph, block):
432        # Important: this is not called recursively.
433        # self.flowin() can only issue calls to self.addpendingblock().
434        # The analysis of a block can be in three states:
435        #  * block not in self.annotated:
436        #      never seen the block.
437        #  * self.annotated[block] == False:
438        #      the input variables of the block are in self.bindings but we
439        #      still have to consider all the operations in the block.
440        #  * self.annotated[block] == graph-containing-block:
441        #      analysis done (at least until we find we must generalize the
442        #      input variables).
443
444        #print '* processblock', block, cells
445        if annmodel.DEBUG:
446            self.reflowcounter.setdefault(block, 0)
447            self.reflowcounter[block] += 1
448        self.annotated[block] = graph
449        if block in self.blocked_blocks:
450            del self.blocked_blocks[block]
451        try:
452            self.flowin(graph, block)
453        except BlockedInference, e:
454            self.annotated[block] = False   # failed, hopefully temporarily
455            self.blocked_blocks[block] = graph
456        except Exception, e:
457            # hack for debug tools only
458            if not hasattr(e, '__annotator_block'):
459                setattr(e, '__annotator_block', block)
460            raise
461
462        # The dict 'added_blocks' is used by rpython.annlowlevel to
463        # detect which are the new blocks that annotating an additional
464        # small helper creates.
465        if self.added_blocks is not None:
466            self.added_blocks[block] = True
467
468    def reflowpendingblock(self, graph, block):
469        assert not self.frozen
470        assert graph not in self.fixed_graphs
471        self.pendingblocks[block] = graph
472        assert block in self.annotated
473        self.annotated[block] = False  # must re-flow
474        self.blocked_blocks[block] = graph
475
476    def bindinputargs(self, graph, block, inputcells, called_from_graph=None):
477        # Create the initial bindings for the input args of a block.
478        assert len(block.inputargs) == len(inputcells)
479        where = (graph, block, None)
480        for a, cell in zip(block.inputargs, inputcells):
481            self.setbinding(a, cell, called_from_graph, where=where)
482        self.annotated[block] = False  # must flowin.
483        self.blocked_blocks[block] = graph
484
485    def mergeinputargs(self, graph, block, inputcells, called_from_graph=None):
486        # Merge the new 'cells' with each of the block's existing input
487        # variables.
488        oldcells = [self.binding(a) for a in block.inputargs]
489        unions = [annmodel.unionof(c1,c2) for c1, c2 in zip(oldcells,inputcells)]
490        # if the merged cells changed, we must redo the analysis
491        if unions != oldcells:
492            self.bindinputargs(graph, block, unions, called_from_graph)
493
494    def whereami(self, position_key):
495        graph, block, i = position_key
496        blk = ""
497        if block:
498            at = block.at()
499            if at:
500                blk = " block"+at
501        opid=""
502        if i is not None:
503            opid = " op=%d" % i
504        return repr(graph) + blk + opid
505
506    def flowin(self, graph, block):
507        #print 'Flowing', block, [self.binding(a) for a in block.inputargs]
508        try:
509            for i in range(len(block.operations)):
510                try:
511                    self.bookkeeper.enter((graph, block, i))
512                    self.consider_op(block.operations[i])
513                finally:
514                    self.bookkeeper.leave()
515
516        except BlockedInference, e:
517            if annmodel.DEBUG:
518                self.why_not_annotated[block] = sys.exc_info()
519
520            if (e.op is block.operations[-1] and
521                block.exitswitch == c_last_exception):
522                # this is the case where the last operation of the block will
523                # always raise an exception which is immediately caught by
524                # an exception handler.  We then only follow the exceptional
525                # branches.
526                exits = [link for link in block.exits
527                              if link.exitcase is not None]
528
529            elif e.op.opname in ('simple_call', 'call_args', 'next'):
530                # XXX warning, keep the name of the call operations in sync
531                # with the flow object space.  These are the operations for
532                # which it is fine to always raise an exception.  We then
533                # swallow the BlockedInference and that's it.
534                # About 'next': see test_annotate_iter_empty_container().
535                return
536
537            else:
538                # other cases are problematic (but will hopefully be solved
539                # later by reflowing).  Throw the BlockedInference up to
540                # processblock().
541                raise
542
543        except annmodel.HarmlesslyBlocked:
544            return
545
546        else:
547            # dead code removal: don't follow all exits if the exitswitch
548            # is known
549            exits = block.exits
550            if isinstance(block.exitswitch, Variable):
551                s_exitswitch = self.bindings[block.exitswitch]
552                if s_exitswitch.is_constant():
553                    exits = [link for link in exits
554                                  if link.exitcase == s_exitswitch.const]
555
556        # mapping (exitcase, variable) -> s_annotation
557        # that can be attached to booleans, exitswitches
558        knowntypedata = getattr(self.bindings.get(block.exitswitch),
559                                "knowntypedata", {})
560
561        # filter out those exceptions which cannot
562        # occour for this specific, typed operation.
563        if block.exitswitch == c_last_exception:
564            op = block.operations[-1]
565            if op.opname in annmodel.BINARY_OPERATIONS:
566                arg1 = self.binding(op.args[0])
567                arg2 = self.binding(op.args[1])
568                binop = getattr(pair(arg1, arg2), op.opname, None)
569                can_only_throw = annmodel.read_can_only_throw(binop, arg1, arg2)
570            elif op.opname in annmodel.UNARY_OPERATIONS:
571                arg1 = self.binding(op.args[0])
572                opname = op.opname
573                if opname == 'contains': opname = 'op_contains'
574                unop = getattr(arg1, opname, None)
575                can_only_throw = annmodel.read_can_only_throw(unop, arg1)
576            else:
577                can_only_throw = None
578
579            if can_only_throw is not None:
580                candidates = can_only_throw
581                candidate_exits = exits
582                exits = []
583                for link in candidate_exits:
584                    case = link.exitcase
585                    if case is None:
586                        exits.append(link)
587                        continue
588                    covered = [c for c in candidates if issubclass(c, case)]
589                    if covered:
590                        exits.append(link)
591                        candidates = [c for c in candidates if c not in covered]
592
593        for link in exits:
594            in_except_block = False
595
596            last_exception_var = link.last_exception # may be None for non-exception link
597            last_exc_value_var = link.last_exc_value # may be None for non-exception link
598
599            if isinstance(link.exitcase, (types.ClassType, type)) \
600                   and issubclass(link.exitcase, py.builtin.BaseException):
601                assert last_exception_var and last_exc_value_var
602                last_exc_value_object = self.bookkeeper.valueoftype(link.exitcase)
603                last_exception_object = annmodel.SomeObject()
604                last_exception_object.knowntype = type
605                if isinstance(last_exception_var, Constant):
606                    last_exception_object.const = last_exception_var.value
607                last_exception_object.is_type_of = [last_exc_value_var]
608
609                if isinstance(last_exception_var, Variable):
610                    self.setbinding(last_exception_var, last_exception_object)
611                if isinstance(last_exc_value_var, Variable):
612                    self.setbinding(last_exc_value_var, last_exc_value_object)
613
614                last_exception_object = annmodel.SomeObject()
615                last_exception_object.knowntype = type
616                if isinstance(last_exception_var, Constant):
617                    last_exception_object.const = last_exception_var.value
618                #if link.exitcase is Exception:
619                #    last_exc_value_object = annmodel.SomeObject()
620                #else:
621                last_exc_value_vars = []
622                in_except_block = True
623
624            ignore_link = False
625            cells = []
626            renaming = {}
627            for a,v in zip(link.args,link.target.inputargs):
628                renaming.setdefault(a, []).append(v)
629            for a,v in zip(link.args,link.target.inputargs):
630                if a == last_exception_var:
631                    assert in_except_block
632                    cells.append(last_exception_object)
633                elif a == last_exc_value_var:
634                    assert in_except_block
635                    cells.append(last_exc_value_object)
636                    last_exc_value_vars.append(v)
637                else:
638                    cell = self.binding(a)
639                    if (link.exitcase, a) in knowntypedata:
640                        knownvarvalue = knowntypedata[(link.exitcase, a)]
641                        cell = pair(cell, knownvarvalue).improve()
642                        # ignore links that try to pass impossible values
643                        if cell == annmodel.s_ImpossibleValue:
644                            ignore_link = True
645
646                    if hasattr(cell,'is_type_of'):
647                        renamed_is_type_of = []
648                        for v in cell.is_type_of:
649                            new_vs = renaming.get(v,[])
650                            renamed_is_type_of += new_vs
651                        newcell = annmodel.SomeObject()
652                        if cell.knowntype == type:
653                            newcell.knowntype = type
654                        if cell.is_constant():
655                            newcell.const = cell.const
656                        cell = newcell
657                        cell.is_type_of = renamed_is_type_of
658
659                    if hasattr(cell, 'knowntypedata'):
660                        renamed_knowntypedata = {}
661                        for (value, v), s in cell.knowntypedata.items():
662                            new_vs = renaming.get(v, [])
663                            for new_v in new_vs:
664                                renamed_knowntypedata[value, new_v] = s
665                        assert isinstance(cell, annmodel.SomeBool)
666                        newcell = annmodel.SomeBool()
667                        if cell.is_constant():
668                            newcell.const = cell.const
669                        cell = newcell
670                        cell.knowntypedata = renamed_knowntypedata
671
672                    cells.append(cell)
673
674            if ignore_link:
675                continue
676
677            if in_except_block:
678                last_exception_object.is_type_of = last_exc_value_vars
679
680            self.links_followed[link] = True
681            self.addpendingblock(graph, link.target, cells)
682
683        if block in self.notify:
684            # reflow from certain positions when this block is done
685            for callback in self.notify[block]:
686                if isinstance(callback, tuple):
687                    self.reflowfromposition(callback) # callback is a position
688                else:
689                    callback()
690
691
692    #___ creating the annotations based on operations ______
693
694    def consider_op(self, op):
695        argcells = [self.binding(a) for a in op.args]
696        consider_meth = getattr(self,'consider_op_'+op.opname,
697                                None)
698        if not consider_meth:
699            raise Exception,"unknown op: %r" % op
700
701        # let's be careful about avoiding propagated SomeImpossibleValues
702        # to enter an op; the latter can result in violations of the
703        # more general results invariant: e.g. if SomeImpossibleValue enters is_
704        #  is_(SomeImpossibleValue, None) -> SomeBool
705        #  is_(SomeInstance(not None), None) -> SomeBool(const=False) ...
706        # boom -- in the assert of setbinding()
707        for arg in argcells:
708            if isinstance(arg, annmodel.SomeImpossibleValue):
709                raise BlockedInference(self, op)
710        try:
711            resultcell = consider_meth(*argcells)
712        except Exception:
713            graph = self.bookkeeper.position_key[0]
714            raise_nicer_exception(op, str(graph))
715        if resultcell is None:
716            resultcell = self.noreturnvalue(op)
717        elif resultcell == annmodel.s_ImpossibleValue:
718            raise BlockedInference(self, op) # the operation cannot succeed
719        assert isinstance(resultcell, annmodel.SomeObject)
720        assert isinstance(op.result, Variable)
721        self.setbinding(op.result, resultcell)  # bind resultcell to op.result
722
723    def noreturnvalue(self, op):
724        return annmodel.s_ImpossibleValue  # no return value (hook method)
725
726    # XXX "contains" clash with SomeObject method
727    def consider_op_contains(self, seq, elem):
728        self.bookkeeper.count("contains", seq)
729        return seq.op_contains(elem)
730
731    def consider_op_newtuple(self, *args):
732        return annmodel.SomeTuple(items = args)
733
734    def consider_op_newlist(self, *args):
735        return self.bookkeeper.newlist(*args)
736
737    def consider_op_newdict(self):
738        return self.bookkeeper.newdict()
739
740
741    def _registeroperations(cls, model):
742        # All unary operations
743        d = {}
744        for opname in model.UNARY_OPERATIONS:
745            fnname = 'consider_op_' + opname
746            exec py.code.Source("""
747def consider_op_%s(self, arg, *args):
748    return arg.%s(*args)
749""" % (opname, opname)).compile() in globals(), d
750            setattr(cls, fnname, d[fnname])
751        # All binary operations
752        for opname in model.BINARY_OPERATIONS:
753            fnname = 'consider_op_' + opname
754            exec py.code.Source("""
755def consider_op_%s(self, arg1, arg2, *args):
756    return pair(arg1,arg2).%s(*args)
757""" % (opname, opname)).compile() in globals(), d
758            setattr(cls, fnname, d[fnname])
759    _registeroperations = classmethod(_registeroperations)
760
761# register simple operations handling
762RPythonAnnotator._registeroperations(annmodel)
763
764
765class BlockedInference(Exception):
766    """This exception signals the type inference engine that the situation
767    is currently blocked, and that it should try to progress elsewhere."""
768
769    def __init__(self, annotator, op):
770        self.annotator = annotator
771        try:
772            self.break_at = annotator.bookkeeper.position_key
773        except AttributeError:
774            self.break_at = None
775        self.op = op
776
777    def __repr__(self):
778        if not self.break_at:
779            break_at = "?"
780        else:
781            break_at = self.annotator.whereami(self.break_at)
782        return "<BlockedInference break_at %s [%s]>" %(break_at, self.op)
783
784    __str__ = __repr__