File: interp_descr.py

package info (click to toggle)
pypy3 7.3.19%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 212,236 kB
  • sloc: python: 2,098,316; ansic: 540,565; sh: 21,462; asm: 14,419; cpp: 4,451; makefile: 4,209; objc: 761; xml: 530; exp: 499; javascript: 314; pascal: 244; lisp: 45; csh: 12; awk: 4
file content (336 lines) | stat: -rw-r--r-- 14,107 bytes parent folder | download | duplicates (2)
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
"""
Implements HPy attribute descriptors, i.e members and getsets.
"""
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib.unroll import unrolling_iterable
from rpython.rlib.objectmodel import import_from_mixin, specialize
from pypy.interpreter.error import oefmt
from pypy.interpreter.baseobjspace import DescrMismatch
from pypy.interpreter.typedef import (
    GetSetProperty, TypeDef, interp_attrproperty, interp2app)
from pypy.module._hpy_universal import llapi
from pypy.module._hpy_universal.state import State

ADDRESS = lltype.Signed

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

# ======== HPyDef_Kind_Member ========

converter_data = [                     # range checking
    ('SHORT',  rffi.SHORT,                      True),
    ('INT',    rffi.INT,                       True),
    ('LONG',   rffi.LONG,                       False),
    ('USHORT', rffi.USHORT,             True),
    ('UINT',   rffi.UINT,               True),
    ('ULONG',  rffi.ULONG,              False),
    ('BYTE',   rffi.SIGNEDCHAR,                 True),
    ('UBYTE',  rffi.UCHAR,              True),
    #('BOOL',   rffi.UCHAR,  convert_bool,                     False),
    #('FLOAT',  rffi.FLOAT,  PyFloat_AsDouble,                 False),
    #('DOUBLE', rffi.DOUBLE, PyFloat_AsDouble,                 False),
    ('LONGLONG',  rffi.LONGLONG,            False),
    ('ULONGLONG', rffi.ULONGLONG,   False),
    ('HPYSSIZET', rffi.SSIZE_T,               False),
    ]
Enum = llapi.cts.gettype('HPyMember_FieldType')
converters = unrolling_iterable([
    (getattr(Enum, 'HPyMember_' + name), typ) for name, typ, _ in converter_data])

def member_get(w_descr, space, w_obj):
    from .interp_type import W_HPyObject
    assert isinstance(w_descr, W_HPyMemberDescriptor)
    check_descr(space, w_obj, w_descr.w_type)
    storage = w_obj._hpy_get_raw_storage(space)
    if not storage:
        raise oefmt(space.w_TypeError, "non-HPy object in member_get")
    # POSSIBLE DRAGONS AHEAD: here we take the address of the HPy object and
    # later we read from it. This is correct because at the moment we allocate
    # HPY_STORAGE as nonmovable=True, but if/when we refactor to use movable
    # memory, we will need to ensure that no GC operations can happen between
    # this cast and the actual read below.
    addr = rffi.cast(ADDRESS, storage) + w_descr.offset
    kind = w_descr.kind
    for num, typ in converters:
        if kind == num:
            return space.newint(rffi.cast(rffi.CArrayPtr(typ), addr)[0])
    if kind == Enum.HPyMember_FLOAT:
        value = rffi.cast(rffi.CArrayPtr(rffi.FLOAT), addr)[0]
        return space.newfloat(rffi.cast(rffi.DOUBLE, value))
    elif kind == Enum.HPyMember_DOUBLE:
        value = rffi.cast(rffi.CArrayPtr(rffi.DOUBLE), addr)[0]
        return space.newfloat(value)
    elif kind == Enum.HPyMember_BOOL:
        value = rffi.cast(rffi.CArrayPtr(rffi.UCHAR), addr)[0]
        value = rffi.cast(lltype.Signed, value)
        return space.newbool(bool(value))
    elif kind == Enum.HPyMember_CHAR:
        value = rffi.cast(rffi.CCHARP, addr)[0]
        return space.newtext(value)
    elif kind == Enum.HPyMember_STRING:
        cstr_p = rffi.cast(rffi.CCHARPP, addr)
        if cstr_p[0]:
            value = rffi.charp2str(cstr_p[0])
            return space.newtext(value)
        else:
            return space.w_None
    elif kind == Enum.HPyMember_STRING_INPLACE:
        value = rffi.charp2str(rffi.cast(rffi.CCHARP, addr))
        return space.newtext(value)
    elif kind == Enum.HPyMember_NONE:
        return space.w_None
    elif kind == Enum.HPyMember_OBJECT:
        from pypy.module._hpy_universal.interp_field import field_load_w
        addr_contents = llapi.cts.cast("HPyField*", addr)[0]
        if  addr_contents == 0:
            return space.w_None
        w_res = field_load_w(space, w_obj, addr_contents)
        return w_res
    elif kind == Enum.HPyMember_OBJECT_EX:
        from pypy.module._hpy_universal.interp_field import field_load_w
        addr_contents = llapi.cts.cast("HPyField*", addr)[0]
        if addr_contents == 0:
            raise oefmt(space.w_AttributeError,
                        "%R object has no attribute '%s'", w_descr.w_type, w_descr.name)
        w_res = field_load_w(space, w_obj, addr_contents)
        return w_res
    else:
        raise oefmt(space.w_NotImplementedError, 'cannot handle get for %d', kind)


def member_set(w_descr, space, w_obj, w_value):
    from .interp_type import W_HPyObject
    assert isinstance(w_descr, W_HPyMemberDescriptor)
    check_descr(space, w_obj, w_descr.w_type)
    storage = w_obj._hpy_get_raw_storage(space)
    if not storage:
        raise oefmt(space.w_TypeError, "non-HPy object in member_set")
    # POSSIBLE DRAGONS AHEAD: here we take the address of the HPy object and
    # later we write into it (look for ptr[0] = ...). This is correct because
    # at the moment we allocate HPY_STORAGE as nonmovable=True, but if/when we
    # refactor to use movable memory, we will need to ensure that no GC
    # operations can happen between this cast and the various
    # ptr[0]=... below.
    addr = rffi.cast(ADDRESS, storage) + w_descr.offset
    kind = w_descr.kind
    for num, typ in converters:
        if kind == num:
            # XXX: this is wrong!
            value = space.int_w(w_value)
            ptr = rffi.cast(rffi.CArrayPtr(typ), addr)
            ptr[0] = rffi.cast(typ, value)
            return
    if kind == Enum.HPyMember_FLOAT:
        value = space.float_w(w_value)
        ptr = rffi.cast(rffi.CArrayPtr(rffi.FLOAT), addr)
        ptr[0] = rffi.cast(rffi.FLOAT, value)
        return
    elif kind == Enum.HPyMember_DOUBLE:
        value = space.float_w(w_value)
        ptr = rffi.cast(rffi.CArrayPtr(rffi.DOUBLE), addr)
        ptr[0] = value
        return
    elif kind == Enum.HPyMember_BOOL:
        if space.is_w(w_value, space.w_False):
            value = False
        elif space.is_w(w_value, space.w_True):
            value = True
        else:
            raise oefmt(space.w_TypeError, "attribute value type must be bool")
        ptr = rffi.cast(rffi.CArrayPtr(rffi.UCHAR), addr)
        ptr[0] = rffi.cast(rffi.UCHAR, value)
        return
    elif kind == Enum.HPyMember_CHAR:
        str_value = space.text_w(w_value)
        if len(str_value) != 1:
            raise oefmt(space.w_TypeError, "string of length 1 expected")
        ptr = rffi.cast(rffi.CCHARP, addr)
        ptr[0] = str_value[0]
    elif kind in (Enum.HPyMember_STRING,
                  Enum.HPyMember_STRING_INPLACE,
                  Enum.HPyMember_NONE):
        raise oefmt(space.w_TypeError, 'readonly attribute')
    elif kind in (Enum.HPyMember_OBJECT, Enum.HPyMember_OBJECT_EX):
        from pypy.module._hpy_universal.interp_field import field_store_w
        ptr = llapi.cts.cast("HPyField *", addr)
        field_store_w(space, ptr, w_obj, w_value)
    else:
        raise oefmt(space.w_NotImplementedError, '...')

def member_del(w_descr, space, w_obj):
    from .interp_type import W_HPyObject
    assert isinstance(w_descr, W_HPyMemberDescriptor)
    check_descr(space, w_obj, w_descr.w_type)
    storage = w_obj._hpy_get_raw_storage(space)
    if not storage:
        raise oefmt(space.w_TypeError, "non-HPy object in member_del")
    # POSSIBLE DRAGONS AHEAD: here we take the address of the HPy object and
    # later we write into it (look for ptr[0] = ...). This is correct because
    # at the moment we allocate HPY_STORAGE as nonmovable=True, but if/when we
    # refactor to use movable memory, we will need to ensure that no GC
    # operations can happen between this cast and the various
    # ptr[0]=... below.
    addr = rffi.cast(ADDRESS, storage) + w_descr.offset
    kind = w_descr.kind
    if kind in (Enum.HPyMember_OBJECT, Enum.HPyMember_OBJECT_EX):
        from pypy.module._hpy_universal.interp_field import field_delete_w
        ptr = llapi.cts.cast("HPyField *", addr)
        field_delete_w(ptr)
    else:
        raise oefmt(space.w_TypeError,
                    "can't delete numeric/char attribute")


class W_HPyMemberDescriptor(GetSetProperty):
    def __init__(self, w_type, kind, name, doc, offset, is_readonly):
        self.kind = kind
        self.name = name
        self.w_type = w_type
        self.offset = offset
        self.is_readonly = is_readonly
        if is_readonly:
            setter = None
            deleter = None
        else:
            setter = member_set
            deleter = member_del
        GetSetProperty.__init__(
            self, member_get, setter, deleter, doc,
            cls=None, use_closure=True, tag="hpy_member", name=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)


W_HPyMemberDescriptor.typedef = TypeDef(
    "hpy_member_descriptor",
    __get__=interp2app(GetSetProperty.descr_property_get),
    __set__=interp2app(GetSetProperty.descr_property_set),
    __delete__=interp2app(GetSetProperty.descr_property_del),
    __name__=interp_attrproperty('name', cls=GetSetProperty,
        wrapfn="newtext_or_none"),
    __objclass__=GetSetProperty(GetSetProperty.descr_get_objclass),
    __doc__=interp_attrproperty('doc', cls=GetSetProperty,
        wrapfn="newtext_or_none"),
    )
assert not W_HPyMemberDescriptor.typedef.acceptable_as_base_class  # no __new__

def add_member(space, w_type, hpymember):
    name = rffi.constcharp2str(hpymember.c_name)
    kind = rffi.cast(lltype.Signed, hpymember.c_type)
    offset = rffi.cast(lltype.Signed, hpymember.c_offset)
    readonly = rffi.cast(lltype.Signed, hpymember.c_readonly)
    doc = rffi.constcharp2str(hpymember.c_doc) if hpymember.c_doc else None
    w_descr = W_HPyMemberDescriptor(w_type, kind, name, doc, offset, readonly)
    w_type.setdictvalue(space, name, w_descr)
    if name == "__vectorcalloffset__":
        return offset
    else:
        return 0


# ======== HPyDef_Kind_GetSet ========

def getset_get_u(w_getset, space, w_self):
    handles = space.fromcache(State).u_handles
    return _getset_get(handles, w_getset, w_self)

def getset_get_d(w_getset, space, w_self):
    handles = space.fromcache(State).d_handles
    return _getset_get(handles, w_getset, w_self)

@specialize.arg(0)
def _getset_get(handles, w_getset, w_self):
    cfuncptr = w_getset.hpygetset.c_getter_impl
    func = llapi.cts.cast('HPyFunc_getter', cfuncptr)
    with handles.using(w_self) as h_self:
        h_result = func(handles.ctx, h_self, w_getset.hpygetset.c_closure)
    return handles.consume(h_result)

def getset_set_u(w_getset, space, w_self, w_value):
    handles = space.fromcache(State).u_handles
    return _getset_set(handles, w_getset, w_self, w_value)

def getset_set_d(w_getset, space, w_self, w_value):
    handles = space.fromcache(State).d_handles
    return _getset_set(handles, w_getset, w_self, w_value)

@specialize.arg(0)
def _getset_set(handles, w_getset, w_self, w_value):
    cfuncptr = w_getset.hpygetset.c_setter_impl
    func = llapi.cts.cast('HPyFunc_setter', cfuncptr)
    with handles.using(w_self, w_value) as (h_self, h_value):
        h_result = func(handles.ctx, h_self, h_value, w_getset.hpygetset.c_closure)
        # XXX: write a test to check that we do the correct thing if
        # c_setter raises an exception

class W_HPyGetSetProperty(GetSetProperty):
    def __init__(self, w_type, hpygetset):
        self.hpygetset = hpygetset
        self.w_type = w_type
        #
        name = rffi.constcharp2str(hpygetset.c_name)
        doc = fset = fget = fdel = None
        if hpygetset.c_doc:
            doc = rffi.constcharp2str(hpygetset.c_doc)
        if hpygetset.c_getter_impl:
            fget = getset_get_u
        if hpygetset.c_setter_impl:
            fset = getset_set_u
            # XXX: write a test to check that 'del' works
            #fdel = ...
        GetSetProperty.__init__(self, fget, fset, fdel, doc,
                                cls=None, use_closure=True,
                                tag="hpy_getset_u", name=name)

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

class W_HPyGetSetPropertyDebug(GetSetProperty):
    def __init__(self, w_type, hpygetset):
        self.hpygetset = hpygetset
        self.w_type = w_type
        #
        name = rffi.constcharp2str(hpygetset.c_name)
        doc = fset = fget = fdel = None
        if hpygetset.c_doc:
            doc = rffi.constcharp2str(hpygetset.c_doc)
        if hpygetset.c_getter_impl:
            fget = getset_get_d
        if hpygetset.c_setter_impl:
            fset = getset_set_d
            # XXX: write a test to check that 'del' works
            #fdel = ...
        GetSetProperty.__init__(self, fget, fset, fdel, doc,
                                cls=None, use_closure=True,
                                tag="hpy_getset_d", name=name)

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


@specialize.arg(0)
def add_getset(handles, w_type, hpygetset):
    space = handles.space
    if handles.is_debug:
        GetSetClass = W_HPyGetSetPropertyDebug
    else:
        GetSetClass = W_HPyGetSetProperty
    w_descr = GetSetClass(w_type, hpygetset)
    w_type.setdictvalue(space, w_descr.name, w_descr)
    #
    # the following is needed to ensure that we annotate getset_*, else
    # test_ztranslation fails
    if hasattr(space, 'is_fake_objspace'):
        w_descr.descr_property_get(space, space.w_None)
        w_descr.descr_property_set(space, space.w_None, space.w_None)