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