Skip to content

Commit a123165

Browse files
committed
[GR-53764] Migrate to sq_item and mp_item slots.
PullRequest: graalpython/3372
2 parents 6e74923 + 39172ad commit a123165

File tree

67 files changed

+2050
-701
lines changed

Some content is hidden

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

67 files changed

+2050
-701
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@
9494
enum SlotKind {
9595
nb_bool,
9696
sq_length,
97+
sq_item,
9798
mp_length,
99+
mp_subscript,
98100
tp_descr_get,
99101
tp_descr_set,
100102
tp_getattro,

graalpython/com.oracle.graal.python.hpy.test/src/hpytest/test_slots.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MIT License
22
#
3-
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
3+
# Copyright (c) 2020, 2024, Oracle and/or its affiliates.
44
# Copyright (c) 2019 pyhandle
55
#
66
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -633,7 +633,10 @@ def test_sq_item_and_sq_length(self):
633633
assert len(p) == 1234
634634
assert p[4] == 8
635635
assert p[21] == 42
636-
assert p[-1] == 1233 * 2
636+
assert p[-1] == 1233 * 2, str(p[-1])
637+
assert p.__getitem__(4) == 8
638+
assert p.__getitem__(21) == 42
639+
assert p.__getitem__(-1) == 1233 * 2
637640

638641
def test_sq_ass_item(self):
639642
import pytest

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ static String getSlotBaseClass(Slot s) {
5252
return switch (s.value()) {
5353
case nb_bool -> "TpSlotInquiry.TpSlotInquiryBuiltin";
5454
case sq_length, mp_length -> "TpSlotLen.TpSlotLenBuiltin" + getSuffix(s.isComplex());
55+
case sq_item -> "TpSlotSizeArgFun.TpSlotSizeArgFunBuiltin";
56+
case mp_subscript -> "TpSlotBinaryFunc.TpSlotMpSubscript";
5557
case tp_getattro -> "TpSlotGetAttr.TpSlotGetAttrBuiltin";
5658
case tp_descr_get -> "TpSlotDescrGet.TpSlotDescrGetBuiltin" + getSuffix(s.isComplex());
5759
case tp_descr_set -> "TpSlotDescrSet.TpSlotDescrSetBuiltin";
@@ -64,6 +66,8 @@ static String getSlotNodeBaseClass(Slot s) {
6466
case tp_descr_get -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode";
6567
case nb_bool -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.NbBoolBuiltinNode";
6668
case sq_length, mp_length -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotLen.LenBuiltinNode";
69+
case sq_item -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFun.SqItemBuiltinNode";
70+
case mp_subscript -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.MpSubscriptBuiltinNode";
6771
case tp_getattro -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.GetAttrBuiltinNode";
6872
case tp_descr_set -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet.DescrSetBuiltinNode";
6973
case tp_setattro -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotSetAttr.SetAttrBuiltinNode";
@@ -75,22 +79,24 @@ static String getUncachedExecuteSignature(SlotKind s) {
7579
case nb_bool -> "boolean executeUncached(Object self)";
7680
case tp_descr_get -> "Object executeUncached(Object self, Object obj, Object type)";
7781
case sq_length, mp_length -> "int executeUncached(Object self)";
78-
case tp_getattro, tp_descr_set, tp_setattro ->
82+
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript ->
7983
throw new AssertionError("Should not reach here: should be always complex");
8084
};
8185
}
8286

8387
static boolean supportsComplex(SlotKind s) {
8488
return switch (s) {
8589
case nb_bool -> false;
86-
case sq_length, mp_length, tp_getattro, tp_descr_get, tp_descr_set, tp_setattro -> true;
90+
case sq_length, mp_length, tp_getattro, tp_descr_get, tp_descr_set,
91+
tp_setattro, sq_item, mp_subscript ->
92+
true;
8793
};
8894
}
8995

9096
static boolean supportsSimple(SlotKind s) {
9197
return switch (s) {
9298
case nb_bool, sq_length, mp_length, tp_descr_get -> true;
93-
case tp_getattro, tp_descr_set, tp_setattro -> false;
99+
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript -> false;
94100
};
95101
}
96102

@@ -99,7 +105,7 @@ static String getUncachedExecuteCall(SlotKind s) {
99105
case nb_bool -> "executeBool(null, self)";
100106
case sq_length, mp_length -> "executeInt(null, self)";
101107
case tp_descr_get -> "execute(null, self, obj, type)";
102-
case tp_getattro, tp_descr_set, tp_setattro ->
108+
case tp_getattro, tp_descr_set, tp_setattro, sq_item, mp_subscript ->
103109
throw new AssertionError("Should not reach here: should be always complex");
104110
};
105111
}

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,14 @@
4040
*/
4141
package com.oracle.graal.python.test.builtin.objects;
4242

43+
import java.util.EnumSet;
44+
4345
import org.junit.Assert;
4446
import org.junit.Test;
4547

4648
import com.oracle.graal.python.builtins.objects.type.TpSlots;
4749
import com.oracle.graal.python.builtins.objects.type.TpSlots.Builder;
50+
import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotGroup;
4851
import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotMeta;
4952
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot;
5053
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative;
@@ -57,7 +60,7 @@ public void testBuilderBasic() {
5760
for (TpSlotMeta def : TpSlotMeta.VALUES) {
5861
// Use the TpSlotMeta as dummy "callable" object to verify that the slot values were
5962
// properly assigned to the right fields of TpSlots record
60-
builder.set(def, new TpSlotNative(def));
63+
builder.set(def, TpSlotNative.createCExtSlot(def));
6164
}
6265

6366
TpSlots slots = builder.build();
@@ -72,9 +75,9 @@ public void testBuilderBasic() {
7275
@Test
7376
public void testBuilderOptimizations1() {
7477
Builder builder = TpSlots.newBuilder();
75-
builder.set(TpSlotMeta.MP_LENGTH, new TpSlotNative(TpSlotMeta.MP_LENGTH));
76-
builder.set(TpSlotMeta.TP_GETATTR, new TpSlotNative(TpSlotMeta.TP_GETATTR));
77-
builder.set(TpSlotMeta.TP_SETATTR, new TpSlotNative(TpSlotMeta.TP_SETATTR));
78+
builder.set(TpSlotMeta.MP_LENGTH, TpSlotNative.createCExtSlot(TpSlotMeta.MP_LENGTH));
79+
builder.set(TpSlotMeta.TP_GETATTR, TpSlotNative.createCExtSlot(TpSlotMeta.TP_GETATTR));
80+
builder.set(TpSlotMeta.TP_SETATTR, TpSlotNative.createCExtSlot(TpSlotMeta.TP_SETATTR));
7881

7982
TpSlots slots = builder.build();
8083
verifySlots(slots, def -> def == TpSlotMeta.MP_LENGTH || def == TpSlotMeta.TP_GETATTR || def == TpSlotMeta.TP_SETATTR);
@@ -88,7 +91,7 @@ public void testBuilderOptimizations1() {
8891
@Test
8992
public void testBuilderOptimizations2() {
9093
Builder builder = TpSlots.newBuilder();
91-
builder.set(TpSlotMeta.SQ_LENGTH, new TpSlotNative(TpSlotMeta.SQ_LENGTH));
94+
builder.set(TpSlotMeta.SQ_LENGTH, TpSlotNative.createCExtSlot(TpSlotMeta.SQ_LENGTH));
9295

9396
TpSlots slots = builder.build();
9497
verifySlots(slots, def -> def == TpSlotMeta.SQ_LENGTH);
@@ -99,14 +102,25 @@ public void testBuilderOptimizations2() {
99102
}
100103

101104
private static void verifySlots(TpSlots slots, Function<TpSlotMeta, Boolean> checkNonNullValue) {
105+
var groupsSeen = EnumSet.noneOf(TpSlotGroup.class);
102106
for (TpSlotMeta def : TpSlotMeta.VALUES) {
103107
TpSlot slotValue = def.getValue(slots);
104108
if (checkNonNullValue.apply(def)) {
105109
checkSlotValue(def, slotValue);
110+
groupsSeen.add(def.getGroup());
106111
} else {
107112
Assert.assertNull(def.name(), slotValue);
108113
}
109114
}
115+
for (TpSlotGroup group : TpSlotGroup.values()) {
116+
switch (group) {
117+
case NO_GROUP -> {
118+
}
119+
case AS_NUMBER -> Assert.assertEquals(slots.has_as_number(), groupsSeen.contains(group));
120+
case AS_SEQUENCE -> Assert.assertEquals(slots.has_as_sequence(), groupsSeen.contains(group));
121+
case AS_MAPPING -> Assert.assertEquals(slots.has_as_mapping(), groupsSeen.contains(group));
122+
}
123+
}
110124
}
111125

112126
private static void checkSlotValue(TpSlotMeta def, TpSlot slotValue) {

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import array
4141
import collections
4242
import sys
43+
import mmap
4344

4445
from . import CPyExtTestCase, CPyExtFunction, CPyExtType, unhandled_error_compare
4546

@@ -177,6 +178,8 @@ def istypeof(obj, types):
177178
def _reference_getitem(args):
178179
seq = args[0]
179180
idx = args[1]
181+
if isinstanceof(seq, [mmap.mmap]):
182+
return seq[idx:(idx+1)]
180183
if not hasattr(seq, '__getitem__'):
181184
raise TypeError
182185
return seq.__getitem__(idx)
@@ -208,6 +211,13 @@ def wrapped_fun(args):
208211
return fun(args)
209212
return wrapped_fun
210213

214+
215+
def create_anon_mmap(data):
216+
mmap_object = mmap.mmap(-1, len(data) + 1)
217+
mmap_object.write(data)
218+
return mmap_object
219+
220+
211221
class NoNumber():
212222
pass
213223

@@ -1175,13 +1185,14 @@ class TestAbstract(CPyExtTestCase):
11751185
(DummyListSubclass(), 1),
11761186
('hello', 1),
11771187
(DictSubclassWithSequenceMethods(), 1),
1188+
(create_anon_mmap(b'hello world!'), 3),
11781189
),
11791190
resultspec="O",
11801191
argspec='On',
11811192
arguments=["PyObject* sequence", "Py_ssize_t idx"],
11821193
cmpfunc=unhandled_error_compare
11831194
)
1184-
1195+
11851196
test_PySequence_GetSlice = CPyExtFunction(
11861197
_wrap_slice_fun(lambda args: args[0][args[1]:args[2]]),
11871198
lambda: (
@@ -1331,6 +1342,7 @@ def _reference_delslice(args):
13311342
(['a', 'b', 'c'], 2),
13321343
([None], 0),
13331344
('hello', 0),
1345+
(create_anon_mmap(b'hello world!'), 3),
13341346
),
13351347
resultspec="O",
13361348
argspec='On',

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

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -644,10 +644,7 @@ def test_tp_as_buffer_pickle(self):
644644

645645

646646
class TestNativeSubclass(unittest.TestCase):
647-
def test_builtins(self):
648-
b = BytesSubclass(b"hello")
649-
assert is_native_object(b)
650-
assert type(b) == BytesSubclass
647+
def _verify_common(self, b):
651648
assert b
652649
assert not BytesSubclass(b'')
653650
assert len(b) == 5
@@ -660,13 +657,11 @@ def test_builtins(self):
660657
assert b + b" world" == b"hello world"
661658
assert b * 2 == b"hellohello"
662659
assert list(b) == [ord('h'), ord('e'), ord('l'), ord('l'), ord('o')]
663-
assert repr(b) == "b'hello'"
664660
assert b.index(ord('l')) == 2
665661
assert b.count(ord('l')) == 2
666662
assert b.decode('ascii') == 'hello'
667663
assert BytesSubclass(b'hello ').strip() == b'hello'
668664
assert BytesSubclass(b',').join([b'a', BytesSubclass(b'b')]) == b'a,b'
669-
assert hash(b) == hash(b'hello')
670665
assert BytesSubclass(b'(%s)') % b'a' == b'(a)'
671666
assert b.startswith(b'h')
672667
assert b.endswith(b'o')
@@ -676,8 +671,43 @@ def test_builtins(self):
676671
assert b.upper() == b'HELLO'
677672
assert BytesSubclass(b'a,b').split(BytesSubclass(b',')) == [b'a', b'b']
678673

679-
def test_managed_subclass(self):
674+
def _verify_bytes(self, b):
675+
self._verify_common(b)
676+
assert is_native_object(b)
677+
assert repr(b) == "b'hello'", repr(b)
678+
assert hash(b) == hash(b'hello')
679+
680+
def _verify_bytearray(self, b):
681+
self._verify_common(b)
682+
# assert is_native_object(b) # TODO: BuiltinConstructors.ByteArrayNode doesnt allocate native objects
683+
self.assertRaises(TypeError, lambda: hash(b))
684+
685+
def test_bytes_subclass(self):
686+
b = BytesSubclass(b"hello")
687+
assert type(b) == BytesSubclass
688+
self._verify_bytes(b)
689+
690+
def test_managed_bytes_subclass(self):
680691
class ManagedSubclass(BytesSubclass):
681692
pass
682693

683-
assert is_native_object(ManagedSubclass(b"hello"))
694+
b = ManagedSubclass(b"hello")
695+
assert type(b) == ManagedSubclass
696+
self._verify_bytes(b)
697+
698+
def test_bytearray_subclass(self):
699+
b = ByteArraySubclass(b"hello")
700+
assert type(b) == ByteArraySubclass
701+
# TODO: if TypeNode.GetNameNode is equivalent to _PyType_Name, it should transform tp_name "abc.xzy" to "xyz"
702+
# assert repr(b) == "ByteArraySubclass(b'hello')", repr(b)
703+
self._verify_bytearray(b)
704+
705+
def test_managed_bytearray_subclass(self):
706+
class ArrManagedSubclass(ByteArraySubclass):
707+
pass
708+
709+
b = ArrManagedSubclass(b"hello")
710+
assert type(b) == ArrManagedSubclass
711+
# TODO: if TypeNode.GetNameNode is equivalent to _PyType_Name, it should transform tp_name "abc.xzy" to "xyz"
712+
# assert repr(b) == "ArrManagedSubclass(b'hello')", repr(b)
713+
self._verify_bytearray(b)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1312,7 +1312,8 @@ def test_slot_precedence(self):
13121312
# Note: len(obj) uses 'PyObject_Lenght' which does not use the attribute but first tries
13131313
# 'sq_length' and falls back to 'mp_length'. Therefore, we just look at '__len__' here.
13141314
assert obj.__len__() == 222
1315-
assert obj['hello'] == 'mp_subscript'
1315+
assert len(obj) == 111
1316+
assert obj['hello'] == 'mp_subscript', obj['hello']
13161317
assert obj + 'hello' == 'mas_nb_add'
13171318

13181319
def test_take_ownership(self):

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,3 +379,62 @@ def test_type_not_ready():
379379
""", "NotReadyType")
380380
not_ready = module.create_not_ready()
381381
assert not_ready.foo == 'foo'
382+
383+
384+
def test_sq_len_and_item():
385+
MySqLenItem = CPyExtType("MySqLenItem",
386+
'''
387+
Py_ssize_t my_sq_len(PyObject* a) { return 10; }
388+
PyObject* my_sq_item(PyObject* self, Py_ssize_t index) { return PyLong_FromSsize_t(index); }
389+
''',
390+
sq_length="&my_sq_len",
391+
sq_item="my_sq_item")
392+
MySqItemAddDunderLen = CPyExtHeapType("MySqItemAddDunderLen",
393+
code = '''
394+
PyObject* my_sq_item(PyObject* self, Py_ssize_t index) { return PyLong_FromSsize_t(index); }
395+
''',
396+
slots=[
397+
'{Py_sq_item, &my_sq_item}',
398+
])
399+
MySqItemAddDunderLen.__len__ = lambda self: 10
400+
401+
def verify(x):
402+
assert x[5] == 5
403+
assert x.__getitem__(5) == 5
404+
assert x[-1] == 9
405+
assert x.__getitem__(-1) == 9
406+
assert x[-20] == -10
407+
assert x.__getitem__(-20) == -10
408+
409+
verify(MySqLenItem())
410+
verify(MySqItemAddDunderLen())
411+
412+
413+
def test_mp_len_and_sq_item():
414+
MyMpLenSqItem = CPyExtType("MyMpLenSqItem",
415+
'''
416+
Py_ssize_t my_mp_len(PyObject* a) { return 10; }
417+
PyObject* my_sq_item(PyObject* self, Py_ssize_t index) { return PyLong_FromSsize_t(index); }
418+
''',
419+
mp_length="&my_mp_len",
420+
sq_item="my_sq_item")
421+
MyMpLenSqItemHeap = CPyExtHeapType("MyMpLenSqItemHeap",
422+
code = '''
423+
Py_ssize_t my_mp_len(PyObject* a) { return 10; }
424+
PyObject* my_sq_item(PyObject* self, Py_ssize_t index) { return PyLong_FromSsize_t(index); }
425+
''',
426+
slots=[
427+
'{Py_mp_length, &my_mp_len}',
428+
'{Py_sq_item, &my_sq_item}',
429+
])
430+
def verify(x):
431+
assert x[5] == 5
432+
assert x.__getitem__(5) == 5
433+
# no sq_length, negative index is just passed to sq_item
434+
assert x[-1] == -1
435+
assert x.__getitem__(-1) == -1
436+
assert x[-20] == -20
437+
assert x.__getitem__(-20) == -20
438+
439+
verify(MyMpLenSqItem())
440+
verify(MyMpLenSqItemHeap())

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,7 @@ class TestPyTuple(CPyExtTestCase):
246246

247247

248248
class TestNativeSubclass(unittest.TestCase):
249-
def test_builtins(self):
250-
t = TupleSubclass(1, 2, 3)
251-
assert type(t) == TupleSubclass
249+
def _verify(self, t):
252250
assert is_native_object(t)
253251
assert t
254252
assert len(t) == 3
@@ -265,8 +263,16 @@ def test_builtins(self):
265263
assert t.index(2) == 1
266264
assert t.count(2) == 1
267265

266+
def test_builtins(self):
267+
t = TupleSubclass(1, 2, 3)
268+
assert type(t) == TupleSubclass
269+
self._verify(t)
270+
268271
def test_managed_sublclass(self):
269272
class ManagedSubclass(TupleSubclass):
270273
pass
271274

272-
assert is_native_object(ManagedSubclass(1, 2, 3))
275+
t = ManagedSubclass(1, 2, 3)
276+
assert is_native_object(t)
277+
assert type(t) == ManagedSubclass
278+
self._verify(t)

0 commit comments

Comments
 (0)