PageRenderTime 66ms CodeModel.GetById 21ms RepoModel.GetById 14ms app.codeStats 0ms

/pypy/module/cpyext/tupleobject.py

https://bitbucket.org/pypy/pypy/
Python | 213 lines | 146 code | 17 blank | 50 comment | 22 complexity | eca71dc081fe8403e514cde5e6c5909a MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from pypy.interpreter.error import oefmt
  2. from rpython.rtyper.lltypesystem import rffi, lltype
  3. from rpython.rlib.debug import fatalerror_notb
  4. from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL,
  5. build_type_checkers, PyVarObjectFields,
  6. cpython_struct, bootstrap_function)
  7. from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
  8. make_ref, from_ref, decref, incref, pyobj_has_w_obj,
  9. track_reference, make_typedescr, get_typedescr)
  10. from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
  11. from pypy.objspace.std.tupleobject import W_TupleObject
  12. ##
  13. ## Implementation of PyTupleObject
  14. ## ===============================
  15. ##
  16. ## Similar to stringobject.py. The reason is only the existance of
  17. ## W_SpecialisedTupleObject_ii and W_SpecialisedTupleObject_ff.
  18. ## These two PyPy classes implement getitem() by returning a freshly
  19. ## constructed W_IntObject or W_FloatObject. This is not compatible
  20. ## with PyTuple_GetItem, which returns a borrowed reference.
  21. ##
  22. ## So we use this more advanced (but also likely faster) solution:
  23. ## tuple_attach makes a real PyTupleObject with an array of N
  24. ## 'PyObject *', which are created immediately and own a reference.
  25. ## Then the macro PyTuple_GET_ITEM can be implemented like CPython.
  26. ##
  27. PyTupleObjectStruct = lltype.ForwardReference()
  28. PyTupleObject = lltype.Ptr(PyTupleObjectStruct)
  29. ObjectItems = rffi.CArray(PyObject)
  30. PyTupleObjectFields = PyVarObjectFields + \
  31. (("ob_item", ObjectItems),)
  32. cpython_struct("PyTupleObject", PyTupleObjectFields, PyTupleObjectStruct)
  33. @bootstrap_function
  34. def init_stringobject(space):
  35. "Type description of PyTupleObject"
  36. make_typedescr(space.w_tuple.layout.typedef,
  37. basestruct=PyTupleObject.TO,
  38. attach=tuple_attach,
  39. dealloc=tuple_dealloc,
  40. realize=tuple_realize)
  41. PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple")
  42. def tuple_check_ref(space, ref):
  43. w_type = from_ref(space, rffi.cast(PyObject, ref.c_ob_type))
  44. return (w_type is space.w_tuple or
  45. space.issubtype_w(w_type, space.w_tuple))
  46. def new_empty_tuple(space, length):
  47. """
  48. Allocate a PyTupleObject and its array of PyObject *, but without a
  49. corresponding interpreter object. The array may be mutated, until
  50. tuple_realize() is called. Refcount of the result is 1.
  51. """
  52. typedescr = get_typedescr(space.w_tuple.layout.typedef)
  53. py_obj = typedescr.allocate(space, space.w_tuple, length)
  54. py_tup = rffi.cast(PyTupleObject, py_obj)
  55. p = py_tup.c_ob_item
  56. for i in range(py_tup.c_ob_size):
  57. p[i] = lltype.nullptr(PyObject.TO)
  58. return py_obj
  59. def tuple_attach(space, py_obj, w_obj):
  60. """
  61. Fills a newly allocated PyTupleObject with the given tuple object. The
  62. buffer must not be modified.
  63. """
  64. items_w = space.fixedview(w_obj)
  65. py_tup = rffi.cast(PyTupleObject, py_obj)
  66. length = len(items_w)
  67. if py_tup.c_ob_size < length:
  68. raise oefmt(space.w_ValueError,
  69. "tuple_attach called on object with ob_size %d but trying to store %d",
  70. py_tup.c_ob_size, length)
  71. i = 0
  72. try:
  73. while i < length:
  74. py_tup.c_ob_item[i] = make_ref(space, items_w[i])
  75. i += 1
  76. except:
  77. while i > 0:
  78. i -= 1
  79. ob = py_tup.c_ob_item[i]
  80. py_tup.c_ob_item[i] = lltype.nullptr(PyObject.TO)
  81. decref(space, ob)
  82. raise
  83. def tuple_realize(space, py_obj):
  84. """
  85. Creates the tuple in the interpreter. The PyTupleObject must not
  86. be modified after this call. We check that it does not contain
  87. any NULLs at this point (which would correspond to half-broken
  88. W_TupleObjects).
  89. """
  90. py_tup = rffi.cast(PyTupleObject, py_obj)
  91. l = py_tup.c_ob_size
  92. p = py_tup.c_ob_item
  93. items_w = [None] * l
  94. for i in range(l):
  95. w_item = from_ref(space, p[i])
  96. if w_item is None:
  97. fatalerror_notb(
  98. "Fatal error in cpyext, CPython compatibility layer: "
  99. "converting a PyTupleObject into a W_TupleObject, "
  100. "but found NULLs as items")
  101. items_w[i] = w_item
  102. w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type))
  103. w_obj = space.allocate_instance(W_TupleObject, w_type)
  104. w_obj.__init__(items_w)
  105. track_reference(space, py_obj, w_obj)
  106. return w_obj
  107. @cpython_api([PyObject], lltype.Void, header=None)
  108. def tuple_dealloc(space, py_obj):
  109. """Frees allocated PyTupleObject resources.
  110. """
  111. py_tup = rffi.cast(PyTupleObject, py_obj)
  112. p = py_tup.c_ob_item
  113. for i in range(py_tup.c_ob_size):
  114. decref(space, p[i])
  115. from pypy.module.cpyext.object import _dealloc
  116. _dealloc(space, py_obj)
  117. #_______________________________________________________________________
  118. @cpython_api([Py_ssize_t], PyObject, result_is_ll=True)
  119. def PyTuple_New(space, size):
  120. return new_empty_tuple(space, size)
  121. @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
  122. def PyTuple_SetItem(space, ref, index, py_obj):
  123. # XXX this will not complain when changing tuples that have
  124. # already been realized as a W_TupleObject, but won't update the
  125. # W_TupleObject
  126. if not tuple_check_ref(space, ref):
  127. decref(space, py_obj)
  128. PyErr_BadInternalCall(space)
  129. ref = rffi.cast(PyTupleObject, ref)
  130. size = ref.c_ob_size
  131. if index < 0 or index >= size:
  132. decref(space, py_obj)
  133. raise oefmt(space.w_IndexError, "tuple assignment index out of range")
  134. old_ref = ref.c_ob_item[index]
  135. ref.c_ob_item[index] = py_obj # consumes a reference
  136. if old_ref:
  137. decref(space, old_ref)
  138. return 0
  139. @cpython_api([PyObject, Py_ssize_t], PyObject,
  140. result_borrowed=True, result_is_ll=True)
  141. def PyTuple_GetItem(space, ref, index):
  142. if not tuple_check_ref(space, ref):
  143. PyErr_BadInternalCall(space)
  144. ref = rffi.cast(PyTupleObject, ref)
  145. size = ref.c_ob_size
  146. if index < 0 or index >= size:
  147. raise oefmt(space.w_IndexError, "tuple index out of range")
  148. return ref.c_ob_item[index] # borrowed ref
  149. @cpython_api([PyObject], Py_ssize_t, error=-1)
  150. def PyTuple_Size(space, ref):
  151. """Take a pointer to a tuple object, and return the size of that tuple."""
  152. if not tuple_check_ref(space, ref):
  153. PyErr_BadInternalCall(space)
  154. ref = rffi.cast(PyTupleObject, ref)
  155. return ref.c_ob_size
  156. @cpython_api([PyObjectP, Py_ssize_t], rffi.INT_real, error=-1)
  157. def _PyTuple_Resize(space, p_ref, newsize):
  158. """Can be used to resize a tuple. newsize will be the new length of the tuple.
  159. Because tuples are supposed to be immutable, this should only be used if there
  160. is only one reference to the object. Do not use this if the tuple may already
  161. be known to some other part of the code. The tuple will always grow or shrink
  162. at the end. Think of this as destroying the old tuple and creating a new one,
  163. only more efficiently. Returns 0 on success. Client code should never
  164. assume that the resulting value of *p will be the same as before calling
  165. this function. If the object referenced by *p is replaced, the original
  166. *p is destroyed. On failure, returns -1 and sets *p to NULL, and
  167. raises MemoryError or SystemError."""
  168. ref = p_ref[0]
  169. if not tuple_check_ref(space, ref):
  170. PyErr_BadInternalCall(space)
  171. oldref = rffi.cast(PyTupleObject, ref)
  172. oldsize = oldref.c_ob_size
  173. p_ref[0] = new_empty_tuple(space, newsize)
  174. newref = rffi.cast(PyTupleObject, p_ref[0])
  175. try:
  176. if oldsize < newsize:
  177. to_cp = oldsize
  178. else:
  179. to_cp = newsize
  180. for i in range(to_cp):
  181. ob = oldref.c_ob_item[i]
  182. incref(space, ob)
  183. newref.c_ob_item[i] = ob
  184. except:
  185. decref(space, p_ref[0])
  186. p_ref[0] = lltype.nullptr(PyObject.TO)
  187. raise
  188. finally:
  189. decref(space, ref)
  190. return 0
  191. @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject)
  192. def PyTuple_GetSlice(space, w_obj, low, high):
  193. """Take a slice of the tuple pointed to by p from low to high and return it
  194. as a new tuple.
  195. """
  196. return space.getslice(w_obj, space.wrap(low), space.wrap(high))