PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/annotation/annrpython.py

https://bitbucket.org/varialus/jyjy
Python | 746 lines | 728 code | 6 blank | 12 comment | 6 complexity | 24b7d63d84b722519a5069338a6a4214 MD5 | raw file
  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 complete_helpers(self, policy):
  121. saved = self.policy, self.added_blocks
  122. self.policy = policy
  123. try:
  124. self.added_blocks = {}
  125. self.complete()
  126. # invoke annotation simplifications for the new blocks
  127. self.simplify(block_subset=self.added_blocks)
  128. finally:
  129. self.policy, self.added_blocks = saved
  130. def build_graph_types(self, flowgraph, inputcells, complete_now=True):
  131. checkgraph(flowgraph)
  132. nbarg = len(flowgraph.getargs())
  133. if len(inputcells) != nbarg:
  134. raise TypeError("%s expects %d args, got %d" %(
  135. flowgraph, nbarg, len(inputcells)))
  136. # register the entry point
  137. self.addpendinggraph(flowgraph, inputcells)
  138. # recursively proceed until no more pending block is left
  139. if complete_now:
  140. self.complete()
  141. return self.binding(flowgraph.getreturnvar(), None)
  142. def gettype(self, variable):
  143. """Return the known type of a control flow graph variable,
  144. defaulting to 'object'."""
  145. if isinstance(variable, Constant):
  146. return type(variable.value)
  147. elif isinstance(variable, Variable):
  148. cell = self.bindings.get(variable)
  149. if cell:
  150. return cell.knowntype
  151. else:
  152. return object
  153. else:
  154. raise TypeError, ("Variable or Constant instance expected, "
  155. "got %r" % (variable,))
  156. def getuserclassdefinitions(self):
  157. """Return a list of ClassDefs."""
  158. return self.bookkeeper.classdefs
  159. #___ medium-level interface ____________________________
  160. def addpendinggraph(self, flowgraph, inputcells):
  161. self._register_returnvar(flowgraph)
  162. self.addpendingblock(flowgraph, flowgraph.startblock, inputcells)
  163. def addpendingblock(self, graph, block, cells, called_from_graph=None):
  164. """Register an entry point into block with the given input cells."""
  165. if graph in self.fixed_graphs:
  166. # special case for annotating/rtyping in several phases: calling
  167. # a graph that has already been rtyped. Safety-check the new
  168. # annotations that are passed in, and don't annotate the old
  169. # graph -- it's already low-level operations!
  170. for a, s_newarg in zip(graph.getargs(), cells):
  171. s_oldarg = self.binding(a)
  172. assert annmodel.unionof(s_oldarg, s_newarg) == s_oldarg
  173. else:
  174. assert not self.frozen
  175. for a in cells:
  176. assert isinstance(a, annmodel.SomeObject)
  177. if block not in self.annotated:
  178. self.bindinputargs(graph, block, cells, called_from_graph)
  179. else:
  180. self.mergeinputargs(graph, block, cells, called_from_graph)
  181. if not self.annotated[block]:
  182. self.pendingblocks[block] = graph
  183. def complete(self):
  184. """Process pending blocks until none is left."""
  185. while True:
  186. while self.pendingblocks:
  187. block, graph = self.pendingblocks.popitem()
  188. if annmodel.DEBUG:
  189. self.flowin_block = block # we need to keep track of block
  190. self.processblock(graph, block)
  191. self.policy.no_more_blocks_to_annotate(self)
  192. if not self.pendingblocks:
  193. break # finished
  194. # make sure that the return variables of all graphs is annotated
  195. if self.added_blocks is not None:
  196. newgraphs = [self.annotated[block] for block in self.added_blocks]
  197. newgraphs = dict.fromkeys(newgraphs)
  198. got_blocked_blocks = False in newgraphs
  199. else:
  200. newgraphs = self.translator.graphs #all of them
  201. got_blocked_blocks = False in self.annotated.values()
  202. if got_blocked_blocks:
  203. for graph in self.blocked_graphs.values():
  204. self.blocked_graphs[graph] = True
  205. blocked_blocks = [block for block, done in self.annotated.items()
  206. if done is False]
  207. assert len(blocked_blocks) == len(self.blocked_blocks)
  208. text = format_blocked_annotation_error(self, self.blocked_blocks)
  209. #raise SystemExit()
  210. raise AnnotatorError(text)
  211. for graph in newgraphs:
  212. v = graph.getreturnvar()
  213. if v not in self.bindings:
  214. self.setbinding(v, annmodel.s_ImpossibleValue)
  215. # policy-dependent computation
  216. self.bookkeeper.compute_at_fixpoint()
  217. def binding(self, arg, default=FAIL):
  218. "Gives the SomeValue corresponding to the given Variable or Constant."
  219. if isinstance(arg, Variable):
  220. try:
  221. return self.bindings[arg]
  222. except KeyError:
  223. if default is not FAIL:
  224. return default
  225. else:
  226. raise
  227. elif isinstance(arg, Constant):
  228. #if arg.value is undefined_value: # undefined local variables
  229. # return annmodel.s_ImpossibleValue
  230. return self.bookkeeper.immutableconstant(arg)
  231. else:
  232. raise TypeError, 'Variable or Constant expected, got %r' % (arg,)
  233. def typeannotation(self, t):
  234. return signature.annotation(t, self.bookkeeper)
  235. def ondegenerated(self, what, s_value, where=None, called_from_graph=None):
  236. if self.policy.allow_someobjects:
  237. return
  238. # is the function itself tagged with allow_someobjects?
  239. position_key = where or getattr(self.bookkeeper, 'position_key', None)
  240. if position_key is not None:
  241. graph, block, i = position_key
  242. try:
  243. if graph.func.allow_someobjects:
  244. return
  245. except AttributeError:
  246. pass
  247. msgstr = format_someobject_error(self, position_key, what, s_value,
  248. called_from_graph,
  249. self.bindings.get(what, "(none)"))
  250. raise AnnotatorError(msgstr)
  251. def setbinding(self, arg, s_value, called_from_graph=None, where=None):
  252. if arg in self.bindings:
  253. assert s_value.contains(self.bindings[arg])
  254. # for debugging purposes, record the history of bindings that
  255. # have been given to this variable
  256. if annmodel.DEBUG:
  257. history = self.bindingshistory.setdefault(arg, [])
  258. history.append(self.bindings[arg])
  259. cause_history = self.binding_cause_history.setdefault(arg, [])
  260. cause_history.append(self.binding_caused_by[arg])
  261. degenerated = annmodel.isdegenerated(s_value)
  262. if degenerated:
  263. self.ondegenerated(arg, s_value, where=where,
  264. called_from_graph=called_from_graph)
  265. self.bindings[arg] = s_value
  266. if annmodel.DEBUG:
  267. if arg in self.return_bindings:
  268. log.event("%s -> %s" %
  269. (self.whereami((self.return_bindings[arg], None, None)),
  270. s_value))
  271. if arg in self.return_bindings and degenerated:
  272. self.warning("result degenerated to SomeObject",
  273. (self.return_bindings[arg],None, None))
  274. self.binding_caused_by[arg] = called_from_graph
  275. def transfer_binding(self, v_target, v_source):
  276. assert v_source in self.bindings
  277. self.bindings[v_target] = self.bindings[v_source]
  278. if annmodel.DEBUG:
  279. self.binding_caused_by[v_target] = None
  280. def warning(self, msg, pos=None):
  281. if pos is None:
  282. try:
  283. pos = self.bookkeeper.position_key
  284. except AttributeError:
  285. pos = '?'
  286. if pos != '?':
  287. pos = self.whereami(pos)
  288. log.WARNING("%s/ %s" % (pos, msg))
  289. #___ interface for annotator.bookkeeper _______
  290. def recursivecall(self, graph, whence, inputcells): # whence = position_key|callback taking the annotator, graph
  291. if isinstance(whence, tuple):
  292. parent_graph, parent_block, parent_index = position_key = whence
  293. tag = parent_block, parent_index
  294. self.translator.update_call_graph(parent_graph, graph, tag)
  295. else:
  296. position_key = None
  297. self._register_returnvar(graph)
  298. # self.notify[graph.returnblock] is a dictionary of call
  299. # points to this func which triggers a reflow whenever the
  300. # return block of this graph has been analysed.
  301. callpositions = self.notify.setdefault(graph.returnblock, {})
  302. if whence is not None:
  303. if callable(whence):
  304. def callback():
  305. whence(self, graph)
  306. else:
  307. callback = whence
  308. callpositions[callback] = True
  309. # generalize the function's input arguments
  310. self.addpendingblock(graph, graph.startblock, inputcells,
  311. position_key)
  312. # get the (current) return value
  313. v = graph.getreturnvar()
  314. try:
  315. return self.bindings[v]
  316. except KeyError:
  317. # the function didn't reach any return statement so far.
  318. # (some functions actually never do, they always raise exceptions)
  319. return annmodel.s_ImpossibleValue
  320. def reflowfromposition(self, position_key):
  321. graph, block, index = position_key
  322. self.reflowpendingblock(graph, block)
  323. #___ simplification (should be moved elsewhere?) _______
  324. def simplify(self, block_subset=None, extra_passes=None):
  325. # Generic simplifications
  326. transform.transform_graph(self, block_subset=block_subset,
  327. extra_passes=extra_passes)
  328. if block_subset is None:
  329. graphs = self.translator.graphs
  330. else:
  331. graphs = {}
  332. for block in block_subset:
  333. graph = self.annotated.get(block)
  334. if graph:
  335. graphs[graph] = True
  336. for graph in graphs:
  337. simplify.eliminate_empty_blocks(graph)
  338. #___ flowing annotations in blocks _____________________
  339. def processblock(self, graph, block):
  340. # Important: this is not called recursively.
  341. # self.flowin() can only issue calls to self.addpendingblock().
  342. # The analysis of a block can be in three states:
  343. # * block not in self.annotated:
  344. # never seen the block.
  345. # * self.annotated[block] == False:
  346. # the input variables of the block are in self.bindings but we
  347. # still have to consider all the operations in the block.
  348. # * self.annotated[block] == graph-containing-block:
  349. # analysis done (at least until we find we must generalize the
  350. # input variables).
  351. #print '* processblock', block, cells
  352. if annmodel.DEBUG:
  353. self.reflowcounter.setdefault(block, 0)
  354. self.reflowcounter[block] += 1
  355. self.annotated[block] = graph
  356. if block in self.blocked_blocks:
  357. del self.blocked_blocks[block]
  358. try:
  359. self.flowin(graph, block)
  360. except BlockedInference, e:
  361. self.annotated[block] = False # failed, hopefully temporarily
  362. self.blocked_blocks[block] = graph
  363. except Exception, e:
  364. # hack for debug tools only
  365. if not hasattr(e, '__annotator_block'):
  366. setattr(e, '__annotator_block', block)
  367. raise
  368. # The dict 'added_blocks' is used by rpython.annlowlevel to
  369. # detect which are the new blocks that annotating an additional
  370. # small helper creates.
  371. if self.added_blocks is not None:
  372. self.added_blocks[block] = True
  373. def reflowpendingblock(self, graph, block):
  374. assert not self.frozen
  375. assert graph not in self.fixed_graphs
  376. self.pendingblocks[block] = graph
  377. assert block in self.annotated
  378. self.annotated[block] = False # must re-flow
  379. self.blocked_blocks[block] = graph
  380. def bindinputargs(self, graph, block, inputcells, called_from_graph=None):
  381. # Create the initial bindings for the input args of a block.
  382. assert len(block.inputargs) == len(inputcells)
  383. where = (graph, block, None)
  384. for a, cell in zip(block.inputargs, inputcells):
  385. self.setbinding(a, cell, called_from_graph, where=where)
  386. self.annotated[block] = False # must flowin.
  387. self.blocked_blocks[block] = graph
  388. def mergeinputargs(self, graph, block, inputcells, called_from_graph=None):
  389. # Merge the new 'cells' with each of the block's existing input
  390. # variables.
  391. oldcells = [self.binding(a) for a in block.inputargs]
  392. unions = [annmodel.unionof(c1,c2) for c1, c2 in zip(oldcells,inputcells)]
  393. # if the merged cells changed, we must redo the analysis
  394. if unions != oldcells:
  395. self.bindinputargs(graph, block, unions, called_from_graph)
  396. def whereami(self, position_key):
  397. graph, block, i = position_key
  398. blk = ""
  399. if block:
  400. at = block.at()
  401. if at:
  402. blk = " block"+at
  403. opid=""
  404. if i is not None:
  405. opid = " op=%d" % i
  406. return repr(graph) + blk + opid
  407. def flowin(self, graph, block):
  408. #print 'Flowing', block, [self.binding(a) for a in block.inputargs]
  409. try:
  410. for i in range(len(block.operations)):
  411. try:
  412. self.bookkeeper.enter((graph, block, i))
  413. self.consider_op(block.operations[i])
  414. finally:
  415. self.bookkeeper.leave()
  416. except BlockedInference, e:
  417. if annmodel.DEBUG:
  418. self.why_not_annotated[block] = sys.exc_info()
  419. if (e.op is block.operations[-1] and
  420. block.exitswitch == c_last_exception):
  421. # this is the case where the last operation of the block will
  422. # always raise an exception which is immediately caught by
  423. # an exception handler. We then only follow the exceptional
  424. # branches.
  425. exits = [link for link in block.exits
  426. if link.exitcase is not None]
  427. elif e.op.opname in ('simple_call', 'call_args', 'next'):
  428. # XXX warning, keep the name of the call operations in sync
  429. # with the flow object space. These are the operations for
  430. # which it is fine to always raise an exception. We then
  431. # swallow the BlockedInference and that's it.
  432. # About 'next': see test_annotate_iter_empty_container().
  433. return
  434. else:
  435. # other cases are problematic (but will hopefully be solved
  436. # later by reflowing). Throw the BlockedInference up to
  437. # processblock().
  438. raise
  439. except annmodel.HarmlesslyBlocked:
  440. return
  441. else:
  442. # dead code removal: don't follow all exits if the exitswitch
  443. # is known
  444. exits = block.exits
  445. if isinstance(block.exitswitch, Variable):
  446. s_exitswitch = self.bindings[block.exitswitch]
  447. if s_exitswitch.is_constant():
  448. exits = [link for link in exits
  449. if link.exitcase == s_exitswitch.const]
  450. # mapping (exitcase, variable) -> s_annotation
  451. # that can be attached to booleans, exitswitches
  452. knowntypedata = getattr(self.bindings.get(block.exitswitch),
  453. "knowntypedata", {})
  454. # filter out those exceptions which cannot
  455. # occour for this specific, typed operation.
  456. if block.exitswitch == c_last_exception:
  457. op = block.operations[-1]
  458. if op.opname in annmodel.BINARY_OPERATIONS:
  459. arg1 = self.binding(op.args[0])
  460. arg2 = self.binding(op.args[1])
  461. binop = getattr(pair(arg1, arg2), op.opname, None)
  462. can_only_throw = annmodel.read_can_only_throw(binop, arg1, arg2)
  463. elif op.opname in annmodel.UNARY_OPERATIONS:
  464. arg1 = self.binding(op.args[0])
  465. opname = op.opname
  466. if opname == 'contains': opname = 'op_contains'
  467. unop = getattr(arg1, opname, None)
  468. can_only_throw = annmodel.read_can_only_throw(unop, arg1)
  469. else:
  470. can_only_throw = None
  471. if can_only_throw is not None:
  472. candidates = can_only_throw
  473. candidate_exits = exits
  474. exits = []
  475. for link in candidate_exits:
  476. case = link.exitcase
  477. if case is None:
  478. exits.append(link)
  479. continue
  480. covered = [c for c in candidates if issubclass(c, case)]
  481. if covered:
  482. exits.append(link)
  483. candidates = [c for c in candidates if c not in covered]
  484. for link in exits:
  485. in_except_block = False
  486. last_exception_var = link.last_exception # may be None for non-exception link
  487. last_exc_value_var = link.last_exc_value # may be None for non-exception link
  488. if isinstance(link.exitcase, (types.ClassType, type)) \
  489. and issubclass(link.exitcase, py.builtin.BaseException):
  490. assert last_exception_var and last_exc_value_var
  491. last_exc_value_object = self.bookkeeper.valueoftype(link.exitcase)
  492. last_exception_object = annmodel.SomeObject()
  493. last_exception_object.knowntype = type
  494. if isinstance(last_exception_var, Constant):
  495. last_exception_object.const = last_exception_var.value
  496. last_exception_object.is_type_of = [last_exc_value_var]
  497. if isinstance(last_exception_var, Variable):
  498. self.setbinding(last_exception_var, last_exception_object)
  499. if isinstance(last_exc_value_var, Variable):
  500. self.setbinding(last_exc_value_var, last_exc_value_object)
  501. last_exception_object = annmodel.SomeObject()
  502. last_exception_object.knowntype = type
  503. if isinstance(last_exception_var, Constant):
  504. last_exception_object.const = last_exception_var.value
  505. #if link.exitcase is Exception:
  506. # last_exc_value_object = annmodel.SomeObject()
  507. #else:
  508. last_exc_value_vars = []
  509. in_except_block = True
  510. ignore_link = False
  511. cells = []
  512. renaming = {}
  513. for a,v in zip(link.args,link.target.inputargs):
  514. renaming.setdefault(a, []).append(v)
  515. for a,v in zip(link.args,link.target.inputargs):
  516. if a == last_exception_var:
  517. assert in_except_block
  518. cells.append(last_exception_object)
  519. elif a == last_exc_value_var:
  520. assert in_except_block
  521. cells.append(last_exc_value_object)
  522. last_exc_value_vars.append(v)
  523. else:
  524. cell = self.binding(a)
  525. if (link.exitcase, a) in knowntypedata:
  526. knownvarvalue = knowntypedata[(link.exitcase, a)]
  527. cell = pair(cell, knownvarvalue).improve()
  528. # ignore links that try to pass impossible values
  529. if cell == annmodel.s_ImpossibleValue:
  530. ignore_link = True
  531. if hasattr(cell,'is_type_of'):
  532. renamed_is_type_of = []
  533. for v in cell.is_type_of:
  534. new_vs = renaming.get(v,[])
  535. renamed_is_type_of += new_vs
  536. newcell = annmodel.SomeObject()
  537. if cell.knowntype == type:
  538. newcell.knowntype = type
  539. if cell.is_constant():
  540. newcell.const = cell.const
  541. cell = newcell
  542. cell.is_type_of = renamed_is_type_of
  543. if hasattr(cell, 'knowntypedata'):
  544. renamed_knowntypedata = {}
  545. for (value, v), s in cell.knowntypedata.items():
  546. new_vs = renaming.get(v, [])
  547. for new_v in new_vs:
  548. renamed_knowntypedata[value, new_v] = s
  549. assert isinstance(cell, annmodel.SomeBool)
  550. newcell = annmodel.SomeBool()
  551. if cell.is_constant():
  552. newcell.const = cell.const
  553. cell = newcell
  554. cell.knowntypedata = renamed_knowntypedata
  555. cells.append(cell)
  556. if ignore_link:
  557. continue
  558. if in_except_block:
  559. last_exception_object.is_type_of = last_exc_value_vars
  560. self.links_followed[link] = True
  561. self.addpendingblock(graph, link.target, cells)
  562. if block in self.notify:
  563. # reflow from certain positions when this block is done
  564. for callback in self.notify[block]:
  565. if isinstance(callback, tuple):
  566. self.reflowfromposition(callback) # callback is a position
  567. else:
  568. callback()
  569. #___ creating the annotations based on operations ______
  570. def consider_op(self, op):
  571. argcells = [self.binding(a) for a in op.args]
  572. consider_meth = getattr(self,'consider_op_'+op.opname,
  573. None)
  574. if not consider_meth:
  575. raise Exception,"unknown op: %r" % op
  576. # let's be careful about avoiding propagated SomeImpossibleValues
  577. # to enter an op; the latter can result in violations of the
  578. # more general results invariant: e.g. if SomeImpossibleValue enters is_
  579. # is_(SomeImpossibleValue, None) -> SomeBool
  580. # is_(SomeInstance(not None), None) -> SomeBool(const=False) ...
  581. # boom -- in the assert of setbinding()
  582. for arg in argcells:
  583. if isinstance(arg, annmodel.SomeImpossibleValue):
  584. raise BlockedInference(self, op)
  585. try:
  586. resultcell = consider_meth(*argcells)
  587. except Exception:
  588. graph = self.bookkeeper.position_key[0]
  589. raise_nicer_exception(op, str(graph))
  590. if resultcell is None:
  591. resultcell = self.noreturnvalue(op)
  592. elif resultcell == annmodel.s_ImpossibleValue:
  593. raise BlockedInference(self, op) # the operation cannot succeed
  594. assert isinstance(resultcell, annmodel.SomeObject)
  595. assert isinstance(op.result, Variable)
  596. self.setbinding(op.result, resultcell) # bind resultcell to op.result
  597. def noreturnvalue(self, op):
  598. return annmodel.s_ImpossibleValue # no return value (hook method)
  599. # XXX "contains" clash with SomeObject method
  600. def consider_op_contains(self, seq, elem):
  601. self.bookkeeper.count("contains", seq)
  602. return seq.op_contains(elem)
  603. def consider_op_newtuple(self, *args):
  604. return annmodel.SomeTuple(items = args)
  605. def consider_op_newlist(self, *args):
  606. return self.bookkeeper.newlist(*args)
  607. def consider_op_newdict(self):
  608. return self.bookkeeper.newdict()
  609. def _registeroperations(cls, model):
  610. # All unary operations
  611. d = {}
  612. for opname in model.UNARY_OPERATIONS:
  613. fnname = 'consider_op_' + opname
  614. exec py.code.Source("""
  615. def consider_op_%s(self, arg, *args):
  616. return arg.%s(*args)
  617. """ % (opname, opname)).compile() in globals(), d
  618. setattr(cls, fnname, d[fnname])
  619. # All binary operations
  620. for opname in model.BINARY_OPERATIONS:
  621. fnname = 'consider_op_' + opname
  622. exec py.code.Source("""
  623. def consider_op_%s(self, arg1, arg2, *args):
  624. return pair(arg1,arg2).%s(*args)
  625. """ % (opname, opname)).compile() in globals(), d
  626. setattr(cls, fnname, d[fnname])
  627. _registeroperations = classmethod(_registeroperations)
  628. # register simple operations handling
  629. RPythonAnnotator._registeroperations(annmodel)
  630. class BlockedInference(Exception):
  631. """This exception signals the type inference engine that the situation
  632. is currently blocked, and that it should try to progress elsewhere."""
  633. def __init__(self, annotator, op):
  634. self.annotator = annotator
  635. try:
  636. self.break_at = annotator.bookkeeper.position_key
  637. except AttributeError:
  638. self.break_at = None
  639. self.op = op
  640. def __repr__(self):
  641. if not self.break_at:
  642. break_at = "?"
  643. else:
  644. break_at = self.annotator.whereami(self.break_at)
  645. return "<BlockedInference break_at %s [%s]>" %(break_at, self.op)
  646. __str__ = __repr__