PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/annotator/bookkeeper.py

https://bitbucket.org/pypy/pypy/
Python | 594 lines | 554 code | 26 blank | 14 comment | 28 complexity | 061e1109940b60cc01495cb693bf404f MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """
  2. The Bookkeeper class.
  3. """
  4. from __future__ import absolute_import
  5. import sys, types, inspect, weakref
  6. from contextlib import contextmanager
  7. from collections import OrderedDict
  8. from rpython.flowspace.model import Constant
  9. from rpython.annotator.model import (
  10. SomeOrderedDict, SomeString, SomeChar, SomeFloat, unionof, SomeInstance,
  11. SomeDict, SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint,
  12. s_None, s_ImpossibleValue, SomeBool, SomeTuple, SomeException,
  13. SomeImpossibleValue, SomeUnicodeString, SomeList, HarmlesslyBlocked,
  14. SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty)
  15. from rpython.annotator.classdesc import ClassDef, ClassDesc
  16. from rpython.annotator.listdef import ListDef, ListItem
  17. from rpython.annotator.dictdef import DictDef
  18. from rpython.annotator import description
  19. from rpython.annotator.signature import annotationoftype
  20. from rpython.annotator.argument import simple_args
  21. from rpython.rlib.objectmodel import r_dict, r_ordereddict, Symbolic
  22. from rpython.tool.algo.unionfind import UnionFind
  23. from rpython.rtyper import extregistry
  24. BUILTIN_ANALYZERS = {}
  25. def analyzer_for(func):
  26. def wrapped(ann_func):
  27. BUILTIN_ANALYZERS[func] = ann_func
  28. return ann_func
  29. return wrapped
  30. class Bookkeeper(object):
  31. """The log of choices that have been made while analysing the operations.
  32. It ensures that the same 'choice objects' will be returned if we ask
  33. again during reflowing. Like ExecutionContext, there is an implicit
  34. Bookkeeper that can be obtained from a thread-local variable.
  35. Currently used for factories and user-defined classes."""
  36. def __setstate__(self, dic):
  37. self.__dict__.update(dic) # normal action
  38. self.register_builtins()
  39. def __init__(self, annotator):
  40. self.annotator = annotator
  41. self.policy = annotator.policy
  42. self.descs = {} # map Python objects to their XxxDesc wrappers
  43. self.methoddescs = {} # map (funcdesc, classdef) to the MethodDesc
  44. self.classdefs = [] # list of all ClassDefs
  45. self.seen_mutable = {}
  46. self.listdefs = {} # map position_keys to ListDefs
  47. self.dictdefs = {} # map position_keys to DictDefs
  48. self.immutable_cache = {}
  49. self.classpbc_attr_families = {} # {'attr': UnionFind(ClassAttrFamily)}
  50. self.frozenpbc_attr_families = UnionFind(description.FrozenAttrFamily)
  51. self.pbc_maximal_call_families = UnionFind(description.CallFamily)
  52. self.emulated_pbc_calls = {}
  53. self.all_specializations = {} # {FuncDesc: specialization-info}
  54. self.pending_specializations = [] # list of callbacks
  55. self.external_class_cache = {} # cache of ExternalType classes
  56. self.needs_generic_instantiate = {}
  57. self.thread_local_fields = set()
  58. self.register_builtins()
  59. def register_builtins(self):
  60. import rpython.annotator.builtin # for side-effects
  61. from rpython.annotator.exception import standardexceptions
  62. for cls in standardexceptions:
  63. self.getuniqueclassdef(cls)
  64. def enter(self, position_key):
  65. """Start of an operation.
  66. The operation is uniquely identified by the given key."""
  67. assert not hasattr(self, 'position_key'), "don't call enter() nestedly"
  68. self.position_key = position_key
  69. TLS.bookkeeper = self
  70. def leave(self):
  71. """End of an operation."""
  72. del TLS.bookkeeper
  73. del self.position_key
  74. @contextmanager
  75. def at_position(self, pos):
  76. """A context manager calling `self.enter()` and `self.leave()`"""
  77. if hasattr(self, 'position_key') and pos is None:
  78. yield
  79. return
  80. self.enter(pos)
  81. try:
  82. yield
  83. finally:
  84. self.leave()
  85. def compute_at_fixpoint(self):
  86. # getbookkeeper() needs to work during this function, so provide
  87. # one with a dummy position
  88. with self.at_position(None):
  89. for call_op in self.annotator.call_sites():
  90. self.consider_call_site(call_op)
  91. for pbc, args_s in self.emulated_pbc_calls.itervalues():
  92. args = simple_args(args_s)
  93. pbc.consider_call_site(args, s_ImpossibleValue, None)
  94. self.emulated_pbc_calls = {}
  95. def check_no_flags_on_instances(self):
  96. # sanity check: no flags attached to heap stored instances
  97. seen = set()
  98. def check_no_flags(s_value_or_def):
  99. if isinstance(s_value_or_def, SomeInstance):
  100. assert not s_value_or_def.flags, "instance annotation with flags escaped to the heap"
  101. check_no_flags(s_value_or_def.classdef)
  102. elif isinstance(s_value_or_def, SomeList):
  103. check_no_flags(s_value_or_def.listdef.listitem)
  104. elif isinstance(s_value_or_def, SomeDict):
  105. check_no_flags(s_value_or_def.dictdef.dictkey)
  106. check_no_flags(s_value_or_def.dictdef.dictvalue)
  107. elif isinstance(s_value_or_def, SomeTuple):
  108. for s_item in s_value_or_def.items:
  109. check_no_flags(s_item)
  110. elif isinstance(s_value_or_def, ClassDef):
  111. if s_value_or_def in seen:
  112. return
  113. seen.add(s_value_or_def)
  114. for attr in s_value_or_def.attrs.itervalues():
  115. s_attr = attr.s_value
  116. check_no_flags(s_attr)
  117. elif isinstance(s_value_or_def, ListItem):
  118. if s_value_or_def in seen:
  119. return
  120. seen.add(s_value_or_def)
  121. check_no_flags(s_value_or_def.s_value)
  122. for clsdef in self.classdefs:
  123. check_no_flags(clsdef)
  124. def consider_call_site(self, call_op):
  125. from rpython.rtyper.llannotation import SomeLLADTMeth, lltype_to_annotation
  126. annotation = self.annotator.annotation
  127. s_callable = annotation(call_op.args[0])
  128. args_s = [annotation(arg) for arg in call_op.args[1:]]
  129. if isinstance(s_callable, SomeLLADTMeth):
  130. adtmeth = s_callable
  131. s_callable = self.immutablevalue(adtmeth.func)
  132. args_s = [lltype_to_annotation(adtmeth.ll_ptrtype)] + args_s
  133. if isinstance(s_callable, SomePBC):
  134. s_result = annotation(call_op.result)
  135. if s_result is None:
  136. s_result = s_ImpossibleValue
  137. args = call_op.build_args(args_s)
  138. s_callable.consider_call_site(args, s_result, call_op)
  139. def getuniqueclassdef(self, cls):
  140. """Get the ClassDef associated with the given user cls."""
  141. assert cls is not object
  142. desc = self.getdesc(cls)
  143. return desc.getuniqueclassdef()
  144. def new_exception(self, exc_classes):
  145. clsdefs = {self.getuniqueclassdef(cls) for cls in exc_classes}
  146. return SomeException(clsdefs)
  147. def getlistdef(self, **flags_if_new):
  148. """Get the ListDef associated with the current position."""
  149. try:
  150. listdef = self.listdefs[self.position_key]
  151. except KeyError:
  152. listdef = self.listdefs[self.position_key] = ListDef(self)
  153. listdef.listitem.__dict__.update(flags_if_new)
  154. return listdef
  155. def newlist(self, *s_values, **flags):
  156. """Make a SomeList associated with the current position, general
  157. enough to contain the s_values as items."""
  158. listdef = self.getlistdef(**flags)
  159. for s_value in s_values:
  160. listdef.generalize(s_value)
  161. if flags:
  162. assert flags.keys() == ['range_step']
  163. listdef.generalize_range_step(flags['range_step'])
  164. return SomeList(listdef)
  165. def getdictdef(self, is_r_dict=False, force_non_null=False):
  166. """Get the DictDef associated with the current position."""
  167. try:
  168. dictdef = self.dictdefs[self.position_key]
  169. except KeyError:
  170. dictdef = DictDef(self, is_r_dict=is_r_dict,
  171. force_non_null=force_non_null)
  172. self.dictdefs[self.position_key] = dictdef
  173. return dictdef
  174. def newdict(self):
  175. """Make a so-far empty SomeDict associated with the current
  176. position."""
  177. return SomeDict(self.getdictdef())
  178. def immutablevalue(self, x):
  179. """The most precise SomeValue instance that contains the
  180. immutable value x."""
  181. # convert unbound methods to the underlying function
  182. if hasattr(x, 'im_self') and x.im_self is None:
  183. x = x.im_func
  184. assert not hasattr(x, 'im_self')
  185. tp = type(x)
  186. if issubclass(tp, Symbolic): # symbolic constants support
  187. result = x.annotation()
  188. result.const_box = Constant(x)
  189. return result
  190. if tp is bool:
  191. result = SomeBool()
  192. elif tp is int:
  193. result = SomeInteger(nonneg = x>=0)
  194. elif tp is long:
  195. if -sys.maxint-1 <= x <= sys.maxint:
  196. x = int(x)
  197. result = SomeInteger(nonneg = x>=0)
  198. else:
  199. # XXX: better error reporting?
  200. raise ValueError("seeing a prebuilt long (value %s)" % hex(x))
  201. elif issubclass(tp, str): # py.lib uses annotated str subclasses
  202. no_nul = not '\x00' in x
  203. if len(x) == 1:
  204. result = SomeChar(no_nul=no_nul)
  205. else:
  206. result = SomeString(no_nul=no_nul)
  207. elif tp is unicode:
  208. no_nul = not u'\x00' in x
  209. if len(x) == 1:
  210. result = SomeUnicodeCodePoint(no_nul=no_nul)
  211. else:
  212. result = SomeUnicodeString(no_nul=no_nul)
  213. elif tp is bytearray:
  214. result = SomeByteArray()
  215. elif tp is tuple:
  216. result = SomeTuple(items = [self.immutablevalue(e) for e in x])
  217. elif tp is float:
  218. result = SomeFloat()
  219. elif tp is list:
  220. key = Constant(x)
  221. try:
  222. return self.immutable_cache[key]
  223. except KeyError:
  224. result = SomeList(ListDef(self, s_ImpossibleValue))
  225. self.immutable_cache[key] = result
  226. for e in x:
  227. result.listdef.generalize(self.immutablevalue(e))
  228. result.const_box = key
  229. return result
  230. elif (tp is dict or tp is r_dict or
  231. tp is OrderedDict or tp is r_ordereddict):
  232. key = Constant(x)
  233. try:
  234. return self.immutable_cache[key]
  235. except KeyError:
  236. if tp is OrderedDict or tp is r_ordereddict:
  237. cls = SomeOrderedDict
  238. else:
  239. cls = SomeDict
  240. is_r_dict = issubclass(tp, r_dict)
  241. result = cls(DictDef(self,
  242. s_ImpossibleValue,
  243. s_ImpossibleValue,
  244. is_r_dict = is_r_dict))
  245. self.immutable_cache[key] = result
  246. if is_r_dict:
  247. s_eqfn = self.immutablevalue(x.key_eq)
  248. s_hashfn = self.immutablevalue(x.key_hash)
  249. result.dictdef.dictkey.update_rdict_annotations(s_eqfn,
  250. s_hashfn)
  251. seen_elements = 0
  252. while seen_elements != len(x):
  253. items = x.items()
  254. for ek, ev in items:
  255. result.dictdef.generalize_key(self.immutablevalue(ek))
  256. result.dictdef.generalize_value(self.immutablevalue(ev))
  257. result.dictdef.seen_prebuilt_key(ek)
  258. seen_elements = len(items)
  259. # if the dictionary grew during the iteration,
  260. # start over again
  261. result.const_box = key
  262. return result
  263. elif tp is weakref.ReferenceType:
  264. x1 = x()
  265. if x1 is None:
  266. result = SomeWeakRef(None) # dead weakref
  267. else:
  268. s1 = self.immutablevalue(x1)
  269. assert isinstance(s1, SomeInstance)
  270. result = SomeWeakRef(s1.classdef)
  271. elif tp is property:
  272. return SomeProperty(x)
  273. elif ishashable(x) and x in BUILTIN_ANALYZERS:
  274. _module = getattr(x,"__module__","unknown")
  275. result = SomeBuiltin(BUILTIN_ANALYZERS[x], methodname="%s.%s" % (_module, x.__name__))
  276. elif extregistry.is_registered(x):
  277. entry = extregistry.lookup(x)
  278. result = entry.compute_annotation_bk(self)
  279. elif tp is type:
  280. result = SomeConstantType(x, self)
  281. elif callable(x):
  282. if hasattr(x, 'im_self') and hasattr(x, 'im_func'):
  283. # on top of PyPy, for cases like 'l.append' where 'l' is a
  284. # global constant list, the find_method() returns non-None
  285. s_self = self.immutablevalue(x.im_self)
  286. result = s_self.find_method(x.im_func.__name__)
  287. elif hasattr(x, '__self__') and x.__self__ is not None:
  288. # for cases like 'l.append' where 'l' is a global constant list
  289. s_self = self.immutablevalue(x.__self__)
  290. result = s_self.find_method(x.__name__)
  291. assert result is not None
  292. else:
  293. result = None
  294. if result is None:
  295. result = SomePBC([self.getdesc(x)])
  296. elif hasattr(x, '_freeze_'):
  297. assert x._freeze_() is True
  298. # user-defined classes can define a method _freeze_(), which
  299. # is called when a prebuilt instance is found. If the method
  300. # returns True, the instance is considered immutable and becomes
  301. # a SomePBC(). Otherwise it's just SomeInstance().
  302. result = SomePBC([self.getdesc(x)])
  303. elif hasattr(x, '__class__') \
  304. and x.__class__.__module__ != '__builtin__':
  305. if hasattr(x, '_cleanup_'):
  306. x._cleanup_()
  307. classdef = self.getuniqueclassdef(x.__class__)
  308. classdef.see_instance(x)
  309. result = SomeInstance(classdef)
  310. elif x is None:
  311. return s_None
  312. else:
  313. raise Exception("Don't know how to represent %r" % (x,))
  314. result.const = x
  315. return result
  316. def getdesc(self, pyobj):
  317. # get the XxxDesc wrapper for the given Python object, which must be
  318. # one of:
  319. # * a user-defined Python function
  320. # * a Python type or class (but not a built-in one like 'int')
  321. # * a user-defined bound or unbound method object
  322. # * a frozen pre-built constant (with _freeze_() == True)
  323. # * a bound method of a frozen pre-built constant
  324. obj_key = Constant(pyobj)
  325. try:
  326. return self.descs[obj_key]
  327. except KeyError:
  328. if isinstance(pyobj, types.FunctionType):
  329. result = description.FunctionDesc(self, pyobj)
  330. elif isinstance(pyobj, (type, types.ClassType)):
  331. if pyobj is object:
  332. raise Exception("ClassDesc for object not supported")
  333. if pyobj.__module__ == '__builtin__': # avoid making classdefs for builtin types
  334. result = self.getfrozen(pyobj)
  335. else:
  336. result = ClassDesc(self, pyobj)
  337. elif isinstance(pyobj, types.MethodType):
  338. if pyobj.im_self is None: # unbound
  339. return self.getdesc(pyobj.im_func)
  340. if hasattr(pyobj.im_self, '_cleanup_'):
  341. pyobj.im_self._cleanup_()
  342. if hasattr(pyobj.im_self, '_freeze_'): # method of frozen
  343. assert pyobj.im_self._freeze_() is True
  344. result = description.MethodOfFrozenDesc(self,
  345. self.getdesc(pyobj.im_func), # funcdesc
  346. self.getdesc(pyobj.im_self)) # frozendesc
  347. else: # regular method
  348. origincls, name = origin_of_meth(pyobj)
  349. classdef = self.getuniqueclassdef(pyobj.im_class)
  350. classdef.see_instance(pyobj.im_self)
  351. assert pyobj == getattr(pyobj.im_self, name), (
  352. "%r is not %s.%s ??" % (pyobj, pyobj.im_self, name))
  353. # emulate a getattr to make sure it's on the classdef
  354. classdef.find_attribute(name)
  355. result = self.getmethoddesc(
  356. self.getdesc(pyobj.im_func), # funcdesc
  357. self.getuniqueclassdef(origincls), # originclassdef
  358. classdef, # selfclassdef
  359. name)
  360. else:
  361. # must be a frozen pre-built constant, but let's check
  362. if hasattr(pyobj, '_freeze_'):
  363. assert pyobj._freeze_() is True
  364. else:
  365. if hasattr(pyobj, '__call__'):
  366. msg = "object with a __call__ is not RPython"
  367. else:
  368. msg = "unexpected prebuilt constant"
  369. raise Exception("%s: %r" % (msg, pyobj))
  370. result = self.getfrozen(pyobj)
  371. self.descs[obj_key] = result
  372. return result
  373. def getfrozen(self, pyobj):
  374. return description.FrozenDesc(self, pyobj)
  375. def getmethoddesc(self, funcdesc, originclassdef, selfclassdef, name,
  376. flags={}):
  377. flagskey = flags.items()
  378. flagskey.sort()
  379. key = funcdesc, originclassdef, selfclassdef, name, tuple(flagskey)
  380. try:
  381. return self.methoddescs[key]
  382. except KeyError:
  383. result = description.MethodDesc(self, funcdesc, originclassdef,
  384. selfclassdef, name, flags)
  385. self.methoddescs[key] = result
  386. return result
  387. def valueoftype(self, t):
  388. return annotationoftype(t, self)
  389. def get_classpbc_attr_families(self, attrname):
  390. """Return the UnionFind for the ClassAttrFamilies corresponding to
  391. attributes of the given name.
  392. """
  393. map = self.classpbc_attr_families
  394. try:
  395. access_sets = map[attrname]
  396. except KeyError:
  397. access_sets = map[attrname] = UnionFind(description.ClassAttrFamily)
  398. return access_sets
  399. def pbc_getattr(self, pbc, s_attr):
  400. assert s_attr.is_constant()
  401. attr = s_attr.const
  402. descs = list(pbc.descriptions)
  403. first = descs[0]
  404. if len(descs) == 1:
  405. return first.s_read_attribute(attr)
  406. change = first.mergeattrfamilies(descs[1:], attr)
  407. attrfamily = first.getattrfamily(attr)
  408. position = self.position_key
  409. attrfamily.read_locations[position] = True
  410. actuals = []
  411. for desc in descs:
  412. actuals.append(desc.s_read_attribute(attr))
  413. s_result = unionof(*actuals)
  414. s_oldvalue = attrfamily.get_s_value(attr)
  415. attrfamily.set_s_value(attr, unionof(s_result, s_oldvalue))
  416. if change:
  417. for position in attrfamily.read_locations:
  418. self.annotator.reflowfromposition(position)
  419. if isinstance(s_result, SomeImpossibleValue):
  420. for desc in descs:
  421. try:
  422. attrs = desc.read_attribute('_attrs_')
  423. except AttributeError:
  424. continue
  425. if isinstance(attrs, Constant):
  426. attrs = attrs.value
  427. if attr in attrs:
  428. raise HarmlesslyBlocked("getattr on enforced attr")
  429. return s_result
  430. def getattr_locations(self, clsdesc, attrname):
  431. attrdef = clsdesc.classdef.find_attribute(attrname)
  432. return attrdef.read_locations
  433. def record_getattr(self, clsdesc, attrname):
  434. locations = self.getattr_locations(clsdesc, attrname)
  435. locations.add(self.position_key)
  436. def update_attr(self, clsdef, attrdef):
  437. locations = self.getattr_locations(clsdef.classdesc, attrdef.name)
  438. for position in locations:
  439. self.annotator.reflowfromposition(position)
  440. attrdef.validate(homedef=clsdef)
  441. def pbc_call(self, pbc, args, emulated=None):
  442. """Analyse a call to a SomePBC() with the given args (list of
  443. annotations).
  444. """
  445. if emulated is None:
  446. whence = self.position_key
  447. # fish the existing annotation for the result variable,
  448. # needed by some kinds of specialization.
  449. fn, block, i = self.position_key
  450. op = block.operations[i]
  451. s_previous_result = self.annotator.annotation(op.result)
  452. if s_previous_result is None:
  453. s_previous_result = s_ImpossibleValue
  454. else:
  455. if emulated is True:
  456. whence = None
  457. else:
  458. whence = emulated # callback case
  459. op = None
  460. s_previous_result = s_ImpossibleValue
  461. results = []
  462. for desc in pbc.descriptions:
  463. results.append(desc.pycall(whence, args, s_previous_result, op))
  464. s_result = unionof(*results)
  465. return s_result
  466. def emulate_pbc_call(self, unique_key, pbc, args_s, replace=[], callback=None):
  467. """For annotating some operation that causes indirectly a Python
  468. function to be called. The annotation of the function is "pbc",
  469. and the list of annotations of arguments is "args_s".
  470. Can be called in various contexts, but from compute_annotation()
  471. or compute_result_annotation() of an ExtRegistryEntry, call it
  472. with both "unique_key" and "callback" set to
  473. "self.bookkeeper.position_key". If there are several calls from
  474. the same operation, they need their own "unique_key", like
  475. (position_key, "first") and (position_key, "second").
  476. In general, "unique_key" should somehow uniquely identify where
  477. the call is in the source code, and "callback" can be either a
  478. position_key to reflow from when we see more general results,
  479. or a real callback function that will be called with arguments
  480. # "(annotator, called_graph)" whenever the result is generalized.
  481. "replace" can be set to a list of old unique_key values to
  482. forget now, because the given "unique_key" replaces them.
  483. """
  484. with self.at_position(None):
  485. emulated_pbc_calls = self.emulated_pbc_calls
  486. prev = [unique_key]
  487. prev.extend(replace)
  488. for other_key in prev:
  489. if other_key in emulated_pbc_calls:
  490. del emulated_pbc_calls[other_key]
  491. emulated_pbc_calls[unique_key] = pbc, args_s
  492. args = simple_args(args_s)
  493. if callback is None:
  494. emulated = True
  495. else:
  496. emulated = callback
  497. return self.pbc_call(pbc, args, emulated=emulated)
  498. def whereami(self):
  499. return self.annotator.whereami(self.position_key)
  500. def event(self, what, x):
  501. return self.annotator.policy.event(self, what, x)
  502. def warning(self, msg):
  503. return self.annotator.warning(msg)
  504. def origin_of_meth(boundmeth):
  505. func = boundmeth.im_func
  506. candname = func.func_name
  507. for cls in inspect.getmro(boundmeth.im_class):
  508. dict = cls.__dict__
  509. if dict.get(candname) is func:
  510. return cls, candname
  511. for name, value in dict.iteritems():
  512. if value is func:
  513. return cls, name
  514. raise Exception("could not match bound-method to attribute name: %r" % (boundmeth,))
  515. def ishashable(x):
  516. try:
  517. hash(x)
  518. except TypeError:
  519. return False
  520. else:
  521. return True
  522. # get current bookkeeper
  523. def getbookkeeper():
  524. """Get the current Bookkeeper.
  525. Only works during the analysis of an operation."""
  526. try:
  527. return TLS.bookkeeper
  528. except AttributeError:
  529. return None
  530. def immutablevalue(x):
  531. return getbookkeeper().immutablevalue(x)