Skip to content

Commit b907353

Browse files
committed
[GR-57635] Implement sq_repeat and nb_multiply CPython like slots.
PullRequest: graalpython/3478
2 parents a3bebd0 + 8b241b1 commit b907353

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+657
-426
lines changed

graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/Slot.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,15 @@ enum SlotKind {
9797
nb_bool("__bool__"),
9898
/** foo + bar */
9999
nb_add("__add__, __radd__"),
100+
nb_multiply("__mul__, __rmul__"),
100101
/** sequence length/size */
101102
sq_length("__len__"),
102103
/** sequence item: read element at index */
103104
sq_item("__getitem__"),
104105
/** seq + seq, nb_add is tried before */
105106
sq_concat("__add__"),
107+
/** seq * number, nb_multiply is tried before */
108+
sq_repeat("__mul__"),
106109
/** mapping length */
107110
mp_length("__len__"),
108111
/** mapping subscript, e.g. o[key], o[i:j] */

graalpython/com.oracle.graal.python.cext/src/abstract.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,7 @@ PyNumber_Check(PyObject *o)
885885
PyNumberMethods *nb = Py_TYPE(o)->tp_as_number;
886886
return nb && (nb->nb_index || nb->nb_int || nb->nb_float || PyComplex_Check(o));
887887
}
888+
#endif // GraalPy
888889

889890
/* Binary operators */
890891

@@ -1108,6 +1109,7 @@ ternary_op(PyObject *v,
11081109
return binary_op(v, w, NB_SLOT(op), op_name); \
11091110
}
11101111

1112+
#if 0 // GraalPy
11111113
BINARY_FUNC(PyNumber_Or, nb_or, "|")
11121114
BINARY_FUNC(PyNumber_Xor, nb_xor, "^")
11131115
BINARY_FUNC(PyNumber_And, nb_and, "&")
@@ -1754,6 +1756,7 @@ PySequence_Concat(PyObject *s, PyObject *o)
17541756
}
17551757
return type_error("'%.200s' object can't be concatenated", s);
17561758
}
1759+
#endif // GraalPy
17571760

17581761
PyObject *
17591762
PySequence_Repeat(PyObject *o, Py_ssize_t count)
@@ -1786,6 +1789,7 @@ PySequence_Repeat(PyObject *o, Py_ssize_t count)
17861789
return type_error("'%.200s' object can't be repeated", o);
17871790
}
17881791

1792+
#if 0 // GraalPy change
17891793
PyObject *
17901794
PySequence_InPlaceConcat(PyObject *s, PyObject *o)
17911795
{

graalpython/com.oracle.graal.python.cext/src/capi.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ Py_LOCAL_SYMBOL int is_builtin_type(PyTypeObject *tp);
194194
#define JWRAPPER_METHOD 8
195195
#define JWRAPPER_UNSUPPORTED 9
196196
#define JWRAPPER_ALLOC 10
197-
#define JWRAPPER_SSIZE_ARG JWRAPPER_ALLOC
198197
#define JWRAPPER_GETATTR 11
199198
#define JWRAPPER_SETATTR 12
200199
#define JWRAPPER_RICHCMP 13
@@ -232,6 +231,7 @@ Py_LOCAL_SYMBOL int is_builtin_type(PyTypeObject *tp);
232231
#define JWRAPPER_REPR 45
233232
#define JWRAPPER_DESCR_DELETE 46
234233
#define JWRAPPER_DELATTRO 47
234+
#define JWRAPPER_SSIZE_ARG 48
235235

236236

237237
static inline int get_method_flags_wrapper(int flags) {

graalpython/com.oracle.graal.python.cext/src/typeobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9383,6 +9383,7 @@ static int type_ready_graalpy_slot_conv(PyTypeObject* cls) {
93839383
ADD_SLOT_CONV("__len__", sequences->sq_length, -1, JWRAPPER_LENFUNC);
93849384
ADD_SLOT_CONV("__add__", sequences->sq_concat, -2, JWRAPPER_BINARYFUNC);
93859385
ADD_SLOT_CONV("__mul__", sequences->sq_repeat, -2, JWRAPPER_SSIZE_ARG);
9386+
ADD_SLOT_CONV("__rmul__", sequences->sq_repeat, -2, JWRAPPER_SSIZE_ARG);
93869387
ADD_SLOT_CONV("__getitem__", sequences->sq_item, -2, JWRAPPER_GETITEM);
93879388
ADD_SLOT_CONV("__setitem__", sequences->sq_ass_item, -3, JWRAPPER_SETITEM);
93889389
ADD_SLOT_CONV("__delitem__", sequences->sq_ass_item, -3, JWRAPPER_DELITEM);

graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/SlotsMapping.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ private static String getSuffix(boolean isComplex) {
5252
static String getSlotBaseClass(Slot s) {
5353
return switch (s.value()) {
5454
case nb_bool -> "TpSlotInquiry.TpSlotInquiryBuiltin";
55-
case nb_add -> "TpSlotBinaryOp.TpSlotBinaryOpBuiltin";
55+
case nb_add, nb_multiply -> "TpSlotBinaryOp.TpSlotBinaryOpBuiltin";
5656
case sq_concat -> "TpSlotBinaryFunc.TpSlotSqConcat";
5757
case sq_length, mp_length -> "TpSlotLen.TpSlotLenBuiltin" + getSuffix(s.isComplex());
58-
case sq_item -> "TpSlotSizeArgFun.TpSlotSizeArgFunBuiltin";
58+
case sq_item, sq_repeat -> "TpSlotSizeArgFun.TpSlotSizeArgFunBuiltin";
5959
case mp_subscript -> "TpSlotBinaryFunc.TpSlotMpSubscript";
6060
case tp_getattro -> "TpSlotGetAttr.TpSlotGetAttrBuiltin";
6161
case tp_descr_get -> "TpSlotDescrGet.TpSlotDescrGetBuiltin" + getSuffix(s.isComplex());
@@ -68,10 +68,11 @@ static String getSlotNodeBaseClass(Slot s) {
6868
return switch (s.value()) {
6969
case tp_descr_get -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode";
7070
case nb_bool -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.NbBoolBuiltinNode";
71-
case nb_add -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode";
71+
case nb_add, nb_multiply -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode";
7272
case sq_concat -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.SqConcatBuiltinNode";
7373
case sq_length, mp_length -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotLen.LenBuiltinNode";
7474
case sq_item -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFun.SqItemBuiltinNode";
75+
case sq_repeat -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFun.SqRepeatBuiltinNode";
7576
case mp_subscript -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.MpSubscriptBuiltinNode";
7677
case tp_getattro -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.GetAttrBuiltinNode";
7778
case tp_descr_set -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet.DescrSetBuiltinNode";
@@ -84,7 +85,7 @@ static String getUncachedExecuteSignature(SlotKind s) {
8485
case nb_bool -> "boolean executeUncached(Object self)";
8586
case tp_descr_get -> "Object executeUncached(Object self, Object obj, Object type)";
8687
case sq_length, mp_length -> "int executeUncached(Object self)";
87-
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript, nb_add, sq_concat ->
88+
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript, nb_add, sq_concat, sq_repeat, nb_multiply ->
8889
throw new AssertionError("Should not reach here: should be always complex");
8990
};
9091
}
@@ -93,15 +94,18 @@ static boolean supportsComplex(SlotKind s) {
9394
return switch (s) {
9495
case nb_bool -> false;
9596
case sq_length, mp_length, tp_getattro, tp_descr_get, tp_descr_set,
96-
tp_setattro, sq_item, mp_subscript, nb_add, sq_concat ->
97+
tp_setattro, sq_item, mp_subscript, nb_add, sq_concat,
98+
sq_repeat, nb_multiply ->
9799
true;
98100
};
99101
}
100102

101103
static boolean supportsSimple(SlotKind s) {
102104
return switch (s) {
103105
case nb_bool, sq_length, mp_length, tp_descr_get -> true;
104-
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript, nb_add, sq_concat -> false;
106+
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript,
107+
nb_add, sq_concat, sq_repeat, nb_multiply ->
108+
false;
105109
};
106110
}
107111

@@ -110,18 +114,17 @@ static String getUncachedExecuteCall(SlotKind s) {
110114
case nb_bool -> "executeBool(null, self)";
111115
case sq_length, mp_length -> "executeInt(null, self)";
112116
case tp_descr_get -> "execute(null, self, obj, type)";
113-
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript, nb_add, sq_concat ->
117+
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript,
118+
nb_add, sq_concat, nb_multiply, sq_repeat ->
114119
throw new AssertionError("Should not reach here: should be always complex");
115120
};
116121
}
117122

118123
public static String getExtraCtorArgs(TpSlotData slot) {
119124
return switch (slot.slot().value()) {
120125
case nb_add -> ", com.oracle.graal.python.nodes.SpecialMethodNames.J___ADD__";
121-
case nb_bool, tp_setattro, tp_getattro,
122-
tp_descr_set, tp_descr_get, mp_subscript,
123-
mp_length, sq_concat, sq_item, sq_length ->
124-
"";
126+
case nb_multiply -> ", com.oracle.graal.python.nodes.SpecialMethodNames.J___MUL__";
127+
default -> "";
125128
};
126129
}
127130
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
// Generated by the slots_fuzzer.py
42+
#include <Python.h>
43+
44+
PyObject *global_stash1;
45+
PyObject *global_stash2;
46+
47+
int Native0_nb_bool(PyObject *self) { return 1; }
48+
PyObject *Native0_nb_add(PyObject *self, PyObject *other) {
49+
return Py_NewRef(self);
50+
}
51+
Py_ssize_t Native0_sq_length(PyObject *self) { return 1; }
52+
PyObject *Native0_sq_concat(PyObject *self, PyObject *other) {
53+
return PyLong_FromLong(10);
54+
}
55+
PyObject *Native0_sq_repeat(PyObject *self, Py_ssize_t count) {
56+
return PyLong_FromLong(count);
57+
}
58+
Py_ssize_t Native0_mp_length(PyObject *self) { return 42; }
59+
PyObject *Native0_tp_getattr(PyObject *self, char *name) {
60+
return Py_NewRef(self);
61+
}
62+
PyObject *Native0_tp_getattro(PyObject *self, PyObject *name) {
63+
return Py_NewRef(self);
64+
}
65+
PyObject *Native0_tp_descr_get(PyObject *self, PyObject *key, PyObject *type) {
66+
Py_RETURN_NONE;
67+
}
68+
int Native0_tp_descr_set(PyObject *self, PyObject *key, PyObject *value) {
69+
return 0;
70+
}
71+
72+
PyNumberMethods Native0_tp_as_number = {
73+
.nb_bool = &Native0_nb_bool,
74+
.nb_add = &Native0_nb_add,
75+
};
76+
PySequenceMethods Native0_tp_as_sequence = {
77+
.sq_length = &Native0_sq_length,
78+
.sq_concat = &Native0_sq_concat,
79+
.sq_repeat = &Native0_sq_repeat,
80+
};
81+
PyMappingMethods Native0_tp_as_mapping = {
82+
.mp_length = &Native0_mp_length,
83+
};
84+
85+
static PyTypeObject CustomType_Native0 = {
86+
.ob_base = PyVarObject_HEAD_INIT(NULL, 0).tp_name = "test10.Native0",
87+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
88+
.tp_new = PyType_GenericNew,
89+
.tp_as_number = &Native0_tp_as_number,
90+
.tp_as_sequence = &Native0_tp_as_sequence,
91+
.tp_as_mapping = &Native0_tp_as_mapping,
92+
.tp_getattr = &Native0_tp_getattr,
93+
.tp_getattro = &Native0_tp_getattro,
94+
.tp_descr_get = &Native0_tp_descr_get,
95+
.tp_descr_set = &Native0_tp_descr_set,
96+
97+
};
98+
99+
static PyObject *create_Native0(PyObject *module, PyObject *args) {
100+
if (PyType_Ready(&CustomType_Native0) < 0)
101+
return NULL;
102+
Py_INCREF(&CustomType_Native0);
103+
return (PyObject *)&CustomType_Native0;
104+
}
105+
106+
static struct PyMethodDef test_module_methods[] = {
107+
{"create_Native0", (PyCFunction)create_Native0, METH_VARARGS, ""},
108+
{NULL, NULL, 0, NULL}};
109+
static PyModuleDef test_module = {PyModuleDef_HEAD_INIT,
110+
"fuzzer_test10",
111+
"",
112+
-1,
113+
test_module_methods,
114+
NULL,
115+
NULL,
116+
NULL,
117+
NULL};
118+
119+
PyMODINIT_FUNC PyInit_fuzzer_test10(void) {
120+
return PyModule_Create(&test_module);
121+
}

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abstract.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646

4747
__dir__ = __file__.rpartition("/")[0]
4848

49+
from .test_modsupport import MySeq
50+
4951

5052
def _safe_check(v, type_check):
5153
try:
@@ -291,13 +293,40 @@ def __len__(self):
291293
return 42
292294

293295

296+
class SeqWithMulAdd:
297+
def __getitem__(self, item):
298+
return item
299+
300+
def __mul__(self, other):
301+
return "mul:" + str(other)
302+
303+
def __add__(self, other):
304+
return "add:" + str(other)
305+
306+
def __str__(self):
307+
return "SeqWithMulAdd"
308+
309+
class NonSeqWithMulAdd:
310+
def __mul__(self, other):
311+
return "not expected!"
312+
313+
def __add__(self, other):
314+
return "not expected!"
315+
316+
294317
class DictSubclassWithSequenceMethods(dict):
295318
def __getitem__(self, key):
296319
return key
297320

298321
def __setitem__(self, key, value):
299322
pass
300323

324+
def __add__(self, other):
325+
return "not expected!"
326+
327+
def __mul__(self, other):
328+
return "not expected!"
329+
301330

302331
def _default_bin_arith_args():
303332
return (
@@ -1487,8 +1516,17 @@ def _reference_delslice(args):
14871516
cmpfunc=unhandled_error_compare
14881517
)
14891518

1519+
def _reference_seq_repeat(args):
1520+
match args[0]:
1521+
case SeqWithMulAdd():
1522+
return "mul:" + str(args[1])
1523+
case NonSeqWithMulAdd() | DictSubclassWithSequenceMethods():
1524+
raise TypeError(f"{type(args[1])} object can't be repeated")
1525+
case _:
1526+
return args[0] * args[1]
1527+
14901528
test_PySequence_Repeat = CPyExtFunction(
1491-
lambda args: args[0] * args[1],
1529+
_reference_seq_repeat,
14921530
lambda: (
14931531
((1,), 0),
14941532
((1,), 1),
@@ -1500,6 +1538,9 @@ def _reference_delslice(args):
15001538
("hello", 1),
15011539
("hello", 3),
15021540
({}, 0),
1541+
(SeqWithMulAdd(), 42),
1542+
(NonSeqWithMulAdd(), 24),
1543+
(DictSubclassWithSequenceMethods(), 5),
15031544
),
15041545
resultspec="O",
15051546
argspec='On',
@@ -1527,8 +1568,19 @@ def _reference_delslice(args):
15271568
cmpfunc=unhandled_error_compare
15281569
)
15291570

1571+
def _reference_seq_concat(args):
1572+
match args[0]:
1573+
case SeqWithMulAdd():
1574+
if hasattr(args[1], "__getitem__"):
1575+
return "add:" + str(args[1])
1576+
raise TypeError("SeqWithMulAdd object can't be concatenated")
1577+
case NonSeqWithMulAdd() | DictSubclassWithSequenceMethods():
1578+
raise TypeError(f"{type(args[1])} object can't be concatenated")
1579+
case _:
1580+
return args[0] + args[1]
1581+
15301582
test_PySequence_Concat = CPyExtFunction(
1531-
lambda args: args[0] + args[1],
1583+
_reference_seq_concat,
15321584
lambda: (
15331585
((1,), tuple()),
15341586
((1,), list()),
@@ -1542,6 +1594,13 @@ def _reference_delslice(args):
15421594
("hello", ""),
15431595
({}, []),
15441596
([], {}),
1597+
(SeqWithMulAdd(), 1),
1598+
(SeqWithMulAdd(), SeqWithMulAdd()),
1599+
(SeqWithMulAdd(), [1,2,3]),
1600+
(NonSeqWithMulAdd(), 2),
1601+
(NonSeqWithMulAdd(), [1,2,3]),
1602+
(DictSubclassWithSequenceMethods(), (1,2,3)),
1603+
((1,2,3), DictSubclassWithSequenceMethods()),
15451604
),
15461605
resultspec="O",
15471606
argspec='OO',

0 commit comments

Comments
 (0)