File: interp_slot.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 (626 lines) | stat: -rw-r--r-- 28,543 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
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rlib.rarithmetic import widen
from rpython.rlib.unroll import unrolling_iterable
from rpython.rlib.objectmodel import specialize, import_from_mixin
from rpython.rlib import jit

from pypy.interpreter.error import oefmt
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.gateway import descr_function_get
from pypy.interpreter.typedef import TypeDef, interp2app
from pypy.objspace.std.typeobject import W_TypeObject
from pypy.module._hpy_universal import llapi
from .state import State

HPySlot_Slot = llapi.cts.gettype('HPySlot_Slot')
HPy_RichCmpOp = llapi.cts.gettype('HPy_RichCmpOp')

_WRAPPER_CACHE = {}

class W_SlotWrapper(W_Root):
    _immutable_fields_ = ["slot"]

    def __init__(self, slot, method_name, cfuncptr, w_objclass):
        self.slot = slot
        self.name = method_name
        self.cfuncptr = cfuncptr
        self.w_objclass = w_objclass

    def check_args(self, space, __args__, arity):
        length = len(__args__.arguments_w)
        if length != arity:
            raise oefmt(space.w_TypeError, "expected %d arguments, got %d",
                        arity, length)
        if __args__.keyword_names_w:
            raise oefmt(space.w_TypeError,
                        "wrapper %s doesn't take any keyword arguments",
                        self.name)

    def check_argsv(self, space, __args__, min, max):
        length = len(__args__.arguments_w)
        if not min <= length <= max:
            raise oefmt(space.w_TypeError, "expected %d-%d arguments, got %d",
                        min, max, length)
        if __args__.keyword_names_w:
            raise oefmt(space.w_TypeError,
                        "wrapper %s doesn't take any keyword arguments",
                        self.name)

    def descr_call(self, space, __args__):
        # XXX: basically a copy of cpyext's W_PyCMethodObject.descr_call()
        if len(__args__.arguments_w) == 0:
            w_objclass = self.w_objclass
            assert isinstance(w_objclass, W_TypeObject)
            raise oefmt(space.w_TypeError,
                "descriptor '%8' of '%s' object needs an argument",
                self.name, self.w_objclass.getname(space))
        w_instance = __args__.arguments_w[0]
        # XXX: needs a stricter test
        if not space.isinstance_w(w_instance, self.w_objclass):
            w_objclass = self.w_objclass
            assert isinstance(w_objclass, W_TypeObject)
            raise oefmt(space.w_TypeError,
                "descriptor '%8' requires a '%s' object but received a '%T'",
                self.name, w_objclass.name, w_instance)
        #
        return self.call(space, __args__)

    def call(self, space, __args__):
        raise oefmt(space.w_RuntimeError, "bad slot wrapper")

W_SlotWrapper.typedef = TypeDef(
    'slot_wrapper',
    __get__ = interp2app(descr_function_get),
    __call__ = interp2app(W_SlotWrapper.descr_call),
    )
W_SlotWrapper.typedef.acceptable_as_base_class = False

# ~~~~~~~~~~ concrete W_SlotWrapper subclasses ~~~~~~~~~~~~~
# these are the equivalent of the various functions wrap_* inside CPython's typeobject.c

class W_wrap_binaryfunc(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_binaryfunc", self.cfuncptr)
        self.check_args(space, __args__, 2)
        w_self = __args__.arguments_w[0]
        w_other = __args__.arguments_w[1]
        with self.handles.using(w_self, w_other) as (h_self, h_other):
            h_result = func(self.ctx, h_self, h_other)
        if not h_result:
            space.fromcache(State).raise_current_exception()
        return self.handles.consume(h_result)

@specialize.memo()
def get_cmp_wrapper_cls(handles, methname, OP):
    try:
        return _WRAPPER_CACHE[handles, methname]
    except KeyError:
        pass
    class wrapper(W_SlotWrapper):
        def call(self, space, __args__):
            func = llapi.cts.cast("HPyFunc_richcmpfunc", self.cfuncptr)
            self.check_args(space, __args__, 2)
            w_self = __args__.arguments_w[0]
            w_other = __args__.arguments_w[1]
            with handles.using(w_self, w_other) as (h_self, h_other):
                # rffi doesn't allow casting to an enum, we need to use int
                # instead
                h_result = func(
                    handles.ctx, h_self, h_other, rffi.cast(rffi.INT_real, OP))
            if not h_result:
                space.fromcache(State).raise_current_exception()
            return handles.consume(h_result)
    suffix = '_d' if handles.is_debug else '_u'
    wrapper.__name__ = 'W_wrap_richcmp%s%s' % (methname, suffix)
    _WRAPPER_CACHE[handles, methname] = wrapper
    return wrapper

CMP_OPNAMES = ['eq', 'ne', 'lt', 'le', 'gt', 'ge']
CMP_ENUM_VALUES = [
    getattr(HPy_RichCmpOp, 'HPy_%s' % opname.upper()) for opname in CMP_OPNAMES]
CMP_SLOTS = unrolling_iterable([
    ('__%s__' % opname, opval)
    for opname, opval in zip(CMP_OPNAMES, CMP_ENUM_VALUES)])

class W_wrap_voidfunc(object):
    def call(self, space, __args__):
        # cheat: it should be a void func here.
        # Note this function cannot error
        func = llapi.cts.cast("HPyFunc_unaryfunc", self.cfuncptr)
        self.check_args(space, __args__, 1)
        w_self = __args__.arguments_w[0]
        with self.handles.using(w_self) as h_self:
            h_result = func(self.ctx, h_self)

class W_wrap_unaryfunc(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_unaryfunc", self.cfuncptr)
        self.check_args(space, __args__, 1)
        w_self = __args__.arguments_w[0]
        with self.handles.using(w_self) as h_self:
            h_result = func(self.ctx, h_self)
        if not h_result:
            space.fromcache(State).raise_current_exception()
        return self.handles.consume(h_result)

class W_wrap_ternaryfunc(object):
    def call(self, space, __args__):
        # Literaly quote of the corresponding CPython comment:
        #     Note: This wrapper only works for __pow__()
        #
        func = llapi.cts.cast("HPyFunc_ternaryfunc", self.cfuncptr)
        self.check_argsv(space, __args__, 2, 3)
        n = len(__args__.arguments_w)
        w_self = __args__.arguments_w[0]
        w1 = __args__.arguments_w[1]
        if n == 2:
            w2 = space.w_None
        else:
            w2 = __args__.arguments_w[2]
        with self.handles.using(w_self, w1, w2) as (h_self, h1, h2):
            h_result = func(self.ctx, h_self, h1, h2)
        if not h_result:
            space.fromcache(State).raise_current_exception()
        return self.handles.consume(h_result)

class W_wrap_indexargfunc(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_ssizeargfunc", self.cfuncptr)
        self.check_args(space, __args__, 2)
        w_self = __args__.arguments_w[0]
        w_idx = __args__.arguments_w[1]
        idx = space.int_w(space.index(w_idx))
        with self.handles.using(w_self) as h_self:
            h_result = func(self.ctx, h_self, idx)
        if not h_result:
            space.fromcache(State).raise_current_exception()
        return self.handles.consume(h_result)

class W_wrap_inquirypred(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_inquiry", self.cfuncptr)
        self.check_args(space, __args__, 1)
        w_self = __args__.arguments_w[0]
        with self.handles.using(w_self) as h_self:
            res = func(self.ctx, h_self)
        res = rffi.cast(lltype.Signed, res)
        if res == -1:
            space.fromcache(State).raise_current_exception()
        return space.newbool(bool(res))

class W_wrap_lenfunc(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_lenfunc", self.cfuncptr)
        self.check_args(space, __args__, 1)
        w_self = __args__.arguments_w[0]
        with self.handles.using(w_self) as h_self:
            result = func(self.ctx, h_self)
        if widen(result) == -1:
            space.fromcache(State).raise_current_exception()
        return space.newint(result)

class W_wrap_hashfunc(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_hashfunc", self.cfuncptr)
        self.check_args(space, __args__, 1)
        w_self = __args__.arguments_w[0]
        with self.handles.using(w_self) as h_self:
            result = func(self.ctx, h_self)
        if widen(result) == -1:
            operror = space.fromcache(State).clear_exception()
            if operror:
                raise operror
        return space.newint(result)

class W_wrap_call(object):
    def call(self, space, __args__):
        w_func = self.handles.w_ExtensionMethod(space, self.handles, "__call__",
            llapi.HPyFunc_KEYWORDS, "", self.cfuncptr, self.w_objclass)
        return space.call_args(w_func, __args__)

class W_wrap_call_at_offset(object):
    def call(self, space, __args__):
        from .interp_type import W_HPyObject
        w_obj = __args__.arguments_w[0]
        assert isinstance(w_obj, W_HPyObject)
        storage = w_obj._hpy_get_raw_storage(space)
        if not storage:
            raise oefmt(space.w_TypeError, "non-HPy object in __call__")
        addr = rffi.cast(lltype.Signed, storage) + self.offset
        callfunc = llapi.cts.cast('HPyCallFunction*', addr)
        cfuncptr = llapi.cts.cast('HPyCFunction', callfunc.c_impl)
        w_func = self.handles.w_ExtensionMethod(space, self.handles, "__call__",
            llapi.HPyFunc_KEYWORDS, "", cfuncptr, self.w_objclass)
        return space.call_args(w_func, __args__)

def sq_getindex(space, w_sequence, w_idx):
    """
    This is equivalent to CPython's typeobject.c:getindex().
    We call it sq_getindex because it's used only by sq_* slots.
    """
    idx = space.int_w(space.index(w_idx))
    if idx < 0 and space.lookup(w_sequence, '__len__'):
        # It is worth noting that we are doing the lookup of __len__ twice,
        # one above and one inside space.len_w. The JIT should optimize it
        # away, but it might be a minor slowdown for interpreted code.
        n = space.len_w(w_sequence)
        idx += n
    return idx

class W_wrap_mp_item(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_ssizeargfunc", self.cfuncptr)
        self.check_args(space, __args__, 2)
        w_self = __args__.arguments_w[0]
        w_key = __args__.arguments_w[1]
        with self.handles.using(w_self, w_key) as (h_self, h_key):
            h_result = func(self.ctx, h_self, h_key)
        if not h_result:
            space.fromcache(State).raise_current_exception()
        return self.handles.consume(h_result)

class W_wrap_sq_item(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_ssizeargfunc", self.cfuncptr)
        self.check_args(space, __args__, 2)
        w_self = __args__.arguments_w[0]
        w_idx = __args__.arguments_w[1]
        idx = sq_getindex(space, w_self, w_idx)
        with self.handles.using(w_self) as h_self:
            h_result = func(self.ctx, h_self, idx)
        if not h_result:
            space.fromcache(State).raise_current_exception()
        return self.handles.consume(h_result)

class W_wrap_mp_setitem(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_ssizeobjargproc", self.cfuncptr)
        self.check_args(space, __args__, 3)
        w_self = __args__.arguments_w[0]
        w_key = __args__.arguments_w[1]
        w_value = __args__.arguments_w[2]
        with self.handles.using(w_self, w_key, w_value) as (h_self, h_key, h_value):
            result = func(self.ctx, h_self, h_key, h_value)
        if widen(result) == -1:
            space.fromcache(State).raise_current_exception()
        return space.w_None

class W_wrap_sq_setitem(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_ssizeobjargproc", self.cfuncptr)
        self.check_args(space, __args__, 3)
        w_self = __args__.arguments_w[0]
        w_idx = __args__.arguments_w[1]
        idx = sq_getindex(space, w_self, w_idx)
        w_value = __args__.arguments_w[2]
        with self.handles.using(w_self, w_value) as (h_self, h_value):
            result = func(self.ctx, h_self, idx, h_value)
        if widen(result) == -1:
            space.fromcache(State).raise_current_exception()
        return space.w_None

class W_wrap_mp_delitem(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_ssizeobjargproc", self.cfuncptr)
        self.check_args(space, __args__, 2)
        w_self = __args__.arguments_w[0]
        w_key = __args__.arguments_w[1]
        with self.handles.using(w_self, w_key) as (h_self, h_key):
            result = func(self.ctx, h_self, h_key, llapi.HPy_NULL)
        if widen(result) == -1:
            space.fromcache(State).raise_current_exception()
        return space.w_None

class W_wrap_sq_delitem(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_ssizeobjargproc", self.cfuncptr)
        self.check_args(space, __args__, 2)
        w_self = __args__.arguments_w[0]
        w_idx = __args__.arguments_w[1]
        idx = sq_getindex(space, w_self, w_idx)
        with self.handles.using(w_self) as h_self:
            result = func(self.ctx, h_self, idx, llapi.HPy_NULL)
        if widen(result) == -1:
            space.fromcache(State).raise_current_exception()
        return space.w_None

class W_wrap_objobjproc(object):
    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_objobjproc", self.cfuncptr)
        self.check_args(space, __args__, 2)
        w_self = __args__.arguments_w[0]
        w_key = __args__.arguments_w[1]
        with self.handles.using(w_self, w_key) as (h_self, h_key):
            res = func(self.ctx, h_self, h_key)
        res = widen(res)
        if res == -1:
            space.fromcache(State).raise_current_exception()
        return space.newbool(bool(res))

class W_wrap_getbuffer(object):
    rbp = llapi.cts.cast('HPyFunc_releasebufferproc', 0)

    def call(self, space, __args__):
        func = llapi.cts.cast("HPyFunc_getbufferproc", self.cfuncptr)
        self.check_args(space, __args__, 2)
        w_self = __args__.arguments_w[0]
        w_flags = __args__.arguments_w[1]
        flags = rffi.cast(rffi.INT_real, space.int_w(w_flags))
        with lltype.scoped_alloc(llapi.cts.gettype('HPy_buffer')) as hpybuf:
            with self.handles.using(w_self) as h_self:
                res = func(self.ctx, h_self, hpybuf, flags)
            if widen(res) < 0:
                space.fromcache(State).raise_current_exception()
            buf_ptr = hpybuf.c_buf
            w_obj = self.handles.consume(hpybuf.c_obj.c__i)
            size = hpybuf.c_len
            ndim = widen(hpybuf.c_ndim)
            shape = None
            if hpybuf.c_shape:
                shape = [hpybuf.c_shape[i] for i in range(ndim)]
            strides = None
            if hpybuf.c_strides:
                strides = [hpybuf.c_strides[i] for i in range(ndim)]
            if hpybuf.c_format:
                format = rffi.charp2str(hpybuf.c_format)
            else:
                format = 'B'
            view = self.handles.HPyBuffer(
                buf_ptr, size, w_obj,
                itemsize=hpybuf.c_itemsize,
                readonly=widen(hpybuf.c_readonly),
                ndim=widen(hpybuf.c_ndim), format=format, shape=shape,
                strides=strides)
            if self.rbp:
                # XXX: we're assuming w_self and w_obj have the same type!
                view.releasebufferproc = self.rbp
                self.handles.BUFFER_FQ.register_finalizer(view)
            return view.wrap(space)


# remaining wrappers to write
## wrap_binaryfunc_l(PyObject *self, PyObject *args, void *wrapped)
## wrap_binaryfunc_r(PyObject *self, PyObject *args, void *wrapped)
## wrap_ternaryfunc_r(PyObject *self, PyObject *args, void *wrapped)
## wrap_objobjargproc(PyObject *self, PyObject *args, void *wrapped)
## wrap_delitem(PyObject *self, PyObject *args, void *wrapped)
## wrap_setattr(PyObject *self, PyObject *args, void *wrapped)
## wrap_delattr(PyObject *self, PyObject *args, void *wrapped)
## wrap_hashfunc(PyObject *self, PyObject *args, void *wrapped)
## wrap_call(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds)
## wrap_del(PyObject *self, PyObject *args, void *wrapped)
## wrap_next(PyObject *self, PyObject *args, void *wrapped)
## wrap_descr_get(PyObject *self, PyObject *args, void *wrapped)
## wrap_descr_set(PyObject *self, PyObject *args, void *wrapped)
## wrap_descr_delete(PyObject *self, PyObject *args, void *wrapped)
 
class W_wrap_init(object):
    def call(self, space, __args__):
        with self.handles.using(__args__.arguments_w[0]) as h_self:
            n = len(__args__.arguments_w) - 1
            with lltype.scoped_alloc(rffi.CArray(llapi.HPy), n) as args_h:
                i = 0
                while i < n:
                    args_h[i] = self.handles.new(__args__.arguments_w[i + 1])
                    i += 1
                h_kw = 0
                if __args__.keyword_names_w:
                    w_kw = space.newdict()
                    for i in range(len(__args__.keyword_names_w)):
                        w_key = __args__.keyword_names_w[i]
                        w_value = __args__.keywords_w[i]
                        space.setitem(w_kw, w_key, w_value)
                    h_kw = self.handles.new(w_kw)
                fptr = llapi.cts.cast('HPyFunc_initproc', self.cfuncptr)
                try:
                    result = fptr(self.ctx, h_self, args_h, n, h_kw)
                finally:
                    if h_kw:
                        self.handles.close(h_kw)
                    for i in range(n):
                        self.handles.close(args_h[i])
        if rffi.cast(lltype.Signed, result) < 0:
            space.fromcache(State).raise_current_exception()
        return space.w_None

@specialize.memo()
def get_slot_cls(handles, mixin):
    try:
        return _WRAPPER_CACHE[handles, mixin]
    except KeyError:
        pass

    _handles = handles
    class wrapper(W_SlotWrapper):
        import_from_mixin(mixin)
        handles = _handles
        ctx = _handles.ctx

    wrapper.__name__ = mixin.__name__ + handles.cls_suffix
    _WRAPPER_CACHE[handles, mixin] = wrapper
    return wrapper

@specialize.memo()
def get_tp_new_wrapper_cls(handles):
    try:
        return _WRAPPER_CACHE[handles, 'new']
    except KeyError:
        pass

    class W_tp_new_wrapper(handles.w_ExtensionFunction):
        """
        Special case for HPy_tp_new. Note that is not NOT a SlotWrapper.

        This is the equivalent of CPython's tp_new_wrapper: the difference is that
        CPython's tp_new_wrapper is a regular PyMethodDef which is wrapped inside
        a PyCFunction, while here we have our own type.
        """

        def __init__(self, cfuncptr, w_type):
            handles.w_ExtensionFunction.__init__(
                self, handles.space, handles, '__new__',
                llapi.HPyFunc_KEYWORDS, None, cfuncptr, w_self=w_type)

        def call(self, space, h_self, __args__, skip_args=0):
            assert space is handles.space
            assert skip_args == 0
            # NOTE: h_self contains the type for which we are calling __new__, but
            # here is ignored. In CPython's tp_new_wrapper it is only used to fish
            # the ->tp_new to call, but here we already have the cfuncptr
            #
            # XXX: tp_new_wrapper does additional checks, we should write tests
            # and implement the same checks
            w_self = __args__.arguments_w[0]
            with handles.using(w_self) as h_self:
                return self.call_varargs_kw(space, h_self, __args__,
                                            skip_args=1, has_keywords=True)
    W_tp_new_wrapper.__name__ += handles.cls_suffix
    _WRAPPER_CACHE[handles, 'new'] = W_tp_new_wrapper
    return W_tp_new_wrapper


# the following table shows how to map C-level slots into Python-level
# __methods__. Note that if a C-level slot corresponds to multiple
# __methods__, it appears multiple times (e.g. sq_ass_item corresponds to both
# __setitem__ and __delitem__).
SLOTS = unrolling_iterable([
    # CPython slots
    ('bf_getbuffer',                '__buffer__',   W_wrap_getbuffer),
    ('mp_ass_subscript',           '__setitem__',   W_wrap_mp_setitem),
    ('mp_ass_subscript',           '__delitem__',   W_wrap_mp_delitem),
    ('mp_length',                  '__len__',       W_wrap_lenfunc),
    ('mp_subscript',               '__getitem__',   W_wrap_mp_item),
    ('nb_absolute',                '__abs__',       W_wrap_unaryfunc),
    ('nb_add',                     '__add__',       W_wrap_binaryfunc),
    ('nb_and',                     '__and__',       W_wrap_binaryfunc),
    ('nb_bool',                    '__bool__',      W_wrap_inquirypred),
    ('nb_divmod',                  '__divmod__',    W_wrap_binaryfunc),
    ('nb_float',                   '__float__',     W_wrap_unaryfunc),
    ('nb_floor_divide',            '__floordiv__',  W_wrap_binaryfunc),
    ('nb_index',                   '__index__',     W_wrap_unaryfunc),
    ('nb_inplace_add',             '__iadd__',      W_wrap_binaryfunc),
    ('nb_inplace_and',             '__iand__',      W_wrap_binaryfunc),
    ('nb_inplace_floor_divide',    '__ifloordiv__', W_wrap_binaryfunc),
    ('nb_inplace_lshift',          '__ilshift__',   W_wrap_binaryfunc),
    ('nb_inplace_multiply',        '__imul__',      W_wrap_binaryfunc),
    ('nb_inplace_or',              '__ior__',       W_wrap_binaryfunc),
    # CPython is buggy here: it uses wrap_binaryfunc for nb_inplace_power, but
    # it means you end up calling the cfunc with the wrong signature! We
    # correctly user W_wrap_ternaryfunc instead
    ('nb_inplace_power',           '__ipow__',      W_wrap_ternaryfunc),
    ('nb_inplace_remainder',       '__imod__',      W_wrap_binaryfunc),
    ('nb_inplace_rshift',          '__irshift__',   W_wrap_binaryfunc),
    ('nb_inplace_subtract',        '__isub__',      W_wrap_binaryfunc),
    ('nb_inplace_true_divide',     '__itruediv__',  W_wrap_binaryfunc),
    ('nb_inplace_xor',             '__ixor__',      W_wrap_binaryfunc),
    ('nb_int',                     '__int__',       W_wrap_unaryfunc),
    ('nb_invert',                  '__invert__',    W_wrap_unaryfunc),
    ('nb_lshift',                  '__lshift__',    W_wrap_binaryfunc),
    ('nb_multiply',                '__mul__',       W_wrap_binaryfunc),
    ('nb_negative',                '__neg__',       W_wrap_unaryfunc),
    ('nb_or',                      '__or__',        W_wrap_binaryfunc),
    ('nb_positive',                '__pos__',       W_wrap_unaryfunc),
    ('nb_power',                   '__pow__',       W_wrap_ternaryfunc),
    ('nb_remainder',               '__mod__',       W_wrap_binaryfunc),
    ('nb_rshift',                  '__rshift__',    W_wrap_binaryfunc),
    ('nb_subtract',                '__sub__',       W_wrap_binaryfunc),
    ('nb_true_divide',             '__truediv__',   W_wrap_binaryfunc),
    ('nb_xor',                     '__xor__',       W_wrap_binaryfunc),
    ('sq_ass_item',                '__setitem__',   W_wrap_sq_setitem),
    ('sq_ass_item',                '__delitem__',   W_wrap_sq_delitem),
    ('sq_concat',                  '__add__',       W_wrap_binaryfunc),
    ('sq_contains',                '__contains__',  W_wrap_objobjproc),
    ('sq_inplace_concat',          '__iadd__',      W_wrap_binaryfunc),
    ('sq_inplace_repeat',          '__imul__',      W_wrap_indexargfunc),
    ('sq_item',                    '__getitem__',   W_wrap_sq_item),
    ('sq_length',                  '__len__',       W_wrap_lenfunc),
    ('sq_repeat',                  '__mul__',       W_wrap_indexargfunc),
#   ('tp_base',                    '__xxx__',       AGS.W_SlotWrapper_...),
#   ('tp_bases',                   '__xxx__',       AGS.W_SlotWrapper_...),
    ('tp_call',                    '__call__',      W_wrap_call),
#   ('tp_clear',                   '__xxx__',       AGS.W_SlotWrapper_...),
#   ('tp_del',                     '__xxx__',       AGS.W_SlotWrapper_...),
    ('tp_descr_get',               '__get__',       W_wrap_ternaryfunc),
#   ('tp_descr_set',               '__xxx__',       AGS.W_SlotWrapper_...),
#   ('tp_doc',                     '__xxx__',       AGS.W_SlotWrapper_...),
#   ('tp_getattr',                 '__xxx__',       AGS.W_SlotWrapper_...),
#   ('tp_getattro',                '__xxx__',       AGS.W_SlotWrapper_...),
    ('tp_hash',                    '__hash__',      W_wrap_hashfunc),
    ('tp_init',                    '__init__',      W_wrap_init),
#   ('tp_is_gc',                   '__xxx__',       AGS.W_SlotWrapper_...),
#    ('tp_iter',                    '__iter__',      W_wrap_unaryfunc),
#   ('tp_iternext',                '__xxx__',       AGS.W_SlotWrapper_...),
#   tp_new     SPECIAL-CASED
    ('tp_repr',                    '__repr__',      W_wrap_unaryfunc),
#   tp_richcompare  SPECIAL-CASED
#   ('tp_setattr',                 '__xxx__',       AGS.W_SlotWrapper_...),
#   ('tp_setattro',                '__xxx__',       AGS.W_SlotWrapper_...),
    ('tp_str',                     '__str__',       W_wrap_unaryfunc),
#   tp_traverse  SPECIAL-CASED
    ('nb_matrix_multiply',         '__matmul__',    W_wrap_binaryfunc),
    ('nb_inplace_matrix_multiply', '__imatmul__',   W_wrap_binaryfunc),
#    ('am_await',                   '__await__',     W_wrap_unaryfunc),
#    ('am_aiter',                   '__aiter__',     W_wrap_unaryfunc),
#    ('am_anext',                   '__anext__',     W_wrap_unaryfunc),
    ('tp_finalize',                '__del__',       W_wrap_unaryfunc),

    # extra HPy-specific slots
#   ('tp_destroy',                 '__xxx__',       AGS.W_SlotWrapper_...),
    ])


@jit.dont_look_inside
@specialize.arg(0)
def fill_slot(handles, w_type, hpyslot):
    space = handles.space
    slot_num = rffi.cast(lltype.Signed, hpyslot.c_slot)
    has_tp_call = False
    # special cases
    if slot_num == HPySlot_Slot.HPy_tp_new:
        # this is the moral equivalent of CPython's add_tp_new_wrapper
        cls = get_tp_new_wrapper_cls(handles)
        w_func = cls(hpyslot.c_impl, w_type)
        w_type.setdictvalue(space, '__new__', w_func)
        return has_tp_call
    elif slot_num == HPySlot_Slot.HPy_tp_destroy:
        w_type.tp_destroy = llapi.cts.cast('HPyFunc_destroyfunc', hpyslot.c_impl)
        return has_tp_call
    elif slot_num == HPySlot_Slot.HPy_tp_traverse:
        w_type.tp_traverse = llapi.cts.cast('HPyFunc_traverseproc', hpyslot.c_impl)
        return has_tp_call
    elif slot_num == HPySlot_Slot.HPy_tp_richcompare:
        for methname, opval in CMP_SLOTS:
            cls = get_cmp_wrapper_cls(handles, methname, opval)
            w_slot = cls(slot_num, methname, hpyslot.c_impl, w_type)
            w_type.setdictvalue(space, methname, w_slot)
        return has_tp_call
    elif slot_num == HPySlot_Slot.HPy_tp_finalize:
        # This is not a normal __slot__ since we want __del__ to be called as a
        # finalizer, not when the object __del__ is called.
        cls = get_slot_cls(handles, W_wrap_voidfunc)
        w_slot = cls(slot_num, "__del__", hpyslot.c_impl, w_type)
        w_type.tp_finalize = w_slot
        return has_tp_call
    elif slot_num == HPySlot_Slot.HPy_bf_releasebuffer:
        # Handled befor this function
        return has_tp_call
    elif slot_num == HPySlot_Slot.HPy_bf_getbuffer:
        cls = get_slot_cls(handles, W_wrap_getbuffer)
        w_slot = cls(slot_num,"__buffer__", hpyslot.c_impl, w_type)
        w_type.setdictvalue(space, "__buffer__", w_slot)
    elif slot_num == HPySlot_Slot.HPy_tp_call:
        has_tp_call = True
    # generic cases
    found = False
    for slotname, methname, mixin in SLOTS:
        assert methname != '__xxx__' # sanity check
        n = getattr(HPySlot_Slot, 'HPy_' + slotname)
        if slot_num == n:
            found = True
            cls = get_slot_cls(handles, mixin)
            w_slot = cls(slot_num, methname, hpyslot.c_impl, w_type)
            w_type.setdictvalue(space, methname, w_slot)

    if not found:
        raise oefmt(space.w_NotImplementedError, "Unimplemented slot: %s", str(slot_num))
    return has_tp_call