~ubuntu-branches/ubuntu/karmic/pypy/karmic

« back to all changes in this revision

Viewing changes to pypy/annotation/annrpython.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexandre Fayolle
  • Date: 2007-04-13 09:33:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070413093309-yoojh4jcoocu2krz
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

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