PageRenderTime 74ms CodeModel.GetById 25ms RepoModel.GetById 5ms app.codeStats 0ms

/rpython/annotator/description.py

https://bitbucket.org/pypy/pypy/
Python | 638 lines | 634 code | 1 blank | 3 comment | 3 complexity | a37734bb741745fc163e256f42a96df6 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from __future__ import absolute_import
  2. import types
  3. from rpython.annotator.signature import (
  4. enforce_signature_args, enforce_signature_return, finish_type)
  5. from rpython.flowspace.model import FunctionGraph
  6. from rpython.flowspace.bytecode import cpython_code_signature
  7. from rpython.annotator.argument import rawshape, ArgErr, simple_args
  8. from rpython.tool.sourcetools import valid_identifier
  9. from rpython.tool.pairtype import extendabletype
  10. from rpython.annotator.model import AnnotatorError, s_ImpossibleValue
  11. class CallFamily(object):
  12. """A family of Desc objects that could be called from common call sites.
  13. The call families are conceptually a partition of all (callable) Desc
  14. objects, where the equivalence relation is the transitive closure of
  15. 'd1~d2 if d1 and d2 might be called at the same call site'.
  16. """
  17. normalized = False
  18. modified = True
  19. def __init__(self, desc):
  20. self.descs = {desc: True}
  21. self.calltables = {} # see calltable_lookup_row()
  22. self.total_calltable_size = 0
  23. def update(self, other):
  24. self.modified = True
  25. self.normalized = self.normalized or other.normalized
  26. self.descs.update(other.descs)
  27. for shape, table in other.calltables.items():
  28. for row in table:
  29. self.calltable_add_row(shape, row)
  30. absorb = update # UnionFind API
  31. def calltable_lookup_row(self, callshape, row):
  32. # this code looks up a table of which graph to
  33. # call at which call site. Each call site gets a row of graphs,
  34. # sharable with other call sites. Each column is a FunctionDesc.
  35. # There is one such table per "call shape".
  36. table = self.calltables.get(callshape, [])
  37. for i, existing_row in enumerate(table):
  38. if existing_row == row: # XXX maybe use a dict again here?
  39. return i
  40. raise LookupError
  41. def calltable_add_row(self, callshape, row):
  42. try:
  43. self.calltable_lookup_row(callshape, row)
  44. except LookupError:
  45. self.modified = True
  46. table = self.calltables.setdefault(callshape, [])
  47. table.append(row)
  48. self.total_calltable_size += 1
  49. def find_row(self, bookkeeper, descs, args, op):
  50. shape = rawshape(args)
  51. with bookkeeper.at_position(None):
  52. row = build_calltable_row(descs, args, op)
  53. index = self.calltable_lookup_row(shape, row)
  54. return shape, index
  55. def build_calltable_row(descs, args, op):
  56. # see comments in CallFamily
  57. row = {}
  58. for desc in descs:
  59. graph = desc.get_graph(args, op)
  60. assert isinstance(graph, FunctionGraph)
  61. row[desc.rowkey()] = graph
  62. return row
  63. class FrozenAttrFamily(object):
  64. """A family of FrozenDesc objects that have any common 'getattr' sites.
  65. The attr families are conceptually a partition of FrozenDesc objects,
  66. where the equivalence relation is the transitive closure of:
  67. d1~d2 if d1 and d2 might have some attribute read on them by the same
  68. getattr operation.
  69. """
  70. def __init__(self, desc):
  71. self.descs = {desc: True}
  72. self.read_locations = {} # set of position_keys
  73. self.attrs = {} # { attr: s_value }
  74. def update(self, other):
  75. self.descs.update(other.descs)
  76. self.read_locations.update(other.read_locations)
  77. self.attrs.update(other.attrs)
  78. absorb = update # UnionFind API
  79. def get_s_value(self, attrname):
  80. try:
  81. return self.attrs[attrname]
  82. except KeyError:
  83. return s_ImpossibleValue
  84. def set_s_value(self, attrname, s_value):
  85. self.attrs[attrname] = s_value
  86. class ClassAttrFamily(object):
  87. """A family of ClassDesc objects that have common 'getattr' sites for a
  88. given attribute name. The attr families are conceptually a partition
  89. of ClassDesc objects, where the equivalence relation is the transitive
  90. closure of: d1~d2 if d1 and d2 might have a common attribute 'attrname'
  91. read on them by the same getattr operation.
  92. The 'attrname' is not explicitly stored here, but is the key used
  93. in the dictionary bookkeeper.pbc_maximal_access_sets_map.
  94. """
  95. # The difference between ClassAttrFamily and FrozenAttrFamily is that
  96. # FrozenAttrFamily is the union for all attribute names, but
  97. # ClassAttrFamily is more precise: it is only about one attribut name.
  98. def __init__(self, desc):
  99. self.descs = {desc: True}
  100. self.read_locations = {} # set of position_keys
  101. self.s_value = s_ImpossibleValue # union of possible values
  102. def update(self, other):
  103. from rpython.annotator.model import unionof
  104. self.descs.update(other.descs)
  105. self.read_locations.update(other.read_locations)
  106. self.s_value = unionof(self.s_value, other.s_value)
  107. absorb = update # UnionFind API
  108. def get_s_value(self, attrname):
  109. return self.s_value
  110. def set_s_value(self, attrname, s_value):
  111. self.s_value = s_value
  112. # ____________________________________________________________
  113. class Desc(object):
  114. __metaclass__ = extendabletype
  115. def __init__(self, bookkeeper, pyobj=None):
  116. self.bookkeeper = bookkeeper
  117. # 'pyobj' is non-None if there is an associated underlying Python obj
  118. self.pyobj = pyobj
  119. def __repr__(self):
  120. pyobj = self.pyobj
  121. if pyobj is None:
  122. return object.__repr__(self)
  123. return '<%s for %r>' % (self.__class__.__name__, pyobj)
  124. def querycallfamily(self):
  125. """Retrieve the CallFamily object if there is one, otherwise
  126. return None."""
  127. call_families = self.bookkeeper.pbc_maximal_call_families
  128. try:
  129. return call_families[self]
  130. except KeyError:
  131. return None
  132. def getcallfamily(self):
  133. """Get the CallFamily object. Possibly creates one."""
  134. call_families = self.bookkeeper.pbc_maximal_call_families
  135. _, _, callfamily = call_families.find(self.rowkey())
  136. return callfamily
  137. def mergecallfamilies(self, *others):
  138. """Merge the call families of the given Descs into one."""
  139. if not others:
  140. return False
  141. call_families = self.bookkeeper.pbc_maximal_call_families
  142. changed, rep, callfamily = call_families.find(self.rowkey())
  143. for desc in others:
  144. changed1, rep, callfamily = call_families.union(rep, desc.rowkey())
  145. changed = changed or changed1
  146. return changed
  147. def queryattrfamily(self):
  148. # no attributes supported by default;
  149. # overriden in FrozenDesc and ClassDesc
  150. return None
  151. def bind_under(self, classdef, name):
  152. return self
  153. @staticmethod
  154. def simplify_desc_set(descs):
  155. pass
  156. class NoStandardGraph(Exception):
  157. """The function doesn't have a single standard non-specialized graph."""
  158. NODEFAULT = object()
  159. class FunctionDesc(Desc):
  160. knowntype = types.FunctionType
  161. def __init__(self, bookkeeper, pyobj=None,
  162. name=None, signature=None, defaults=None,
  163. specializer=None):
  164. super(FunctionDesc, self).__init__(bookkeeper, pyobj)
  165. if name is None:
  166. name = pyobj.func_name
  167. if signature is None:
  168. if hasattr(pyobj, '_generator_next_method_of_'):
  169. from rpython.flowspace.argument import Signature
  170. signature = Signature(['entry']) # haaaaaack
  171. defaults = ()
  172. else:
  173. signature = cpython_code_signature(pyobj.func_code)
  174. if defaults is None:
  175. defaults = pyobj.func_defaults
  176. self.name = name
  177. self.signature = signature
  178. self.defaults = defaults or ()
  179. # 'specializer' is a function with the following signature:
  180. # specializer(funcdesc, args_s) => graph
  181. # or => s_result (overridden/memo cases)
  182. self.specializer = specializer
  183. self._cache = {} # convenience for the specializer
  184. def buildgraph(self, alt_name=None, builder=None):
  185. translator = self.bookkeeper.annotator.translator
  186. if builder:
  187. graph = builder(translator, self.pyobj)
  188. else:
  189. graph = translator.buildflowgraph(self.pyobj)
  190. if alt_name:
  191. graph.name = alt_name
  192. return graph
  193. def getgraphs(self):
  194. return self._cache.values()
  195. def getuniquegraph(self):
  196. if len(self._cache) != 1:
  197. raise NoStandardGraph(self)
  198. [graph] = self._cache.values()
  199. relax_sig_check = getattr(self.pyobj, "relax_sig_check", False)
  200. if (graph.signature != self.signature or
  201. graph.defaults != self.defaults) and not relax_sig_check:
  202. raise NoStandardGraph(self)
  203. return graph
  204. def cachedgraph(self, key, alt_name=None, builder=None):
  205. try:
  206. return self._cache[key]
  207. except KeyError:
  208. def nameof(thing):
  209. if isinstance(thing, str):
  210. return thing
  211. elif hasattr(thing, '__name__'): # mostly types and functions
  212. return thing.__name__
  213. elif hasattr(thing, 'name') and isinstance(thing.name, str):
  214. return thing.name # mostly ClassDescs
  215. elif isinstance(thing, tuple):
  216. return '_'.join(map(nameof, thing))
  217. else:
  218. return str(thing)[:30]
  219. if key is not None and alt_name is None:
  220. postfix = valid_identifier(nameof(key))
  221. alt_name = "%s__%s" % (self.name, postfix)
  222. graph = self.buildgraph(alt_name, builder)
  223. self._cache[key] = graph
  224. return graph
  225. def parse_arguments(self, args, graph=None):
  226. defs_s = []
  227. if graph is None:
  228. signature = self.signature
  229. defaults = self.defaults
  230. else:
  231. signature = graph.signature
  232. defaults = graph.defaults
  233. if defaults:
  234. for x in defaults:
  235. if x is NODEFAULT:
  236. defs_s.append(None)
  237. else:
  238. defs_s.append(self.bookkeeper.immutablevalue(x))
  239. try:
  240. inputcells = args.match_signature(signature, defs_s)
  241. except ArgErr as e:
  242. raise AnnotatorError("signature mismatch: %s() %s" %
  243. (self.name, e.getmsg()))
  244. return inputcells
  245. def specialize(self, inputcells, op=None):
  246. if (op is None and
  247. getattr(self.bookkeeper, "position_key", None) is not None):
  248. _, block, i = self.bookkeeper.position_key
  249. op = block.operations[i]
  250. if self.specializer is None:
  251. # get the specializer based on the tag of the 'pyobj'
  252. # (if any), according to the current policy
  253. tag = getattr(self.pyobj, '_annspecialcase_', None)
  254. policy = self.bookkeeper.annotator.policy
  255. self.specializer = policy.get_specializer(tag)
  256. enforceargs = getattr(self.pyobj, '_annenforceargs_', None)
  257. signature = getattr(self.pyobj, '_signature_', None)
  258. if enforceargs and signature:
  259. raise Exception("%r: signature and enforceargs cannot both be "
  260. "used" % (self,))
  261. if enforceargs:
  262. if not callable(enforceargs):
  263. from rpython.annotator.signature import Sig
  264. enforceargs = Sig(*enforceargs)
  265. self.pyobj._annenforceargs_ = enforceargs
  266. enforceargs(self, inputcells) # can modify inputcells in-place
  267. if signature:
  268. enforce_signature_args(self, signature[0], inputcells) # mutates inputcells
  269. if getattr(self.pyobj, '_annspecialcase_', '').endswith("call_location"):
  270. return self.specializer(self, inputcells, op)
  271. else:
  272. return self.specializer(self, inputcells)
  273. def pycall(self, whence, args, s_previous_result, op=None):
  274. inputcells = self.parse_arguments(args)
  275. result = self.specialize(inputcells, op)
  276. if isinstance(result, FunctionGraph):
  277. graph = result # common case
  278. annotator = self.bookkeeper.annotator
  279. # if that graph has a different signature, we need to re-parse
  280. # the arguments.
  281. # recreate the args object because inputcells may have been changed
  282. new_args = args.unmatch_signature(self.signature, inputcells)
  283. inputcells = self.parse_arguments(new_args, graph)
  284. result = annotator.recursivecall(graph, whence, inputcells)
  285. signature = getattr(self.pyobj, '_signature_', None)
  286. if signature:
  287. sigresult = enforce_signature_return(self, signature[1], result)
  288. if sigresult is not None:
  289. annotator.addpendingblock(
  290. graph, graph.returnblock, [sigresult])
  291. result = sigresult
  292. # Some specializations may break the invariant of returning
  293. # annotations that are always more general than the previous time.
  294. # We restore it here:
  295. from rpython.annotator.model import unionof
  296. result = unionof(result, s_previous_result)
  297. return result
  298. def get_graph(self, args, op):
  299. inputs_s = self.parse_arguments(args)
  300. return self.specialize(inputs_s, op)
  301. def get_call_parameters(self, args_s):
  302. args = simple_args(args_s)
  303. inputcells = self.parse_arguments(args)
  304. graph = self.specialize(inputcells)
  305. assert isinstance(graph, FunctionGraph)
  306. # if that graph has a different signature, we need to re-parse
  307. # the arguments.
  308. # recreate the args object because inputcells may have been changed
  309. new_args = args.unmatch_signature(self.signature, inputcells)
  310. inputcells = self.parse_arguments(new_args, graph)
  311. signature = getattr(self.pyobj, '_signature_', None)
  312. if signature:
  313. s_result = finish_type(signature[1], self.bookkeeper, self.pyobj)
  314. if s_result is not None:
  315. self.bookkeeper.annotator.addpendingblock(
  316. graph, graph.returnblock, [s_result])
  317. return graph, inputcells
  318. def bind_under(self, classdef, name):
  319. # XXX static methods
  320. return self.bookkeeper.getmethoddesc(self,
  321. classdef, # originclassdef,
  322. None, # selfclassdef
  323. name)
  324. @staticmethod
  325. def consider_call_site(descs, args, s_result, op):
  326. family = descs[0].getcallfamily()
  327. shape = rawshape(args)
  328. row = build_calltable_row(descs, args, op)
  329. family.calltable_add_row(shape, row)
  330. descs[0].mergecallfamilies(*descs[1:])
  331. def rowkey(self):
  332. return self
  333. def get_s_signatures(self, shape):
  334. family = self.getcallfamily()
  335. table = family.calltables.get(shape)
  336. if table is None:
  337. return []
  338. else:
  339. graph_seen = {}
  340. s_sigs = []
  341. binding = self.bookkeeper.annotator.binding
  342. def enlist(graph):
  343. if graph in graph_seen:
  344. return
  345. graph_seen[graph] = True
  346. s_sig = ([binding(v) for v in graph.getargs()],
  347. binding(graph.getreturnvar()))
  348. if s_sig in s_sigs:
  349. return
  350. s_sigs.append(s_sig)
  351. for row in table:
  352. for graph in row.itervalues():
  353. enlist(graph)
  354. return s_sigs
  355. class MethodDesc(Desc):
  356. knowntype = types.MethodType
  357. def __init__(self, bookkeeper, funcdesc, originclassdef,
  358. selfclassdef, name, flags={}):
  359. super(MethodDesc, self).__init__(bookkeeper)
  360. self.funcdesc = funcdesc
  361. self.originclassdef = originclassdef
  362. self.selfclassdef = selfclassdef
  363. self.name = name
  364. self.flags = flags
  365. def __repr__(self):
  366. if self.selfclassdef is None:
  367. return '<unbound MethodDesc %r of %r>' % (self.name,
  368. self.originclassdef)
  369. else:
  370. return '<MethodDesc %r of %r bound to %r %r>' % (self.name,
  371. self.originclassdef,
  372. self.selfclassdef,
  373. self.flags)
  374. def getuniquegraph(self):
  375. return self.funcdesc.getuniquegraph()
  376. def func_args(self, args):
  377. from rpython.annotator.model import SomeInstance
  378. if self.selfclassdef is None:
  379. raise Exception("calling %r" % (self,))
  380. s_instance = SomeInstance(self.selfclassdef, flags=self.flags)
  381. return args.prepend(s_instance)
  382. def pycall(self, whence, args, s_previous_result, op=None):
  383. func_args = self.func_args(args)
  384. return self.funcdesc.pycall(whence, func_args, s_previous_result, op)
  385. def get_graph(self, args, op):
  386. func_args = self.func_args(args)
  387. return self.funcdesc.get_graph(func_args, op)
  388. def bind_under(self, classdef, name):
  389. self.bookkeeper.warning("rebinding an already bound %r" % (self,))
  390. return self.funcdesc.bind_under(classdef, name)
  391. def bind_self(self, newselfclassdef, flags={}):
  392. return self.bookkeeper.getmethoddesc(self.funcdesc,
  393. self.originclassdef,
  394. newselfclassdef,
  395. self.name,
  396. flags)
  397. @staticmethod
  398. def consider_call_site(descs, args, s_result, op):
  399. cnt, keys, star = rawshape(args)
  400. shape = cnt + 1, keys, star # account for the extra 'self'
  401. row = build_calltable_row(descs, args, op)
  402. family = descs[0].getcallfamily()
  403. family.calltable_add_row(shape, row)
  404. descs[0].mergecallfamilies(*descs[1:])
  405. def rowkey(self):
  406. # we are computing call families and call tables that always contain
  407. # FunctionDescs, not MethodDescs. The present method returns the
  408. # FunctionDesc to use as a key in that family.
  409. return self.funcdesc
  410. @staticmethod
  411. def simplify_desc_set(descs):
  412. # Some hacking needed to make contains() happy on SomePBC: if the
  413. # set of MethodDescs contains some "redundant" ones, i.e. ones that
  414. # are less general than others already in the set, then kill them.
  415. # This ensures that if 'a' is less general than 'b', then
  416. # SomePBC({a}) union SomePBC({b}) is again SomePBC({b}).
  417. #
  418. # Two cases:
  419. # 1. if two MethodDescs differ in their selfclassdefs, and if one
  420. # of the selfclassdefs is a subclass of the other;
  421. # 2. if two MethodDescs differ in their flags, take the intersection.
  422. # --- case 2 ---
  423. # only keep the intersection of all the flags, that's good enough
  424. lst = list(descs)
  425. commonflags = lst[0].flags.copy()
  426. for key, value in commonflags.items():
  427. for desc in lst[1:]:
  428. if key not in desc.flags or desc.flags[key] != value:
  429. del commonflags[key]
  430. break
  431. for desc in lst:
  432. if desc.flags != commonflags:
  433. newdesc = desc.bookkeeper.getmethoddesc(desc.funcdesc,
  434. desc.originclassdef,
  435. desc.selfclassdef,
  436. desc.name,
  437. commonflags)
  438. descs.remove(desc)
  439. descs.add(newdesc)
  440. # --- case 1 ---
  441. groups = {}
  442. for desc in descs:
  443. if desc.selfclassdef is not None:
  444. key = desc.funcdesc, desc.originclassdef, desc.name
  445. groups.setdefault(key, []).append(desc)
  446. for group in groups.values():
  447. if len(group) > 1:
  448. for desc1 in group:
  449. cdef1 = desc1.selfclassdef
  450. for desc2 in group:
  451. cdef2 = desc2.selfclassdef
  452. if cdef1 is not cdef2 and cdef1.issubclass(cdef2):
  453. descs.remove(desc1)
  454. break
  455. def new_or_old_class(c):
  456. if hasattr(c, '__class__'):
  457. return c.__class__
  458. else:
  459. return type(c)
  460. class FrozenDesc(Desc):
  461. def __init__(self, bookkeeper, pyobj, read_attribute=None):
  462. super(FrozenDesc, self).__init__(bookkeeper, pyobj)
  463. if read_attribute is None:
  464. read_attribute = lambda attr: getattr(pyobj, attr)
  465. self._read_attribute = read_attribute
  466. self.attrcache = {}
  467. self.knowntype = new_or_old_class(pyobj)
  468. assert bool(pyobj), "__nonzero__ unsupported on frozen PBC %r" % (pyobj,)
  469. def has_attribute(self, attr):
  470. if attr in self.attrcache:
  471. return True
  472. try:
  473. self._read_attribute(attr)
  474. return True
  475. except AttributeError:
  476. return False
  477. def warn_missing_attribute(self, attr):
  478. # only warn for missing attribute names whose name doesn't start
  479. # with '$', to silence the warnings about '$memofield_xxx'.
  480. return not self.has_attribute(attr) and not attr.startswith('$')
  481. def read_attribute(self, attr):
  482. try:
  483. return self.attrcache[attr]
  484. except KeyError:
  485. result = self.attrcache[attr] = self._read_attribute(attr)
  486. return result
  487. def s_read_attribute(self, attr):
  488. try:
  489. value = self.read_attribute(attr)
  490. except AttributeError:
  491. return s_ImpossibleValue
  492. else:
  493. return self.bookkeeper.immutablevalue(value)
  494. def create_new_attribute(self, name, value):
  495. try:
  496. self.read_attribute(name)
  497. except AttributeError:
  498. pass
  499. else:
  500. raise AssertionError("name clash: %r" % (name,))
  501. self.attrcache[name] = value
  502. def getattrfamily(self, attrname=None):
  503. "Get the FrozenAttrFamily object for attrname. Possibly creates one."
  504. access_sets = self.bookkeeper.frozenpbc_attr_families
  505. _, _, attrfamily = access_sets.find(self)
  506. return attrfamily
  507. def queryattrfamily(self, attrname=None):
  508. """Retrieve the FrozenAttrFamily object for attrname if there is one,
  509. otherwise return None."""
  510. access_sets = self.bookkeeper.frozenpbc_attr_families
  511. try:
  512. return access_sets[self]
  513. except KeyError:
  514. return None
  515. def mergeattrfamilies(self, others, attrname=None):
  516. """Merge the attr families of the given Descs into one."""
  517. access_sets = self.bookkeeper.frozenpbc_attr_families
  518. changed, rep, attrfamily = access_sets.find(self)
  519. for desc in others:
  520. changed1, rep, attrfamily = access_sets.union(rep, desc)
  521. changed = changed or changed1
  522. return changed
  523. class MethodOfFrozenDesc(Desc):
  524. knowntype = types.MethodType
  525. def __init__(self, bookkeeper, funcdesc, frozendesc):
  526. super(MethodOfFrozenDesc, self).__init__(bookkeeper)
  527. self.funcdesc = funcdesc
  528. self.frozendesc = frozendesc
  529. def __repr__(self):
  530. return '<MethodOfFrozenDesc %r of %r>' % (self.funcdesc,
  531. self.frozendesc)
  532. def func_args(self, args):
  533. from rpython.annotator.model import SomePBC
  534. s_self = SomePBC([self.frozendesc])
  535. return args.prepend(s_self)
  536. def pycall(self, whence, args, s_previous_result, op=None):
  537. func_args = self.func_args(args)
  538. return self.funcdesc.pycall(whence, func_args, s_previous_result, op)
  539. def get_graph(self, args, op):
  540. func_args = self.func_args(args)
  541. return self.funcdesc.get_graph(func_args, op)
  542. @staticmethod
  543. def consider_call_site(descs, args, s_result, op):
  544. cnt, keys, star = rawshape(args)
  545. shape = cnt + 1, keys, star # account for the extra 'self'
  546. row = build_calltable_row(descs, args, op)
  547. family = descs[0].getcallfamily()
  548. family.calltable_add_row(shape, row)
  549. descs[0].mergecallfamilies(*descs[1:])
  550. def rowkey(self):
  551. return self.funcdesc