PageRenderTime 27ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 1ms

/pypy/module/gc/referents.py

https://bitbucket.org/pypy/pypy/
Python | 210 lines | 157 code | 27 blank | 26 comment | 43 complexity | 1a0d837b70ac4acc81d227a92c7af9a2 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from rpython.rlib import rgc
  2. from pypy.interpreter.baseobjspace import W_Root
  3. from pypy.interpreter.typedef import TypeDef
  4. from pypy.interpreter.gateway import unwrap_spec
  5. from pypy.interpreter.error import oefmt, wrap_oserror
  6. from rpython.rlib.objectmodel import we_are_translated
  7. class W_GcRef(W_Root):
  8. def __init__(self, gcref):
  9. self.gcref = gcref
  10. W_GcRef.typedef = TypeDef("GcRef")
  11. def try_cast_gcref_to_w_root(gcref):
  12. w_obj = rgc.try_cast_gcref_to_instance(W_Root, gcref)
  13. # Ignore the instances of W_Root that are not really valid as Python
  14. # objects. There is e.g. WeakrefLifeline in module/_weakref that
  15. # inherits from W_Root for internal reasons. Such instances don't
  16. # have a typedef at all (or have a null typedef after translation).
  17. if not we_are_translated():
  18. if not hasattr(w_obj, 'typedef'):
  19. return None
  20. else:
  21. if w_obj is None or not w_obj.typedef:
  22. return None
  23. return w_obj
  24. def wrap(space, gcref):
  25. w_obj = try_cast_gcref_to_w_root(gcref)
  26. if w_obj is None:
  27. w_obj = space.wrap(W_GcRef(gcref))
  28. return w_obj
  29. def unwrap(space, w_obj):
  30. if isinstance(w_obj, W_GcRef):
  31. gcref = w_obj.gcref
  32. else:
  33. gcref = rgc.cast_instance_to_gcref(w_obj)
  34. return gcref
  35. def missing_operation(space):
  36. return oefmt(space.w_NotImplementedError,
  37. "operation not implemented by this GC")
  38. # ____________________________________________________________
  39. class PathEntry(object):
  40. # PathEntries are nodes of a complete tree of all objects, but
  41. # built lazily (there is only one branch alive at any time).
  42. # Each node has a 'gcref' and the list of referents from this gcref.
  43. def __init__(self, prev, gcref, referents):
  44. self.prev = prev
  45. self.gcref = gcref
  46. self.referents = referents
  47. self.remaining = len(referents)
  48. def get_most_recent_w_obj(self):
  49. entry = self
  50. while entry is not None:
  51. if entry.gcref:
  52. w_obj = try_cast_gcref_to_w_root(entry.gcref)
  53. if w_obj is not None:
  54. return w_obj
  55. entry = entry.prev
  56. return None
  57. def do_get_referrers(w_arg):
  58. result_w = []
  59. gcarg = rgc.cast_instance_to_gcref(w_arg)
  60. roots = [gcref for gcref in rgc.get_rpy_roots() if gcref]
  61. head = PathEntry(None, rgc.NULL_GCREF, roots)
  62. while True:
  63. head.remaining -= 1
  64. if head.remaining >= 0:
  65. gcref = head.referents[head.remaining]
  66. if not rgc.get_gcflag_extra(gcref):
  67. # not visited so far
  68. if gcref == gcarg:
  69. w_obj = head.get_most_recent_w_obj()
  70. if w_obj is not None:
  71. result_w.append(w_obj) # found!
  72. rgc.toggle_gcflag_extra(gcref) # toggle twice
  73. rgc.toggle_gcflag_extra(gcref)
  74. head = PathEntry(head, gcref, rgc.get_rpy_referents(gcref))
  75. else:
  76. # no more referents to visit
  77. head = head.prev
  78. if head is None:
  79. break
  80. # done. Clear flags carefully
  81. rgc.toggle_gcflag_extra(gcarg)
  82. rgc.clear_gcflag_extra(roots)
  83. rgc.clear_gcflag_extra([gcarg])
  84. return result_w
  85. # ____________________________________________________________
  86. def _list_w_obj_referents(gcref, result_w):
  87. # Get all W_Root reachable directly from gcref, and add them to
  88. # the list 'result_w'.
  89. pending = [] # = list of all objects whose gcflag was toggled
  90. i = 0
  91. gcrefparent = gcref
  92. while True:
  93. for gcref in rgc.get_rpy_referents(gcrefparent):
  94. if rgc.get_gcflag_extra(gcref):
  95. continue
  96. rgc.toggle_gcflag_extra(gcref)
  97. pending.append(gcref)
  98. while i < len(pending):
  99. gcrefparent = pending[i]
  100. i += 1
  101. w_obj = try_cast_gcref_to_w_root(gcrefparent)
  102. if w_obj is not None:
  103. result_w.append(w_obj)
  104. else:
  105. break # jump back to the start of the outermost loop
  106. else:
  107. break # done
  108. for gcref in pending:
  109. rgc.toggle_gcflag_extra(gcref) # reset the gcflag_extra's
  110. # ____________________________________________________________
  111. def get_rpy_roots(space):
  112. lst = rgc.get_rpy_roots()
  113. if lst is None:
  114. raise missing_operation(space)
  115. return space.newlist([wrap(space, gcref) for gcref in lst if gcref])
  116. def get_rpy_referents(space, w_obj):
  117. """Return a list of all the referents, as reported by the GC.
  118. This is likely to contain a lot of GcRefs."""
  119. gcref = unwrap(space, w_obj)
  120. lst = rgc.get_rpy_referents(gcref)
  121. if lst is None:
  122. raise missing_operation(space)
  123. return space.newlist([wrap(space, gcref) for gcref in lst])
  124. def get_rpy_memory_usage(space, w_obj):
  125. """Return the memory usage of just the given object or GcRef.
  126. This does not include the internal structures of the object."""
  127. gcref = unwrap(space, w_obj)
  128. size = rgc.get_rpy_memory_usage(gcref)
  129. if size < 0:
  130. raise missing_operation(space)
  131. return space.wrap(size)
  132. def get_rpy_type_index(space, w_obj):
  133. """Return an integer identifying the RPython type of the given
  134. object or GcRef. The number starts at 1; it is an index in the
  135. file typeids.txt produced at translation."""
  136. gcref = unwrap(space, w_obj)
  137. index = rgc.get_rpy_type_index(gcref)
  138. if index < 0:
  139. raise missing_operation(space)
  140. return space.wrap(index)
  141. def get_objects(space):
  142. """Return a list of all app-level objects."""
  143. if not rgc.has_gcflag_extra():
  144. raise missing_operation(space)
  145. result_w = rgc.do_get_objects(try_cast_gcref_to_w_root)
  146. return space.newlist(result_w)
  147. def get_referents(space, args_w):
  148. """Return a list of objects directly referred to by any of the arguments.
  149. """
  150. if not rgc.has_gcflag_extra():
  151. raise missing_operation(space)
  152. result_w = []
  153. for w_obj in args_w:
  154. gcref = rgc.cast_instance_to_gcref(w_obj)
  155. _list_w_obj_referents(gcref, result_w)
  156. rgc.assert_no_more_gcflags()
  157. return space.newlist(result_w)
  158. def get_referrers(space, args_w):
  159. """Return the list of objects that directly refer to any of objs."""
  160. if not rgc.has_gcflag_extra():
  161. raise missing_operation(space)
  162. result_w = []
  163. for w_arg in args_w:
  164. result_w += do_get_referrers(w_arg)
  165. rgc.assert_no_more_gcflags()
  166. return space.newlist(result_w)
  167. @unwrap_spec(fd=int)
  168. def _dump_rpy_heap(space, fd):
  169. try:
  170. ok = rgc.dump_rpy_heap(fd)
  171. except OSError as e:
  172. raise wrap_oserror(space, e)
  173. if not ok:
  174. raise missing_operation(space)
  175. def get_typeids_z(space):
  176. a = rgc.get_typeids_z()
  177. s = ''.join([a[i] for i in range(len(a))])
  178. return space.wrap(s)
  179. def get_typeids_list(space):
  180. l = rgc.get_typeids_list()
  181. list_w = [space.wrap(l[i]) for i in range(len(l))]
  182. return space.newlist(list_w)