PageRenderTime 86ms CodeModel.GetById 15ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/module/cpyext/typeobject.py

https://bitbucket.org/pypy/pypy/
Python | 911 lines | 722 code | 98 blank | 91 comment | 121 complexity | 2676b0ac75c12fc8543bb9fb53ce2649 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1import os
  2
  3from rpython.rlib import jit
  4from rpython.rlib.objectmodel import specialize
  5from rpython.rlib.rstring import rsplit
  6from rpython.rtyper.annlowlevel import llhelper
  7from rpython.rtyper.lltypesystem import rffi, lltype
  8
  9from pypy.interpreter.baseobjspace import W_Root, DescrMismatch
 10from pypy.interpreter.error import oefmt
 11from pypy.interpreter.typedef import (GetSetProperty, TypeDef,
 12        interp_attrproperty, interp_attrproperty, interp2app)
 13from pypy.module.__builtin__.abstractinst import abstract_issubclass_w
 14from pypy.module.cpyext import structmemberdefs
 15from pypy.module.cpyext.api import (
 16    cpython_api, cpython_struct, bootstrap_function, Py_ssize_t, Py_ssize_tP,
 17    generic_cpy_call, Py_TPFLAGS_READY, Py_TPFLAGS_READYING,
 18    Py_TPFLAGS_HEAPTYPE, METH_VARARGS, METH_KEYWORDS, CANNOT_FAIL,
 19    Py_TPFLAGS_HAVE_GETCHARBUFFER, build_type_checkers, StaticObjectBuilder,
 20    PyObjectFields, Py_TPFLAGS_BASETYPE, PyTypeObject, PyTypeObjectPtr)
 21from pypy.module.cpyext.methodobject import (W_PyCClassMethodObject,
 22    W_PyCWrapperObject, PyCFunction_NewEx, PyCFunction_typedef, PyMethodDef,
 23    W_PyCMethodObject, W_PyCFunctionObject)
 24from pypy.module.cpyext.modsupport import convert_method_defs
 25from pypy.module.cpyext.pyobject import (
 26    PyObject, make_ref, create_ref, from_ref, get_typedescr, make_typedescr,
 27    track_reference, Py_DecRef, as_pyobj)
 28from pypy.module.cpyext.slotdefs import (
 29    slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function)
 30from pypy.module.cpyext.state import State
 31from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne
 32from pypy.module.cpyext.typeobjectdefs import (
 33    PyGetSetDef, PyMemberDef, newfunc,
 34    PyNumberMethods, PyMappingMethods, PySequenceMethods, PyBufferProcs)
 35from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
 36
 37
 38WARN_ABOUT_MISSING_SLOT_FUNCTIONS = False
 39
 40PyType_Check, PyType_CheckExact = build_type_checkers("Type", "w_type")
 41
 42PyHeapTypeObjectStruct = lltype.ForwardReference()
 43PyHeapTypeObject = lltype.Ptr(PyHeapTypeObjectStruct)
 44PyHeapTypeObjectFields = (
 45    ("ht_type", PyTypeObject),
 46    ("ht_name", PyObject),
 47    ("as_number", PyNumberMethods),
 48    ("as_mapping", PyMappingMethods),
 49    ("as_sequence", PySequenceMethods),
 50    ("as_buffer", PyBufferProcs),
 51    )
 52cpython_struct("PyHeapTypeObject", PyHeapTypeObjectFields, PyHeapTypeObjectStruct,
 53               level=2)
 54
 55class W_GetSetPropertyEx(GetSetProperty):
 56    def __init__(self, getset, w_type):
 57        self.getset = getset
 58        self.name = rffi.charp2str(getset.c_name)
 59        self.w_type = w_type
 60        doc = set = get = None
 61        if doc:
 62            doc = rffi.charp2str(getset.c_doc)
 63        if getset.c_get:
 64            get = GettersAndSetters.getter.im_func
 65        if getset.c_set:
 66            set = GettersAndSetters.setter.im_func
 67        GetSetProperty.__init__(self, get, set, None, doc,
 68                                cls=None, use_closure=True,
 69                                tag="cpyext_1")
 70
 71def PyDescr_NewGetSet(space, getset, w_type):
 72    return space.wrap(W_GetSetPropertyEx(getset, w_type))
 73
 74class W_MemberDescr(GetSetProperty):
 75    name = 'member_descriptor'
 76    def __init__(self, member, w_type):
 77        self.member = member
 78        self.name = rffi.charp2str(member.c_name)
 79        self.w_type = w_type
 80        flags = rffi.cast(lltype.Signed, member.c_flags)
 81        doc = set = None
 82        if member.c_doc:
 83            doc = rffi.charp2str(member.c_doc)
 84        get = GettersAndSetters.member_getter.im_func
 85        del_ = GettersAndSetters.member_delete.im_func
 86        if not (flags & structmemberdefs.READONLY):
 87            set = GettersAndSetters.member_setter.im_func
 88        GetSetProperty.__init__(self, get, set, del_, doc,
 89                                cls=None, use_closure=True,
 90                                tag="cpyext_2")
 91
 92# change the typedef name
 93W_MemberDescr.typedef = TypeDef(
 94    "member_descriptor",
 95    __get__ = interp2app(GetSetProperty.descr_property_get),
 96    __set__ = interp2app(GetSetProperty.descr_property_set),
 97    __delete__ = interp2app(GetSetProperty.descr_property_del),
 98    __name__ = interp_attrproperty('name', cls=GetSetProperty),
 99    __objclass__ = GetSetProperty(GetSetProperty.descr_get_objclass),
100    __doc__ = interp_attrproperty('doc', cls=GetSetProperty),
101    )
102assert not W_MemberDescr.typedef.acceptable_as_base_class  # no __new__
103
104PyDescrObject = lltype.ForwardReference()
105PyDescrObjectPtr = lltype.Ptr(PyDescrObject)
106PyDescrObjectFields = PyObjectFields + (
107    ("d_type", PyTypeObjectPtr),
108    ("d_name", PyObject),
109    )
110cpython_struct("PyDescrObject", PyDescrObjectFields,
111               PyDescrObject)
112
113PyMemberDescrObjectStruct = lltype.ForwardReference()
114PyMemberDescrObject = lltype.Ptr(PyMemberDescrObjectStruct)
115PyMemberDescrObjectFields = PyDescrObjectFields + (
116    ("d_member", lltype.Ptr(PyMemberDef)),
117    )
118cpython_struct("PyMemberDescrObject", PyMemberDescrObjectFields,
119               PyMemberDescrObjectStruct, level=2)
120
121PyGetSetDescrObjectStruct = lltype.ForwardReference()
122PyGetSetDescrObject = lltype.Ptr(PyGetSetDescrObjectStruct)
123PyGetSetDescrObjectFields = PyDescrObjectFields + (
124    ("d_getset", lltype.Ptr(PyGetSetDef)),
125    )
126cpython_struct("PyGetSetDescrObject", PyGetSetDescrObjectFields,
127               PyGetSetDescrObjectStruct, level=2)
128
129PyMethodDescrObjectStruct = lltype.ForwardReference()
130PyMethodDescrObject = lltype.Ptr(PyMethodDescrObjectStruct)
131PyMethodDescrObjectFields = PyDescrObjectFields + (
132    ("d_method", lltype.Ptr(PyMethodDef)),
133    )
134cpython_struct("PyMethodDescrObject", PyMethodDescrObjectFields,
135               PyMethodDescrObjectStruct, level=2)
136
137@bootstrap_function
138def init_memberdescrobject(space):
139    make_typedescr(W_MemberDescr.typedef,
140                   basestruct=PyMemberDescrObject.TO,
141                   attach=memberdescr_attach,
142                   realize=memberdescr_realize,
143                   )
144    make_typedescr(W_GetSetPropertyEx.typedef,
145                   basestruct=PyGetSetDescrObject.TO,
146                   attach=getsetdescr_attach,
147                   )
148    make_typedescr(W_PyCClassMethodObject.typedef,
149                   basestruct=PyMethodDescrObject.TO,
150                   attach=methoddescr_attach,
151                   realize=classmethoddescr_realize,
152                   )
153    make_typedescr(W_PyCMethodObject.typedef,
154                   basestruct=PyMethodDescrObject.TO,
155                   attach=methoddescr_attach,
156                   realize=methoddescr_realize,
157                   )
158
159def memberdescr_attach(space, py_obj, w_obj):
160    """
161    Fills a newly allocated PyMemberDescrObject with the given W_MemberDescr
162    object. The values must not be modified.
163    """
164    py_memberdescr = rffi.cast(PyMemberDescrObject, py_obj)
165    # XXX assign to d_dname, d_type?
166    assert isinstance(w_obj, W_MemberDescr)
167    py_memberdescr.c_d_member = w_obj.member
168
169def memberdescr_realize(space, obj):
170    # XXX NOT TESTED When is this ever called?
171    member = rffi.cast(lltype.Ptr(PyMemberDef), obj)
172    w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
173    w_obj = space.allocate_instance(W_MemberDescr, w_type)
174    w_obj.__init__(member, w_type)
175    track_reference(space, obj, w_obj)
176    return w_obj
177
178def getsetdescr_attach(space, py_obj, w_obj):
179    """
180    Fills a newly allocated PyGetSetDescrObject with the given W_GetSetPropertyEx
181    object. The values must not be modified.
182    """
183    py_getsetdescr = rffi.cast(PyGetSetDescrObject, py_obj)
184    # XXX assign to d_dname, d_type?
185    assert isinstance(w_obj, W_GetSetPropertyEx)
186    py_getsetdescr.c_d_getset = w_obj.getset
187
188def methoddescr_attach(space, py_obj, w_obj):
189    py_methoddescr = rffi.cast(PyMethodDescrObject, py_obj)
190    # XXX assign to d_dname, d_type?
191    assert isinstance(w_obj, W_PyCFunctionObject)
192    py_methoddescr.c_d_method = w_obj.ml
193
194def classmethoddescr_realize(space, obj):
195    # XXX NOT TESTED When is this ever called?
196    method = rffi.cast(lltype.Ptr(PyMethodDef), obj)
197    w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
198    w_obj = space.allocate_instance(W_PyCClassMethodObject, w_type)
199    w_obj.__init__(space, method, w_type)
200    track_reference(space, obj, w_obj)
201    return w_obj
202
203def methoddescr_realize(space, obj):
204    # XXX NOT TESTED When is this ever called?
205    method = rffi.cast(lltype.Ptr(PyMethodDef), obj)
206    w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
207    w_obj = space.allocate_instance(W_PyCMethodObject, w_type)
208    w_obj.__init__(space, method, w_type)
209    track_reference(space, obj, w_obj)
210    return w_obj
211
212def convert_getset_defs(space, dict_w, getsets, w_type):
213    getsets = rffi.cast(rffi.CArrayPtr(PyGetSetDef), getsets)
214    if getsets:
215        i = -1
216        while True:
217            i = i + 1
218            getset = getsets[i]
219            name = getset.c_name
220            if not name:
221                break
222            name = rffi.charp2str(name)
223            w_descr = PyDescr_NewGetSet(space, getset, w_type)
224            dict_w[name] = w_descr
225
226def convert_member_defs(space, dict_w, members, w_type):
227    members = rffi.cast(rffi.CArrayPtr(PyMemberDef), members)
228    if members:
229        i = 0
230        while True:
231            member = members[i]
232            name = member.c_name
233            if not name:
234                break
235            name = rffi.charp2str(name)
236            w_descr = space.wrap(W_MemberDescr(member, w_type))
237            dict_w[name] = w_descr
238            i += 1
239
240def update_all_slots(space, w_type, pto):
241    # fill slots in pto
242    # Not very sure about it, but according to
243    # test_call_tp_dealloc_when_created_from_python, we should not
244    # overwrite slots that are already set: these ones are probably
245    # coming from a parent C type.
246
247    typedef = w_type.layout.typedef
248    for method_name, slot_name, slot_names, slot_func in slotdefs_for_tp_slots:
249        w_descr = w_type.lookup(method_name)
250        if w_descr is None:
251            # XXX special case iternext
252            continue
253
254        slot_func_helper = None
255
256        if slot_func is None and typedef is not None:
257            get_slot = get_slot_tp_function(space, typedef, slot_name)
258            if get_slot:
259                slot_func_helper = get_slot()
260        elif slot_func:
261            slot_func_helper = llhelper(slot_func.api_func.functype,
262                                        slot_func.api_func.get_wrapper(space))
263
264        if slot_func_helper is None:
265            if WARN_ABOUT_MISSING_SLOT_FUNCTIONS:
266                os.write(2, "%s defined by %s but no slot function defined!\n" % (
267                        method_name, w_type.getname(space)))
268            continue
269
270        # XXX special case wrapper-functions and use a "specific" slot func
271
272        if len(slot_names) == 1:
273            if not getattr(pto, slot_names[0]):
274                setattr(pto, slot_names[0], slot_func_helper)
275        elif (w_type.getname(space) in ('list', 'tuple') and
276              slot_names[0] == 'c_tp_as_number'):
277            # XXX hack - hwo can we generalize this? The problem is method
278            # names like __mul__ map to more than one slot, and we have no
279            # convenient way to indicate which slots CPython have filled
280            #
281            # We need at least this special case since Numpy checks that
282            # (list, tuple) do __not__ fill tp_as_number
283            pass
284        else:
285            assert len(slot_names) == 2
286            struct = getattr(pto, slot_names[0])
287            if not struct:
288                #assert not space.config.translating
289                assert not pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE
290                if slot_names[0] == 'c_tp_as_number':
291                    STRUCT_TYPE = PyNumberMethods
292                elif slot_names[0] == 'c_tp_as_sequence':
293                    STRUCT_TYPE = PySequenceMethods
294                else:
295                    raise AssertionError(
296                        "Structure not allocated: %s" % (slot_names[0],))
297                struct = lltype.malloc(STRUCT_TYPE, flavor='raw', zero=True)
298                setattr(pto, slot_names[0], struct)
299
300            if not getattr(struct, slot_names[1]):
301                setattr(struct, slot_names[1], slot_func_helper)
302
303def add_operators(space, dict_w, pto):
304    # XXX support PyObject_HashNotImplemented
305    for method_name, slot_names, wrapper_func, wrapper_func_kwds, doc in slotdefs_for_wrappers:
306        if method_name in dict_w:
307            continue
308        offset = [rffi.offsetof(lltype.typeOf(pto).TO, slot_names[0])]
309        if len(slot_names) == 1:
310            func = getattr(pto, slot_names[0])
311        else:
312            assert len(slot_names) == 2
313            struct = getattr(pto, slot_names[0])
314            if not struct:
315                continue
316            offset.append(rffi.offsetof(lltype.typeOf(struct).TO, slot_names[1]))
317            func = getattr(struct, slot_names[1])
318        func_voidp = rffi.cast(rffi.VOIDP, func)
319        if not func:
320            continue
321        if wrapper_func is None and wrapper_func_kwds is None:
322            continue
323        w_obj = W_PyCWrapperObject(space, pto, method_name, wrapper_func,
324                wrapper_func_kwds, doc, func_voidp, offset=offset)
325        dict_w[method_name] = space.wrap(w_obj)
326    if pto.c_tp_new:
327        add_tp_new_wrapper(space, dict_w, pto)
328
329@cpython_api([PyObject, PyObject, PyObject], PyObject, header=None)
330def tp_new_wrapper(space, self, w_args, w_kwds):
331    self_pytype = rffi.cast(PyTypeObjectPtr, self)
332    tp_new = self_pytype.c_tp_new
333
334    # Check that the user doesn't do something silly and unsafe like
335    # object.__new__(dict).  To do this, we check that the most
336    # derived base that's not a heap type is this type.
337    # XXX do it
338
339    args_w = space.fixedview(w_args)
340    w_subtype = args_w[0]
341    w_args = space.newtuple(args_w[1:])
342    if not space.is_true(w_kwds):
343        w_kwds = None
344
345    try:
346        subtype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_subtype))
347        w_obj = generic_cpy_call(space, tp_new, subtype, w_args, w_kwds)
348    finally:
349        Py_DecRef(space, w_subtype)
350    return w_obj
351
352@specialize.memo()
353def get_new_method_def(space):
354    state = space.fromcache(State)
355    if state.new_method_def:
356        return state.new_method_def
357    ptr = lltype.malloc(PyMethodDef, flavor="raw", zero=True,
358                        immortal=True)
359    ptr.c_ml_name = rffi.cast(rffi.CONST_CCHARP, rffi.str2charp("__new__"))
360    lltype.render_immortal(ptr.c_ml_name)
361    rffi.setintfield(ptr, 'c_ml_flags', METH_VARARGS | METH_KEYWORDS)
362    ptr.c_ml_doc = rffi.cast(rffi.CONST_CCHARP, rffi.str2charp(
363        "T.__new__(S, ...) -> a new object with type S, a subtype of T"))
364    lltype.render_immortal(ptr.c_ml_doc)
365    state.new_method_def = ptr
366    return ptr
367
368def setup_new_method_def(space):
369    ptr = get_new_method_def(space)
370    ptr.c_ml_meth = rffi.cast(PyCFunction_typedef,
371        llhelper(tp_new_wrapper.api_func.functype,
372                 tp_new_wrapper.api_func.get_wrapper(space)))
373
374def add_tp_new_wrapper(space, dict_w, pto):
375    if "__new__" in dict_w:
376        return
377    pyo = rffi.cast(PyObject, pto)
378    dict_w["__new__"] = PyCFunction_NewEx(space, get_new_method_def(space),
379                                          from_ref(space, pyo), None)
380
381def inherit_special(space, pto, base_pto):
382    # XXX missing: copy basicsize and flags in a magical way
383    # (minimally, if tp_basicsize is zero we copy it from the base)
384    if not pto.c_tp_basicsize:
385        pto.c_tp_basicsize = base_pto.c_tp_basicsize
386    if pto.c_tp_itemsize < base_pto.c_tp_itemsize:
387        pto.c_tp_itemsize = base_pto.c_tp_itemsize
388    flags = rffi.cast(lltype.Signed, pto.c_tp_flags)
389    base_object_pyo = make_ref(space, space.w_object)
390    base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo)
391    if base_pto != base_object_pto or flags & Py_TPFLAGS_HEAPTYPE:
392        if not pto.c_tp_new:
393            pto.c_tp_new = base_pto.c_tp_new
394    Py_DecRef(space, base_object_pyo)
395
396def check_descr(space, w_self, w_type):
397    if not space.isinstance_w(w_self, w_type):
398        raise DescrMismatch()
399
400class GettersAndSetters:
401    def getter(self, space, w_self):
402        assert isinstance(self, W_GetSetPropertyEx)
403        check_descr(space, w_self, self.w_type)
404        return generic_cpy_call(
405            space, self.getset.c_get, w_self,
406            self.getset.c_closure)
407
408    def setter(self, space, w_self, w_value):
409        assert isinstance(self, W_GetSetPropertyEx)
410        check_descr(space, w_self, self.w_type)
411        res = generic_cpy_call(
412            space, self.getset.c_set, w_self, w_value,
413            self.getset.c_closure)
414        if rffi.cast(lltype.Signed, res) < 0:
415            state = space.fromcache(State)
416            state.check_and_raise_exception()
417
418    def member_getter(self, space, w_self):
419        assert isinstance(self, W_MemberDescr)
420        check_descr(space, w_self, self.w_type)
421        pyref = make_ref(space, w_self)
422        try:
423            return PyMember_GetOne(
424                space, rffi.cast(rffi.CCHARP, pyref), self.member)
425        finally:
426            Py_DecRef(space, pyref)
427
428    def member_delete(self, space, w_self):
429        assert isinstance(self, W_MemberDescr)
430        check_descr(space, w_self, self.w_type)
431        pyref = make_ref(space, w_self)
432        try:
433            PyMember_SetOne(
434                space, rffi.cast(rffi.CCHARP, pyref), self.member, None)
435        finally:
436            Py_DecRef(space, pyref)
437
438    def member_setter(self, space, w_self, w_value):
439        assert isinstance(self, W_MemberDescr)
440        check_descr(space, w_self, self.w_type)
441        pyref = make_ref(space, w_self)
442        try:
443            PyMember_SetOne(
444                space, rffi.cast(rffi.CCHARP, pyref), self.member, w_value)
445        finally:
446            Py_DecRef(space, pyref)
447
448class W_PyCTypeObject(W_TypeObject):
449    @jit.dont_look_inside
450    def __init__(self, space, pto):
451        bases_w = space.fixedview(from_ref(space, pto.c_tp_bases))
452        dict_w = {}
453
454        add_operators(space, dict_w, pto)
455        convert_method_defs(space, dict_w, pto.c_tp_methods, self)
456        convert_getset_defs(space, dict_w, pto.c_tp_getset, self)
457        convert_member_defs(space, dict_w, pto.c_tp_members, self)
458
459        name = rffi.charp2str(pto.c_tp_name)
460        new_layout = (pto.c_tp_basicsize > rffi.sizeof(PyObject.TO) or
461                      pto.c_tp_itemsize > 0)
462
463        W_TypeObject.__init__(self, space, name,
464            bases_w or [space.w_object], dict_w, force_new_layout=new_layout)
465        self.flag_cpytype = True
466        self.flag_heaptype = False
467        # if a sequence or a mapping, then set the flag to force it
468        if pto.c_tp_as_sequence and pto.c_tp_as_sequence.c_sq_item:
469            self.flag_map_or_seq = 'S'
470        elif (pto.c_tp_as_mapping and pto.c_tp_as_mapping.c_mp_subscript and
471              not (pto.c_tp_as_sequence and pto.c_tp_as_sequence.c_sq_slice)):
472            self.flag_map_or_seq = 'M'
473        if pto.c_tp_doc:
474            self.w_doc = space.wrap(rffi.charp2str(pto.c_tp_doc))
475
476@bootstrap_function
477def init_typeobject(space):
478    make_typedescr(space.w_type.layout.typedef,
479                   basestruct=PyTypeObject,
480                   alloc=type_alloc,
481                   attach=type_attach,
482                   realize=type_realize,
483                   dealloc=type_dealloc)
484
485@cpython_api([PyObject], lltype.Void, header=None)
486def subtype_dealloc(space, obj):
487    pto = obj.c_ob_type
488    base = pto
489    this_func_ptr = llhelper(subtype_dealloc.api_func.functype,
490            subtype_dealloc.api_func.get_wrapper(space))
491    while base.c_tp_dealloc == this_func_ptr:
492        base = base.c_tp_base
493        assert base
494    dealloc = base.c_tp_dealloc
495    # XXX call tp_del if necessary
496    generic_cpy_call(space, dealloc, obj)
497    # XXX cpy decrefs the pto here but we do it in the base-dealloc
498    # hopefully this does not clash with the memory model assumed in
499    # extension modules
500
501@cpython_api([PyObject, Py_ssize_tP], lltype.Signed, header=None,
502             error=CANNOT_FAIL)
503def bf_segcount(space, w_obj, ref):
504    if ref:
505        ref[0] = space.len_w(w_obj)
506    return 1
507
508@cpython_api([PyObject, Py_ssize_t, rffi.VOIDPP], lltype.Signed,
509             header=None, error=-1)
510def bf_getreadbuffer(space, w_buf, segment, ref):
511    if segment != 0:
512        raise oefmt(space.w_SystemError,
513                    "accessing non-existent segment")
514    buf = space.readbuf_w(w_buf)
515    address = buf.get_raw_address()
516    ref[0] = address
517    return len(buf)
518
519@cpython_api([PyObject, Py_ssize_t, rffi.CCHARPP], lltype.Signed,
520             header=None, error=-1)
521def bf_getcharbuffer(space, w_buf, segment, ref):
522    return bf_getreadbuffer(space, w_buf, segment, rffi.cast(rffi.VOIDPP, ref))
523
524@cpython_api([PyObject, Py_ssize_t, rffi.VOIDPP], lltype.Signed,
525             header=None, error=-1)
526def bf_getwritebuffer(space, w_buf, segment, ref):
527    if segment != 0:
528        raise oefmt(space.w_SystemError,
529                    "accessing non-existent segment")
530
531    buf = space.writebuf_w(w_buf)
532    ref[0] = buf.get_raw_address()
533    return len(buf)
534
535@cpython_api([PyObject, Py_ssize_t, rffi.VOIDPP], lltype.Signed,
536             header=None, error=-1)
537def str_getreadbuffer(space, w_str, segment, ref):
538    from pypy.module.cpyext.bytesobject import PyString_AsString
539    if segment != 0:
540        raise oefmt(space.w_SystemError,
541                    "accessing non-existent string segment")
542    pyref = make_ref(space, w_str)
543    ref[0] = PyString_AsString(space, pyref)
544    # Stolen reference: the object has better exist somewhere else
545    Py_DecRef(space, pyref)
546    return space.len_w(w_str)
547
548@cpython_api([PyObject, Py_ssize_t, rffi.CCHARPP], lltype.Signed,
549             header=None, error=-1)
550def str_getcharbuffer(space, w_buf, segment, ref):
551    return str_getreadbuffer(space, w_buf, segment, rffi.cast(rffi.VOIDPP, ref))
552
553@cpython_api([PyObject, Py_ssize_t, rffi.VOIDPP], lltype.Signed,
554             header=None, error=-1)
555def buf_getreadbuffer(space, pyref, segment, ref):
556    from pypy.module.cpyext.bufferobject import PyBufferObject
557    if segment != 0:
558        raise oefmt(space.w_SystemError,
559                    "accessing non-existent buffer segment")
560    py_buf = rffi.cast(PyBufferObject, pyref)
561    ref[0] = py_buf.c_b_ptr
562    return py_buf.c_b_size
563
564@cpython_api([PyObject, Py_ssize_t, rffi.CCHARPP], lltype.Signed,
565             header=None, error=-1)
566def buf_getcharbuffer(space, w_buf, segment, ref):
567    return buf_getreadbuffer(space, w_buf, segment, rffi.cast(rffi.VOIDPP, ref))
568
569def setup_buffer_procs(space, w_type, pto):
570    bufspec = w_type.layout.typedef.buffer
571    if bufspec is None:
572        # not a buffer
573        return
574    c_buf = lltype.malloc(PyBufferProcs, flavor='raw', zero=True)
575    lltype.render_immortal(c_buf)
576    c_buf.c_bf_getsegcount = llhelper(bf_segcount.api_func.functype,
577                                      bf_segcount.api_func.get_wrapper(space))
578    if space.is_w(w_type, space.w_str):
579        # Special case: str doesn't support get_raw_address(), so we have a
580        # custom get*buffer that instead gives the address of the char* in the
581        # PyBytesObject*!
582        c_buf.c_bf_getreadbuffer = llhelper(
583            str_getreadbuffer.api_func.functype,
584            str_getreadbuffer.api_func.get_wrapper(space))
585        c_buf.c_bf_getcharbuffer = llhelper(
586            str_getcharbuffer.api_func.functype,
587            str_getcharbuffer.api_func.get_wrapper(space))
588    elif space.is_w(w_type, space.w_buffer):
589        # Special case: we store a permanent address on the cpyext wrapper,
590        # so we'll reuse that.
591        # Note: we could instead store a permanent address on the buffer object,
592        # and use get_raw_address()
593        c_buf.c_bf_getreadbuffer = llhelper(
594            buf_getreadbuffer.api_func.functype,
595            buf_getreadbuffer.api_func.get_wrapper(space))
596        c_buf.c_bf_getcharbuffer = llhelper(
597            buf_getcharbuffer.api_func.functype,
598            buf_getcharbuffer.api_func.get_wrapper(space))
599    else:
600        # use get_raw_address()
601        c_buf.c_bf_getreadbuffer = llhelper(bf_getreadbuffer.api_func.functype,
602                                    bf_getreadbuffer.api_func.get_wrapper(space))
603        c_buf.c_bf_getcharbuffer = llhelper(bf_getcharbuffer.api_func.functype,
604                                    bf_getcharbuffer.api_func.get_wrapper(space))
605        if bufspec == 'read-write':
606            c_buf.c_bf_getwritebuffer = llhelper(
607                bf_getwritebuffer.api_func.functype,
608                bf_getwritebuffer.api_func.get_wrapper(space))
609    pto.c_tp_as_buffer = c_buf
610    pto.c_tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER
611
612@cpython_api([PyObject], lltype.Void, header=None)
613def type_dealloc(space, obj):
614    from pypy.module.cpyext.object import _dealloc
615    obj_pto = rffi.cast(PyTypeObjectPtr, obj)
616    base_pyo = rffi.cast(PyObject, obj_pto.c_tp_base)
617    Py_DecRef(space, obj_pto.c_tp_bases)
618    Py_DecRef(space, obj_pto.c_tp_mro)
619    Py_DecRef(space, obj_pto.c_tp_cache) # let's do it like cpython
620    Py_DecRef(space, obj_pto.c_tp_dict)
621    if obj_pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE:
622        heaptype = rffi.cast(PyHeapTypeObject, obj)
623        Py_DecRef(space, heaptype.c_ht_name)
624        Py_DecRef(space, base_pyo)
625        _dealloc(space, obj)
626
627
628def type_alloc(space, w_metatype, itemsize=0):
629    metatype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_metatype))
630    # Don't increase refcount for non-heaptypes
631    if metatype:
632        flags = rffi.cast(lltype.Signed, metatype.c_tp_flags)
633        if not flags & Py_TPFLAGS_HEAPTYPE:
634            Py_DecRef(space, w_metatype)
635
636    heaptype = lltype.malloc(PyHeapTypeObject.TO,
637                             flavor='raw', zero=True,
638                             add_memory_pressure=True)
639    pto = heaptype.c_ht_type
640    pto.c_ob_refcnt = 1
641    pto.c_ob_pypy_link = 0
642    pto.c_ob_type = metatype
643    pto.c_tp_flags |= Py_TPFLAGS_HEAPTYPE
644    pto.c_tp_as_number = heaptype.c_as_number
645    pto.c_tp_as_sequence = heaptype.c_as_sequence
646    pto.c_tp_as_mapping = heaptype.c_as_mapping
647    pto.c_tp_as_buffer = heaptype.c_as_buffer
648    pto.c_tp_basicsize = -1 # hopefully this makes malloc bail out
649    pto.c_tp_itemsize = 0
650
651    return rffi.cast(PyObject, heaptype)
652
653def type_attach(space, py_obj, w_type):
654    """
655    Fills a newly allocated PyTypeObject from an existing type.
656    """
657    from pypy.module.cpyext.object import PyObject_Free
658
659    assert isinstance(w_type, W_TypeObject)
660
661    pto = rffi.cast(PyTypeObjectPtr, py_obj)
662
663    typedescr = get_typedescr(w_type.layout.typedef)
664
665    # dealloc
666    if space.gettypeobject(w_type.layout.typedef) is w_type:
667        # only for the exact type, like 'space.w_tuple' or 'space.w_list'
668        pto.c_tp_dealloc = typedescr.get_dealloc(space)
669    else:
670        # for all subtypes, use subtype_dealloc()
671        pto.c_tp_dealloc = llhelper(
672            subtype_dealloc.api_func.functype,
673            subtype_dealloc.api_func.get_wrapper(space))
674    if space.is_w(w_type, space.w_str):
675        pto.c_tp_itemsize = 1
676    elif space.is_w(w_type, space.w_tuple):
677        pto.c_tp_itemsize = rffi.sizeof(PyObject)
678    # buffer protocol
679    setup_buffer_procs(space, w_type, pto)
680
681    pto.c_tp_free = llhelper(PyObject_Free.api_func.functype,
682            PyObject_Free.api_func.get_wrapper(space))
683    pto.c_tp_alloc = llhelper(PyType_GenericAlloc.api_func.functype,
684            PyType_GenericAlloc.api_func.get_wrapper(space))
685    builder = space.fromcache(StaticObjectBuilder)
686    if ((pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE) != 0
687            and builder.cpyext_type_init is None):
688            # this ^^^ is not None only during startup of cpyext.  At that
689            # point we might get into troubles by doing make_ref() when
690            # things are not initialized yet.  So in this case, simply use
691            # str2charp() and "leak" the string.
692        w_typename = space.getattr(w_type, space.wrap('__name__'))
693        heaptype = rffi.cast(PyHeapTypeObject, pto)
694        heaptype.c_ht_name = make_ref(space, w_typename)
695        from pypy.module.cpyext.bytesobject import PyString_AsString
696        pto.c_tp_name = PyString_AsString(space, heaptype.c_ht_name)
697    else:
698        pto.c_tp_name = rffi.str2charp(w_type.name)
699    # uninitialized fields:
700    # c_tp_print
701    # XXX implement
702    # c_tp_compare and the following fields (see http://docs.python.org/c-api/typeobj.html )
703    w_base = best_base(space, w_type.bases_w)
704    pto.c_tp_base = rffi.cast(PyTypeObjectPtr, make_ref(space, w_base))
705
706    if builder.cpyext_type_init is not None:
707        builder.cpyext_type_init.append((pto, w_type))
708    else:
709        finish_type_1(space, pto)
710        finish_type_2(space, pto, w_type)
711
712    pto.c_tp_basicsize = rffi.sizeof(typedescr.basestruct)
713    if pto.c_tp_base:
714        if pto.c_tp_base.c_tp_basicsize > pto.c_tp_basicsize:
715            pto.c_tp_basicsize = pto.c_tp_base.c_tp_basicsize
716        if pto.c_tp_itemsize < pto.c_tp_base.c_tp_itemsize:
717            pto.c_tp_itemsize = pto.c_tp_base.c_tp_itemsize
718
719    # will be filled later on with the correct value
720    # may not be 0
721    if space.is_w(w_type, space.w_object):
722        pto.c_tp_new = rffi.cast(newfunc, 1)
723    update_all_slots(space, w_type, pto)
724    pto.c_tp_flags |= Py_TPFLAGS_READY
725    return pto
726
727def py_type_ready(space, pto):
728    if pto.c_tp_flags & Py_TPFLAGS_READY:
729        return
730    type_realize(space, rffi.cast(PyObject, pto))
731
732@cpython_api([PyTypeObjectPtr], rffi.INT_real, error=-1)
733def PyType_Ready(space, pto):
734    py_type_ready(space, pto)
735    return 0
736
737def type_realize(space, py_obj):
738    pto = rffi.cast(PyTypeObjectPtr, py_obj)
739    assert pto.c_tp_flags & Py_TPFLAGS_READY == 0
740    assert pto.c_tp_flags & Py_TPFLAGS_READYING == 0
741    pto.c_tp_flags |= Py_TPFLAGS_READYING
742    try:
743        w_obj = _type_realize(space, py_obj)
744    finally:
745        name = rffi.charp2str(pto.c_tp_name)
746        pto.c_tp_flags &= ~Py_TPFLAGS_READYING
747    pto.c_tp_flags |= Py_TPFLAGS_READY
748    return w_obj
749
750def solid_base(space, w_type):
751    typedef = w_type.layout.typedef
752    return space.gettypeobject(typedef)
753
754def best_base(space, bases_w):
755    if not bases_w:
756        return None
757    return find_best_base(bases_w)
758
759def inherit_slots(space, pto, w_base):
760    # XXX missing: nearly everything
761    base_pyo = make_ref(space, w_base)
762    try:
763        base = rffi.cast(PyTypeObjectPtr, base_pyo)
764        if not pto.c_tp_dealloc:
765            pto.c_tp_dealloc = base.c_tp_dealloc
766        if not pto.c_tp_init:
767            pto.c_tp_init = base.c_tp_init
768        if not pto.c_tp_alloc:
769            pto.c_tp_alloc = base.c_tp_alloc
770        # XXX check for correct GC flags!
771        if not pto.c_tp_free:
772            pto.c_tp_free = base.c_tp_free
773        if not pto.c_tp_setattro:
774            pto.c_tp_setattro = base.c_tp_setattro
775        if not pto.c_tp_getattro:
776            pto.c_tp_getattro = base.c_tp_getattro
777    finally:
778        Py_DecRef(space, base_pyo)
779
780def _type_realize(space, py_obj):
781    """
782    Creates an interpreter type from a PyTypeObject structure.
783    """
784    # missing:
785    # unsupported:
786    # tp_mro, tp_subclasses
787    py_type = rffi.cast(PyTypeObjectPtr, py_obj)
788
789    if not py_type.c_tp_base:
790        # borrowed reference, but w_object is unlikely to disappear
791        base = as_pyobj(space, space.w_object)
792        py_type.c_tp_base = rffi.cast(PyTypeObjectPtr, base)
793
794    finish_type_1(space, py_type)
795
796    if py_type.c_ob_type:
797        w_metatype = from_ref(space, rffi.cast(PyObject, py_type.c_ob_type))
798    else:
799        # Somehow the tp_base type is created with no ob_type, notably
800        # PyString_Type and PyBaseString_Type
801        # While this is a hack, cpython does it as well.
802        w_metatype = space.w_type
803
804    w_obj = space.allocate_instance(W_PyCTypeObject, w_metatype)
805    track_reference(space, py_obj, w_obj)
806    w_obj.__init__(space, py_type)
807    w_obj.ready()
808
809    finish_type_2(space, py_type, w_obj)
810    # inheriting tp_as_* slots
811    base = py_type.c_tp_base
812    if base:
813        if not py_type.c_tp_as_number: py_type.c_tp_as_number = base.c_tp_as_number
814        if not py_type.c_tp_as_sequence: py_type.c_tp_as_sequence = base.c_tp_as_sequence
815        if not py_type.c_tp_as_mapping: py_type.c_tp_as_mapping = base.c_tp_as_mapping
816        if not py_type.c_tp_as_buffer: py_type.c_tp_as_buffer = base.c_tp_as_buffer
817
818    return w_obj
819
820def finish_type_1(space, pto):
821    """
822    Sets up tp_bases, necessary before creating the interpreter type.
823    """
824    base = pto.c_tp_base
825    base_pyo = rffi.cast(PyObject, pto.c_tp_base)
826    if base and not base.c_tp_flags & Py_TPFLAGS_READY:
827        name = rffi.charp2str(base.c_tp_name)
828        type_realize(space, base_pyo)
829    if base and not pto.c_ob_type: # will be filled later
830        pto.c_ob_type = base.c_ob_type
831    if not pto.c_tp_bases:
832        if not base:
833            bases = space.newtuple([])
834        else:
835            bases = space.newtuple([from_ref(space, base_pyo)])
836        pto.c_tp_bases = make_ref(space, bases)
837
838def finish_type_2(space, pto, w_obj):
839    """
840    Sets up other attributes, when the interpreter type has been created.
841    """
842    pto.c_tp_mro = make_ref(space, space.newtuple(w_obj.mro_w))
843    base = pto.c_tp_base
844    if base:
845        inherit_special(space, pto, base)
846    for w_base in space.fixedview(from_ref(space, pto.c_tp_bases)):
847        inherit_slots(space, pto, w_base)
848
849    if not pto.c_tp_setattro:
850        from pypy.module.cpyext.object import PyObject_GenericSetAttr
851        pto.c_tp_setattro = llhelper(
852            PyObject_GenericSetAttr.api_func.functype,
853            PyObject_GenericSetAttr.api_func.get_wrapper(space))
854
855    if not pto.c_tp_getattro:
856        from pypy.module.cpyext.object import PyObject_GenericGetAttr
857        pto.c_tp_getattro = llhelper(
858            PyObject_GenericGetAttr.api_func.functype,
859            PyObject_GenericGetAttr.api_func.get_wrapper(space))
860
861    if w_obj.is_cpytype():
862        Py_DecRef(space, pto.c_tp_dict)
863    w_dict = w_obj.getdict(space)
864    pto.c_tp_dict = make_ref(space, w_dict)
865
866@cpython_api([PyTypeObjectPtr, PyTypeObjectPtr], rffi.INT_real, error=CANNOT_FAIL)
867def PyType_IsSubtype(space, a, b):
868    """Return true if a is a subtype of b.
869    """
870    w_type1 = from_ref(space, rffi.cast(PyObject, a))
871    w_type2 = from_ref(space, rffi.cast(PyObject, b))
872    return int(abstract_issubclass_w(space, w_type1, w_type2)) #XXX correct?
873
874@cpython_api([PyTypeObjectPtr, Py_ssize_t], PyObject, result_is_ll=True)
875def PyType_GenericAlloc(space, type, nitems):
876    from pypy.module.cpyext.object import _PyObject_NewVar
877    return _PyObject_NewVar(space, type, nitems)
878
879@cpython_api([PyTypeObjectPtr, PyObject, PyObject], PyObject)
880def PyType_GenericNew(space, type, w_args, w_kwds):
881    return generic_cpy_call(
882        space, type.c_tp_alloc, type, 0)
883
884@cpython_api([PyTypeObjectPtr, PyObject], PyObject, error=CANNOT_FAIL,
885             result_borrowed=True)
886def _PyType_Lookup(space, type, w_name):
887    """Internal API to look for a name through the MRO.
888    This returns a borrowed reference, and doesn't set an exception!"""
889    w_type = from_ref(space, rffi.cast(PyObject, type))
890    assert isinstance(w_type, W_TypeObject)
891
892    if not space.isinstance_w(w_name, space.w_str):
893        return None
894    name = space.str_w(w_name)
895    w_obj = w_type.lookup(name)
896    # this assumes that w_obj is not dynamically created, but will stay alive
897    # until w_type is modified or dies.  Assuming this, we return a borrowed ref
898    return w_obj
899
900@cpython_api([PyTypeObjectPtr], lltype.Void)
901def PyType_Modified(space, w_obj):
902    """Invalidate the internal lookup cache for the type and all of its
903    subtypes.  This function must be called after any manual
904    modification of the attributes or base classes of the type.
905    """
906    # Invalidate the type cache in case of a builtin type.
907    if not isinstance(w_obj, W_TypeObject):
908        return
909    if w_obj.is_cpytype():
910        w_obj.mutated(None)
911