File: interp_cpy_compat.py

package info (click to toggle)
pypy3 7.3.11%2Bdfsg-2%2Bdeb12u3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 201,024 kB
  • sloc: python: 1,950,308; ansic: 517,580; sh: 21,417; asm: 14,419; cpp: 4,263; makefile: 4,228; objc: 761; xml: 530; exp: 499; javascript: 314; pascal: 244; lisp: 45; csh: 11; awk: 4
file content (233 lines) | stat: -rw-r--r-- 9,769 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rlib.rarithmetic import widen
from rpython.rlib import rgc
from rpython.rlib.unroll import unrolling_iterable
#
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.baseobjspace import W_Root, DescrMismatch
from pypy.interpreter.gateway import interp2app
from pypy.interpreter.typedef import TypeDef, GetSetProperty
#
from pypy.module.cpyext import pyobject
from pypy.module.cpyext.methodobject import PyMethodDef, PyCFunction
from pypy.module.cpyext.modsupport import convert_method_defs
from pypy.module.cpyext.api import PyTypeObjectPtr, cts as cpyts, generic_cpy_call
from pypy.module.cpyext import structmemberdefs
from pypy.module.cpyext.state import State
#
from pypy.module._hpy_universal.apiset import API
from pypy.module._hpy_universal.interp_descr import W_HPyMemberDescriptor
from pypy.module._hpy_universal.interp_type import W_HPyObject

@API.func("HPy HPy_FromPyObject(HPyContext *ctx, void *obj)", cpyext=True)
def HPy_FromPyObject(space, handles, ctx, obj):
    if not obj:
        return 0  # HPy_NULL
    w_obj = pyobject.from_ref(space, rffi.cast(pyobject.PyObject, obj))
    return handles.new(w_obj)

@API.func("void *HPy_AsPyObject(HPyContext *ctx, HPy h)", cpyext=True)
def HPy_AsPyObject(space, handles, ctx, h):
    if h == 0:
        return rffi.cast(rffi.VOIDP, 0)
    w_obj = handles.deref(h)
    pyobj = pyobject.make_ref(space, w_obj)
    return rffi.cast(rffi.VOIDP, pyobj)

# ~~~ legacy_methods ~~~
# This is used by both modules and types

def attach_legacy_methods(space, pymethods, w_obj, modname, type_name):
    """
    pymethods is passed as a void*, but it is expected to be a PyMethodDef[].
    Wrap its items into the proper cpyext.W_*Function objects, and attach them
    to w_obj (which can be either a module or a type).
    """
    pymethods = cpyts.cast('PyMethodDef*', pymethods)
    dict_w = {}
    convert_method_defs(space, dict_w, pymethods, None, w_obj, modname, type_name)
    for key, w_func in dict_w.items():
        space.setattr(w_obj, space.newtext(key), w_func)

# ~~~ legacy_members ~~~
# This is used only by types

def attach_legacy_members(space, pymembers, w_type, type_name):
    PyMemberDef = cpyts.gettype('PyMemberDef')
    pymembers = rffi.cast(rffi.CArrayPtr(PyMemberDef), pymembers)
    if not pymembers:
        return
    i = 0
    while True:
        pymember = pymembers[i]
        name = pymember.c_name
        if not name:
            break
        i += 1
        name = rffi.constcharp2str(pymember.c_name)
        doc = rffi.constcharp2str(pymember.c_doc) if pymember.c_doc else None
        offset = rffi.cast(lltype.Signed, pymember.c_offset)
        #
        # NOTE: the following works only because the HPy's
        # HPyMember_FieldType.* happen to have the same numeric value as
        # cpyexts' structmemberdefs.T_*
        kind = rffi.cast(lltype.Signed, pymember.c_type)
        #
        # XXX: write tests about the other flags? I think that READ_RESTRICTED
        # and WRITE_RESTRICTED are not used nowadays?
        flags = rffi.cast(lltype.Signed, pymember.c_flags)
        is_readonly = flags & structmemberdefs.READONLY
        w_member = W_HPyMemberDescriptor(w_type, kind, name, doc, offset, is_readonly)
        w_type.setdictvalue(space, name, w_member)

# ~~~ legacy_getset ~~~
# This is used only by types

def check_descr(space, w_self, w_type):
    if not space.isinstance_w(w_self, w_type):
        raise DescrMismatch()

# Copied from cpyext.typeobject, but modified the call to use get_pyobject
class GettersAndSetters:
    def getter(self, space, w_self):
        assert isinstance(self, W_GetSetPropertyHPy)
        assert isinstance(w_self, W_HPyObject)
        check_descr(space, w_self, self.w_type)
        return generic_cpy_call(
            space, self.getset.c_get, w_self.get_pyobject(),
            self.getset.c_closure)

    def setter(self, space, w_self, w_value):
        assert isinstance(self, W_GetSetPropertyHPy)
        assert isinstance(w_self, W_HPyObject)
        assert isinstance(w_value, W_HPyObject)
        check_descr(space, w_self, self.w_type)
        res = generic_cpy_call(
            space, self.getset.c_set,
            w_self.get_pyobject(), w_value.get_pyobject(),
            self.getset.c_closure)
        if rffi.cast(lltype.Signed, res) < 0:
            state = space.fromcache(State)
            state.check_and_raise_exception()

    def deleter(self, space, w_self):
        assert isinstance(self, W_GetSetPropertyHPy)
        assert isinstance(w_self, W_HPyObject)
        check_descr(space, w_self, self.w_type)
        res = generic_cpy_call(
            space, self.getset.c_set, w_self.get_pyobject(), None,
            self.getset.c_closure)
        if rffi.cast(lltype.Signed, res) < 0:
            state = space.fromcache(State)
            state.check_and_raise_exception()

# Copied from cpyext.typeobject, but use the local GettersAndSetters
class W_GetSetPropertyHPy(GetSetProperty):
    def __init__(self, getset, w_type):
        self.getset = getset
        self.w_type = w_type
        doc = fset = fget = fdel = None
        if doc:
            # XXX dead code?
            doc = rffi.constcharp2str(getset.c_doc)
        if getset.c_get:
            fget = GettersAndSetters.getter.im_func
        if getset.c_set:
            fset = GettersAndSetters.setter.im_func
            fdel = GettersAndSetters.deleter.im_func
        GetSetProperty.__init__(self, fget, fset, fdel, doc,
                                cls=None, use_closure=True,
                                tag="HPy_legacy")
        self.name = rffi.constcharp2str(getset.c_name)

    def readonly_attribute(self, space):   # overwritten
        raise oefmt(space.w_AttributeError,
            "attribute '%s' of '%N' objects is not writable",
            self.name, self.w_type)

def attach_legacy_getsets(space, pygetsets, w_type):
    from pypy.module.cpyext.typeobjectdefs import PyGetSetDef
    getsets = rffi.cast(rffi.CArrayPtr(PyGetSetDef), pygetsets)
    if getsets:
        i = -1
        while True:
            i = i + 1
            getset = getsets[i]
            name = getset.c_name
            if not name:
                break
            name = rffi.constcharp2str(name)
            w_descr = W_GetSetPropertyHPy(getset, w_type)
            space.setattr(w_type, space.newtext(name), w_descr)

# ~~~ legacy_slots ~~~
# This is used only by types

def make_slot_wrappers_table():
    from pypy.module.cpyext.typeobject import SLOT_TABLE
    from pypy.module.cpyext.slotdefs import slotdefs
    table = [] # (slotnum, method_name, doc, wrapper_class)
    for typeslot in slotdefs:
        # ignore pypy-specific slots
        if typeslot.slot_names[-1] in ('c_bf_getbuffer',
                                       'c_bf_getreadbuffer',
                                       'c_bf_getwritebuffer'):
            continue
        for num, membername, slotname, TARGET in SLOT_TABLE:
            if typeslot.slot_names[-1] == slotname:
                ts = typeslot
                table.append((num, ts.method_name, ts.doc, ts.wrapper_class))
                break
        else:
            assert False, 'Cannot find slot num for typeslot %s' % typeslot.slot_name
    return table
SLOT_WRAPPERS_TABLE = unrolling_iterable(make_slot_wrappers_table())

def attach_legacy_slots_to_type(space, w_type, c_legacy_slots, needs_hpytype_dealloc):
    from pypy.module.cpyext.slotdefs import wrap_unaryfunc
    slotdefs = rffi.cast(rffi.CArrayPtr(cpyts.gettype('PyType_Slot')), c_legacy_slots)
    i = 0
    type_name = w_type.getqualname(space)
    while True:
        slotdef = slotdefs[i]
        slotnum = rffi.cast(lltype.Signed, slotdef.c_slot)
        if slotnum == 0:
            break
        elif slotnum == cpyts.macros['Py_tp_methods']:
            attach_legacy_methods(space, slotdef.c_pfunc, w_type, None, type_name)
        elif slotnum == cpyts.macros['Py_tp_members']:
            attach_legacy_members(space, slotdef.c_pfunc, w_type, type_name)
        elif slotnum == cpyts.macros['Py_tp_getset']:
            attach_legacy_getsets(space, slotdef.c_pfunc, w_type)
        elif slotnum == cpyts.macros['Py_tp_dealloc']:
            from pypy.module.cpyext.pyobject import as_pyobj
            from pypy.module.cpyext.typeobjectdefs import destructor
            if needs_hpytype_dealloc:
                raise oefmt(space.w_TypeError,
                    "legacy tp_dealloc is incompatible with HPy_tp_traverse"
                    " or HPy_tp_destroy.")
            # asssign ((PyTypeObject *)w_type)->tp_dealloc
            w_type.has_tp_dealloc = True
            funcptr = slotdef.c_pfunc
            if not hasattr(space, 'is_fake_objspace'):
                # the following lines break test_ztranslation :(
                pytype = rffi.cast(PyTypeObjectPtr, as_pyobj(space, w_type))
                pytype.c_tp_dealloc = rffi.cast(destructor, funcptr)
    
        else:
            attach_legacy_slot(space, w_type, slotdef, slotnum, type_name)
        i += 1

def attach_legacy_slot(space, w_type, slotdef, slotnum, type_name):
    
    for num, method_name, doc, wrapper_class in SLOT_WRAPPERS_TABLE:
        if num == slotnum:
            if wrapper_class is None:
                # XXX: we probably need to handle manually these slots
                raise NotImplementedError("slot wrapper for slot %d" % num)
            funcptr = slotdef.c_pfunc
            w_wrapper = wrapper_class(space, w_type, method_name, doc, funcptr, type_name)
            w_type.setdictvalue(space, method_name, w_wrapper)
            break
    else:
        assert False, 'cannot find the slot %d' % (slotnum)