Skip to content

Commit ca8b1b9

Browse files
committed
Implement 'PyStructSequence_Set/GetItem'.
1 parent 8e03cf1 commit ca8b1b9

File tree

9 files changed

+188
-18
lines changed

9 files changed

+188
-18
lines changed

graalpython/com.oracle.graal.python.cext/include/tupleobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ returned item's reference count.
3030
typedef struct {
3131
PyObject_VAR_HEAD
3232
// Truffle change: PyObject *ob_item[1] doesn't work for us in Sulong
33-
PyObject **ob_item;
33+
PyObject *ob_item[];
3434

3535
/* ob_item contains space for 'ob_size' elements.
3636
* Items must normally not be NULL, except during construction when

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,10 @@ PRIMITIVE_ARRAY_TO_NATIVE(Long, int64_t, i64, polyglot_as_i64);
402402
PRIMITIVE_ARRAY_TO_NATIVE(Double, double, double, polyglot_as_double);
403403
PRIMITIVE_ARRAY_TO_NATIVE(Object, PyObjectPtr, PyObjectPtr, (PyObjectPtr));
404404

405+
Py_ssize_t PyTruffle_Object_Size(PyObject *op) {
406+
return ((PyVarObject*)op)->ob_size;
407+
}
408+
405409
#define ReadMember(object, offset, T) ((T*)(((char*)object) + offset))[0]
406410

407411
int ReadShortMember(PyObject* object, Py_ssize_t offset) {

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

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ PyObject* PyStructSequence_New(PyTypeObject* o) {
4848

4949
UPCALL_ID(PyStructSequence_InitType2);
5050
int PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc) {
51-
PyObject *dict;
52-
PyMemberDef* members;
53-
Py_ssize_t n_members, n_unnamed_members, i, k;
54-
PyObject *v;
51+
Py_ssize_t n_members = desc->n_in_sequence;
52+
Py_ssize_t i;
5553

5654
memset(type, 0, sizeof(PyTypeObject));
5755

@@ -79,6 +77,7 @@ int PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
7977
type->tp_members = NULL;
8078
type->tp_new = newType->tp_new;
8179
type->tp_base = &PyTuple_Type;
80+
type->tp_alloc = PyType_GenericAlloc;
8281

8382
// now copy specific fields
8483
type->tp_name = newType->tp_name;
@@ -93,8 +92,40 @@ int PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
9392
return 0;
9493
}
9594

95+
PyTypeObject* PyStructSequence_NewType(PyStructSequence_Desc *desc) {
96+
Py_ssize_t n_members = desc->n_in_sequence;
97+
Py_ssize_t i;
98+
99+
// put field names and doc strings into two tuples
100+
PyObject* field_names = PyTuple_New(n_members);
101+
PyObject* field_docs = PyTuple_New(n_members);
102+
PyStructSequence_Field* fields = desc->fields;
103+
for (i = 0; i < n_members; i++) {
104+
PyTuple_SetItem(field_names, i, polyglot_from_string(fields[i].name, SRC_CS));
105+
PyTuple_SetItem(field_docs, i, polyglot_from_string(fields[i].doc, SRC_CS));
106+
}
107+
108+
// we create the new type managed
109+
PyTypeObject* newType = (PyTypeObject*) UPCALL_CEXT_O(_jls_PyStructSequence_InitType2,
110+
polyglot_from_string(desc->name, SRC_CS),
111+
polyglot_from_string(desc->doc, SRC_CS),
112+
native_to_java(field_names),
113+
native_to_java(field_docs));
114+
return newType;
115+
}
116+
96117
// taken from CPython "Objects/structseq.c"
97118
void PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc) {
98119
(void)PyStructSequence_InitType2(type, desc);
99120
}
100121

122+
// taken from CPython "Objects/structseq.c"
123+
void PyStructSequence_SetItem(PyObject* op, Py_ssize_t i, PyObject* v) {
124+
PyStructSequence_SET_ITEM(op, i, v);
125+
}
126+
127+
// taken from CPython "Objects/structseq.c"
128+
PyObject* PyStructSequence_GetItem(PyObject* op, Py_ssize_t i) {
129+
return PyStructSequence_GET_ITEM(op, i);
130+
}
131+

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@ static PyObject * tuple_create(PyObject *iterable) {
8888
return PySequence_Tuple(iterable);
8989
}
9090

91+
POLYGLOT_DECLARE_TYPE(PyTupleObject);
9192
PyObject * tuple_subtype_new(PyTypeObject *type, PyObject *iterable) {
92-
PyObject *tmp, *newobj, *item;
93+
PyTupleObject* newobj;
94+
PyObject *tmp, *item;
9395
Py_ssize_t i, n;
9496

9597
assert(PyType_IsSubtype(type, &PyTuple_Type));
@@ -100,15 +102,25 @@ PyObject * tuple_subtype_new(PyTypeObject *type, PyObject *iterable) {
100102
assert(PyTuple_Check(tmp));
101103
n = PyTuple_GET_SIZE(tmp);
102104

103-
newobj = type->tp_alloc(type, n);
105+
newobj = (PyTupleObject*) type->tp_alloc(type, n);
104106
if (newobj == NULL) {
105107
return NULL;
106108
}
109+
newobj = polyglot_from_PyTupleObject(newobj);
107110
for (i = 0; i < n; i++) {
108111
item = PyTuple_GetItem(tmp, i);
109112
Py_INCREF(item);
110-
PyTuple_SetItem(newobj, i, item);
113+
PyTuple_SetItem((PyObject*)newobj, i, item);
111114
}
112115
Py_DECREF(tmp);
113-
return newobj;
116+
117+
// This polyglot type cast is important such that we can directly read and
118+
// write members of the pointer from Java code.
119+
// Note: the return type is 'PyObject*' to be compatible with CPython
120+
return (PyObject*) newobj;
121+
}
122+
123+
int PyTruffle_Tuple_SetItem(PyObject* tuple, Py_ssize_t position, PyObject* item) {
124+
PyTuple_SET_ITEM(tuple, position, item);
125+
return 0;
114126
}

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

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,35 +38,65 @@
3838
# SOFTWARE.
3939

4040
import sys
41-
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, GRAALPYTHON
41+
from . import CPyExtType, CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, GRAALPYTHON
4242
__dir__ = __file__.rpartition("/")[0]
4343

44+
#
45+
# class TestPyStructSequenceTypes(object):
46+
# def test_properties(self):
47+
# TestPyStructSequence = CPyExtType("TestPyStructSequence",
48+
# """
49+
# static PyStructSequence_Field typeinfo_fields[] = {
50+
# {"element0", "The first element."},
51+
# {"element1", "The second element."},
52+
# {NULL, NULL,}
53+
# };
54+
#
55+
# static PyStructSequence_Desc typeinfo_desc = {
56+
# "TestPyStructSequenceTypes.TestPyStructSequence",
57+
# "Information about some custom struct type",
58+
# typeinfo_fields,
59+
# 2,
60+
# };
61+
# """,
62+
# ready_code="""if(PyStructSequence_InitType2(&TestPyStructSequenceType, &typeinfo_desc) < 0) {
63+
# return NULL;
64+
# }
65+
# Py_INCREF(&TestPyStructSequenceType);
66+
# """,
67+
# )
68+
# assert TestPyStructSequence.__doc__ == "Information about some custom struct type"
69+
#
70+
# tester = TestPyStructSequence()
71+
# assert hasattr(tester.element0)
72+
# assert hasattr(tester.element1)
4473

45-
class TestPyStructSequence(CPyExtTestCase):
4674

75+
class TestPyStructSequence(CPyExtTestCase):
76+
4777
def compile_module(self, name):
4878
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
4979
super(TestPyStructSequence, self).compile_module(name)
50-
80+
5181
test_PyStructSequence_InitType2 = CPyExtFunction(
5282
lambda args: 0,
5383
lambda: ( (0,), ),
5484
code='''
5585
static PyTypeObject CustomStructSeqType;
56-
86+
5787
static PyStructSequence_Field typeinfo_fields[] = {
5888
{"element0", "The first element."},
5989
{"element1", "The second element."},
6090
{NULL, NULL,}
6191
};
62-
92+
6393
static PyStructSequence_Desc typeinfo_desc = {
6494
"custom.named.tuple",
6595
"Information about some custom struct type",
6696
typeinfo_fields,
6797
2,
6898
};
69-
99+
70100
int wrap_PyStructSequence_InitType2(int n) {
71101
return PyStructSequence_InitType2(&CustomStructSeqType, &typeinfo_desc);
72102
}
@@ -78,3 +108,64 @@ def compile_module(self, name):
78108
cmpfunc=unhandled_error_compare
79109
)
80110

111+
test_PyStructSequence_Usage = CPyExtFunction(
112+
lambda args: args[0],
113+
lambda: (
114+
(("hello", "world",),),
115+
# ("john", "doe",),
116+
# (1, "doe",),
117+
# ("john", False,),
118+
# ("john", None,),
119+
),
120+
code='''
121+
static PyTypeObject CustomStructSeqType;
122+
123+
static PyStructSequence_Field typeinfo_fields[] = {
124+
{"element0", "The first element."},
125+
{"element1", "The second element."},
126+
{NULL, NULL,}
127+
};
128+
129+
static PyStructSequence_Desc typeinfo_desc = {
130+
"custom.named.tuple",
131+
"Information about some custom struct type",
132+
typeinfo_fields,
133+
2,
134+
};
135+
136+
PyObject* wrap_PyStructSequence_Usage(PyObject* elements) {
137+
Py_ssize_t i = 0;
138+
PyObject* elem = NULL;
139+
Py_INCREF(elements);
140+
141+
PyStructSequence_InitType2(&CustomStructSeqType, &typeinfo_desc);
142+
143+
PyObject* struct_obj = PyStructSequence_New(&CustomStructSeqType);
144+
if (struct_obj == NULL) {
145+
return NULL;
146+
}
147+
for (i = 0; i < PyObject_Length(elements); i++) {
148+
elem = PyTuple_GetItem(elements, i);
149+
Py_INCREF(elem);
150+
PyStructSequence_SetItem(struct_obj, i, elem);
151+
}
152+
153+
PyObject* result = PyTuple_New(PyObject_Length(struct_obj));
154+
if (result == NULL) {
155+
return NULL;
156+
}
157+
for (i = 0; i < PyObject_Length(struct_obj); i++) {
158+
elem = PyStructSequence_GetItem(struct_obj, i);
159+
Py_INCREF(elem);
160+
PyTuple_SetItem(result, i, elem);
161+
}
162+
Py_INCREF(result);
163+
return result;
164+
}
165+
''',
166+
resultspec="O",
167+
argspec='O',
168+
arguments=["PyObject* elements"],
169+
callfunction="wrap_PyStructSequence_Usage",
170+
cmpfunc=unhandled_error_compare
171+
)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PythonCextBuiltins.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,14 @@
8181
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.MayRaiseNodeFactory;
8282
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.MayRaiseTernaryNode;
8383
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.MayRaiseUnaryNode;
84+
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.PCallCapiFunction;
8485
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory;
8586
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.MayRaiseBinaryNodeGen;
8687
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.MayRaiseTernaryNodeGen;
8788
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.MayRaiseUnaryNodeGen;
8889
import com.oracle.graal.python.builtins.objects.cext.DynamicObjectNativeWrapper;
8990
import com.oracle.graal.python.builtins.objects.cext.HandleCache;
91+
import com.oracle.graal.python.builtins.objects.cext.NativeCAPISymbols;
9092
import com.oracle.graal.python.builtins.objects.cext.PThreadState;
9193
import com.oracle.graal.python.builtins.objects.cext.PySequenceArrayWrapper;
9294
import com.oracle.graal.python.builtins.objects.cext.PythonClassInitNativeWrapper;
@@ -324,12 +326,23 @@ PTuple doGeneric(Object size,
324326
@GenerateNodeFactory
325327
abstract static class PyTuple_SetItem extends PythonTernaryBuiltinNode {
326328
@Specialization
327-
int doI(PTuple tuple, Object position, Object element,
329+
int doManaged(PTuple tuple, Object position, Object element,
328330
@Cached("createSetItem()") SequenceStorageNodes.SetItemNode setItemNode) {
329331
setItemNode.execute(tuple.getSequenceStorage(), position, element);
330332
return 0;
331333
}
332334

335+
@Specialization(limit = "1")
336+
int doNative(PythonNativeObject tuple, long position, Object element,
337+
@Cached PCallCapiFunction callSetItem,
338+
@Cached CExtNodes.ToSulongNode receiverToSulongNode,
339+
@Cached CExtNodes.ToSulongNode elementToSulongNode) {
340+
// TODO(fa): This path should be avoided since this is called from native code to do a
341+
// native operation.
342+
callSetItem.call(NativeCAPISymbols.FUN_PY_TRUFFLE_TUPLE_SET_ITEM, receiverToSulongNode.execute(tuple), position, elementToSulongNode.execute(element));
343+
return 0;
344+
}
345+
333346
protected static SequenceStorageNodes.SetItemNode createSetItem() {
334347
return SequenceStorageNodes.SetItemNode.create(NormalizeIndexNode.forTupleAssign(), "invalid item for assignment");
335348
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/NativeCAPISymbols.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ public abstract class NativeCAPISymbols {
8181
public static final String FUN_PY_OBJECT_GENERIC_NEW = "PyTruffle_Type_GenericNew";
8282
public static final String FUN_GET_THREAD_STATE_TYPE_ID = "get_thread_state_typeid";
8383
public static final String FUN_ADD_NATIVE_SLOTS = "PyTruffle_Type_AddSlots";
84+
public static final String FUN_PY_TRUFFLE_TUPLE_SET_ITEM = "PyTruffle_Tuple_SetItem";
85+
public static final String FUN_PY_TRUFFLE_OBJECT_SIZE = "PyTruffle_Object_Size";
8486

8587
@CompilationFinal(dimensions = 1) private static final String[] values;
8688
static {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@
5555
import com.oracle.graal.python.builtins.modules.MathGuards;
5656
import com.oracle.graal.python.builtins.objects.PNone;
5757
import com.oracle.graal.python.builtins.objects.PNotImplemented;
58+
import com.oracle.graal.python.builtins.objects.cext.CExtNodes;
59+
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.PCallCapiFunction;
60+
import com.oracle.graal.python.builtins.objects.cext.NativeCAPISymbols;
61+
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
5862
import com.oracle.graal.python.builtins.objects.common.IndexNodes.NormalizeIndexNode;
5963
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
6064
import com.oracle.graal.python.builtins.objects.ints.PInt;
@@ -282,10 +286,18 @@ long count(VirtualFrame frame, PTuple self, Object value,
282286
@GenerateNodeFactory
283287
public abstract static class LenNode extends PythonUnaryBuiltinNode {
284288
@Specialization
285-
public int len(PTuple self,
289+
public int doManaged(PTuple self,
286290
@Cached("create()") SequenceStorageNodes.LenNode lenNode) {
287291
return lenNode.execute(self.getSequenceStorage());
288292
}
293+
294+
@Specialization
295+
public int doNative(PythonNativeObject self,
296+
@Cached PCallCapiFunction callSizeNode,
297+
@Cached CExtNodes.ToSulongNode toSulongNode,
298+
@Cached CastToJavaLongNode castToLongNode) {
299+
return (int) castToLongNode.execute(callSizeNode.call(NativeCAPISymbols.FUN_PY_TRUFFLE_OBJECT_SIZE, toSulongNode.execute(self)));
300+
}
289301
}
290302

291303
@Builtin(name = __REPR__, minNumOfPositionalArgs = 1)

graalpython/lib-graalpython/python_cext.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,11 +724,14 @@ def PyModule_AddObject(m, k, v):
724724
return None
725725

726726

727+
@may_raise
727728
def PyStructSequence_New(typ):
728-
return typ()
729+
n = len(typ._fields)
730+
return typ(*([None]*n))
729731

730732

731733
namedtuple_type = None
734+
@may_raise
732735
def PyStructSequence_InitType2(type_name, type_doc, field_names, field_docs):
733736
assert len(field_names) == len(field_docs)
734737
global namedtuple_type
@@ -740,6 +743,8 @@ def PyStructSequence_InitType2(type_name, type_doc, field_names, field_docs):
740743
prop = getattr(new_type, field_names[i])
741744
assert isinstance(prop, property)
742745
prop.__doc__ = field_docs[i]
746+
# ensure '_fields' attribute; required in 'PyStructSequence_New'
747+
assert hasattr(new_type, "_fields")
743748
return new_type
744749

745750

0 commit comments

Comments
 (0)