Skip to content

Commit 75d8864

Browse files
committed
[GR-32014] Use intrinsified StructSequence for C API.
PullRequest: graalpython/1848
2 parents 988ffaf + fa01ddf commit 75d8864

File tree

19 files changed

+565
-244
lines changed

19 files changed

+565
-244
lines changed

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

Lines changed: 68 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
#define REAL_SIZE_TP(tp) PyLong_AsSsize_t(PyDict_GetItem((tp)->tp_dict, polyglot_from_string("n_fields", SRC_CS)))
4444
#define REAL_SIZE(op) REAL_SIZE_TP(Py_TYPE(op))
4545

46+
char *PyStructSequence_UnnamedField = "unnamed field";
47+
4648
static void
4749
structseq_dealloc(PyStructSequence *obj)
4850
{
@@ -55,87 +57,104 @@ structseq_dealloc(PyStructSequence *obj)
5557
PyObject_GC_Del(obj);
5658
}
5759

58-
/* StructSequences a.k.a. 'namedtuple' */
59-
UPCALL_ID(PyStructSequence_New);
60+
typedef PyObject *(*structseq_new_fun_t)(PyTypeObject *);
61+
UPCALL_TYPED_ID(PyStructSequence_New, structseq_new_fun_t);
6062
PyObject* PyStructSequence_New(PyTypeObject* o) {
61-
return UPCALL_CEXT_O(_jls_PyStructSequence_New, native_type_to_java(o));
63+
return _jls_PyStructSequence_New(native_type_to_java(o));
6264
}
6365

64-
UPCALL_ID(PyStructSequence_InitType2);
65-
int PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc) {
66-
Py_ssize_t n_members = desc->n_in_sequence;
66+
static Py_ssize_t
67+
count_members(PyStructSequence_Desc *desc, Py_ssize_t *n_unnamed_members) {
6768
Py_ssize_t i;
6869

69-
memset(type, 0, sizeof(PyTypeObject));
70-
71-
// put field names and doc strings into two tuples
72-
PyObject* field_names = PyTuple_New(n_members);
73-
PyObject* field_docs = PyTuple_New(n_members);
74-
PyStructSequence_Field* fields = desc->fields;
75-
for (i = 0; i < n_members; i++) {
76-
PyTuple_SetItem(field_names, i, polyglot_from_string(fields[i].name, SRC_CS));
77-
PyTuple_SetItem(field_docs, i, polyglot_from_string(fields[i].doc, SRC_CS));
70+
*n_unnamed_members = 0;
71+
for (i = 0; desc->fields[i].name != NULL; ++i) {
72+
if (desc->fields[i].name == PyStructSequence_UnnamedField) {
73+
(*n_unnamed_members)++;
74+
}
7875
}
76+
return i;
77+
}
7978

80-
// we create the new type managed
81-
PyTypeObject* newType = (PyTypeObject*) UPCALL_CEXT_O(_jls_PyStructSequence_InitType2,
82-
polyglot_from_string(desc->name, SRC_CS),
83-
polyglot_from_string(desc->doc, SRC_CS),
84-
native_to_java(field_names),
85-
native_to_java(field_docs));
79+
typedef int (*structseq_init_fun_t)(void *, void *, void *, int);
80+
UPCALL_TYPED_ID(PyStructSequence_InitType2, structseq_init_fun_t);
81+
int PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc) {
82+
Py_ssize_t n_members, n_unnamed_members, n_named_members;
83+
Py_ssize_t i;
84+
85+
memset(type, 0, sizeof(PyTypeObject));
8686

87-
if (newType == NULL) {
88-
Py_DECREF(field_names);
89-
Py_DECREF(field_docs);
90-
return -1;
91-
}
9287

9388
// copy generic fields (CPython mem-copies a template)
89+
type->tp_name = desc->name;
9490
type->tp_basicsize = sizeof(PyStructSequence) - sizeof(PyObject *);
9591
type->tp_itemsize = sizeof(PyObject *);
96-
type->tp_repr = newType->tp_repr;
9792
type->tp_flags = Py_TPFLAGS_DEFAULT;
9893
type->tp_members = NULL;
99-
type->tp_new = newType->tp_new;
94+
type->tp_doc = desc->doc;
10095
type->tp_base = &PyTuple_Type;
101-
type->tp_alloc = newType->tp_alloc;
10296
type->tp_dealloc = (destructor)structseq_dealloc;
10397

104-
// now copy specific fields
105-
type->tp_name = newType->tp_name;
106-
type->tp_doc = newType->tp_doc;
107-
type->tp_dict = newType->tp_dict;
108-
109-
Py_DECREF(newType);
110-
11198
// now initialize the type
11299
if (PyType_Ready(type) < 0)
113100
return -1;
114101
Py_INCREF(type);
115102

116-
return 0;
103+
n_members = count_members(desc, &n_unnamed_members);
104+
n_named_members = n_members - n_unnamed_members;
105+
// put field names and doc strings into two lists
106+
void** field_names = (void **) truffle_managed_malloc(n_named_members * sizeof(void *));
107+
void** field_docs = (void **) truffle_managed_malloc(n_named_members * sizeof(void *));
108+
PyStructSequence_Field* fields = desc->fields;
109+
int j = 0;
110+
for (i = 0; i < n_members; i++) {
111+
if (fields[i].name != PyStructSequence_UnnamedField) {
112+
field_names[j] = polyglot_from_string(fields[i].name, SRC_CS);
113+
field_docs[j] = polyglot_from_string(fields[i].doc, SRC_CS);
114+
j++;
115+
}
116+
}
117+
118+
// this initializes the type dict (adds attributes)
119+
return _jls_PyStructSequence_InitType2(
120+
native_type_to_java(type),
121+
/* TODO(fa): use polyglot_from_VoidPtr_array once this is visible */
122+
polyglot_from_PyObjectPtr_array((PyObjectPtr *) field_names, (uint64_t) n_members),
123+
polyglot_from_PyObjectPtr_array((PyObjectPtr *) field_docs, (uint64_t) n_members),
124+
desc->n_in_sequence
125+
);
117126
}
118127

128+
typedef PyTypeObject *(*structseq_newtype_fun_t)(void *, void *, void *, void *, int);
129+
UPCALL_TYPED_ID(PyStructSequence_NewType, structseq_newtype_fun_t);
119130
PyTypeObject* PyStructSequence_NewType(PyStructSequence_Desc *desc) {
120-
Py_ssize_t n_members = desc->n_in_sequence;
131+
Py_ssize_t n_members, n_unnamed_members, n_named_members;
121132
Py_ssize_t i;
122133

123-
// put field names and doc strings into two tuples
124-
PyObject* field_names = PyTuple_New(n_members);
125-
PyObject* field_docs = PyTuple_New(n_members);
134+
n_members = count_members(desc, &n_unnamed_members);
135+
n_named_members = n_members - n_unnamed_members;
136+
// put field names and doc strings into two lists
137+
void** field_names = (void **) truffle_managed_malloc(n_named_members * sizeof(void *));
138+
void** field_docs = (void **) truffle_managed_malloc(n_named_members * sizeof(void *));
126139
PyStructSequence_Field* fields = desc->fields;
140+
int j = 0;
127141
for (i = 0; i < n_members; i++) {
128-
PyTuple_SetItem(field_names, i, polyglot_from_string(fields[i].name, SRC_CS));
129-
PyTuple_SetItem(field_docs, i, polyglot_from_string(fields[i].doc, SRC_CS));
142+
if (fields[i].name != PyStructSequence_UnnamedField) {
143+
field_names[j] = polyglot_from_string(fields[i].name, SRC_CS);
144+
field_docs[j] = polyglot_from_string(fields[i].doc, SRC_CS);
145+
j++;
146+
}
130147
}
131148

132149
// we create the new type managed
133-
PyTypeObject* newType = (PyTypeObject*) UPCALL_CEXT_O(_jls_PyStructSequence_InitType2,
134-
polyglot_from_string(desc->name, SRC_CS),
135-
polyglot_from_string(desc->doc, SRC_CS),
136-
native_to_java(field_names),
137-
native_to_java(field_docs));
138-
return newType;
150+
return _jls_PyStructSequence_NewType(
151+
polyglot_from_string(desc->name, SRC_CS),
152+
polyglot_from_string(desc->doc, SRC_CS),
153+
/* TODO(fa): use polyglot_from_VoidPtr_array once this is visible */
154+
polyglot_from_PyObjectPtr_array((PyObjectPtr *) field_names, (uint64_t) n_members),
155+
polyglot_from_PyObjectPtr_array((PyObjectPtr *) field_docs, (uint64_t) n_members),
156+
desc->n_in_sequence
157+
);
139158
}
140159

141160
// taken from CPython "Objects/structseq.c"

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ static void add_method_or_slot(PyTypeObject* cls, PyObject* type_dict, char* nam
408408

409409
UPCALL_ID(PyTruffle_Get_Inherited_Native_Slots);
410410
UPCALL_ID(PyTruffle_Compute_Mro);
411+
UPCALL_ID(PyTruffle_NewTypeDict);
411412
int PyType_Ready(PyTypeObject* cls) {
412413
#define RETURN_ERROR(__type__) \
413414
do { \
@@ -492,7 +493,7 @@ int PyType_Ready(PyTypeObject* cls) {
492493
/* Initialize tp_dict */
493494
PyObject* dict = cls->tp_dict;
494495
if (dict == NULL) {
495-
dict = PyDict_New();
496+
dict = UPCALL_CEXT_O(_jls_PyTruffle_NewTypeDict, native_type_to_java(cls));
496497
if (dict == NULL) {
497498
RETURN_ERROR(cls);
498499
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -574,16 +574,15 @@ def CPyExtType(name, code, **kwargs):
574574
PyMODINIT_FUNC
575575
PyInit_{name}(void)
576576
{{
577-
PyObject* m;
577+
PyObject* m = PyModule_Create(&{name}module);
578+
if (m == NULL)
579+
return NULL;
578580
579581
{ready_code}
580582
if (PyType_Ready(&{name}Type) < 0)
581583
return NULL;
582584
{post_ready_code}
583585
584-
m = PyModule_Create(&{name}module);
585-
if (m == NULL)
586-
return NULL;
587586
588587
Py_INCREF(&{name}Type);
589588
PyModule_AddObject(m, "{name}", (PyObject *)&{name}Type);

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

Lines changed: 79 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,64 +39,104 @@
3939

4040
import sys
4141
from . import CPyExtType, CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, GRAALPYTHON
42+
4243
__dir__ = __file__.rpartition("/")[0]
4344

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)
45+
46+
class TestPyStructSequenceTypes(object):
47+
def test_properties(self):
48+
TestPyStructSequence = CPyExtType("TestPyStructSequence",
49+
"""
50+
static PyStructSequence_Field typeinfo_fields[] = {
51+
{"element0", "The first element."},
52+
{"element1", "The second element."},
53+
{NULL, NULL,}
54+
};
55+
56+
static PyStructSequence_Desc typeinfo_desc = {
57+
"TestPyStructSequenceTypes.TestPyStructSequence",
58+
"Information about some custom struct type",
59+
typeinfo_fields,
60+
2,
61+
};
62+
""",
63+
ready_code="""
64+
/* our template initializes refcnt to 1; so reset */
65+
Py_REFCNT(&TestPyStructSequenceType) = 0;
66+
if(PyStructSequence_InitType2(&TestPyStructSequenceType, &typeinfo_desc) < 0) {
67+
return NULL;
68+
}
69+
Py_INCREF(&TestPyStructSequenceType);
70+
""",
71+
)
72+
assert TestPyStructSequence.__doc__ == "Information about some custom struct type"
73+
74+
tester = TestPyStructSequence(("hello", "world"))
75+
assert hasattr(tester, "element0")
76+
assert hasattr(tester, "element1")
77+
assert tester.element0 == "hello"
78+
assert tester.element1 == "world"
79+
80+
def test_structseq_newtype(self):
81+
TestPyStructSequenceNewType = CPyExtType("TestPyStructSequenceNewType", """
82+
static PyStructSequence_Field typeinfo_fields[] = {
83+
{"element0", "The first element."},
84+
{"element1", "The second element."},
85+
{"element2", "The third element."},
86+
{NULL, NULL,}
87+
};
88+
89+
static PyStructSequence_Desc typeinfo_desc = {
90+
"TestPyStructSequenceNewType.TestPyStructSequenceNewType",
91+
"Information about some new struct type",
92+
typeinfo_fields,
93+
3,
94+
};
95+
""",
96+
post_ready_code="""
97+
PyTypeObject *new_type = PyStructSequence_NewType(&typeinfo_desc);
98+
if (new_type == NULL)
99+
return NULL;
100+
PyModule_AddObject(m, "TestPyStructSequenceNewType", (PyObject *)new_type);
101+
return m;
102+
""",
103+
)
104+
assert TestPyStructSequenceNewType.__doc__ == "Information about some new struct type"
105+
106+
tester = TestPyStructSequenceNewType(("hello", "beautiful", "world"))
107+
assert hasattr(tester, "element0")
108+
assert hasattr(tester, "element1")
109+
assert hasattr(tester, "element2")
110+
assert tester.element0 == "hello"
111+
assert tester.element1 == "beautiful"
112+
assert tester.element2 == "world"
73113

74114

75115
class TestPyStructSequence(CPyExtTestCase):
76-
116+
77117
def compile_module(self, name):
78118
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
79119
super(TestPyStructSequence, self).compile_module(name)
80-
120+
81121
test_PyStructSequence_InitType2 = CPyExtFunction(
82122
lambda args: 0,
83-
lambda: ( (0,), ),
123+
lambda: ((0,),),
84124
code='''
85125
static PyTypeObject CustomStructSeqType;
86-
126+
87127
static PyStructSequence_Field typeinfo_fields[] = {
88128
{"element0", "The first element."},
89129
{"element1", "The second element."},
90130
{NULL, NULL,}
91131
};
92-
132+
93133
static PyStructSequence_Desc typeinfo_desc = {
94134
"custom.named.tuple",
95135
"Information about some custom struct type",
96136
typeinfo_fields,
97137
2,
98138
};
99-
139+
100140
int wrap_PyStructSequence_InitType2(int n) {
101141
return PyStructSequence_InitType2(&CustomStructSeqType, &typeinfo_desc);
102142
}
@@ -110,12 +150,8 @@ def compile_module(self, name):
110150

111151
test_PyStructSequence_Usage = CPyExtFunction(
112152
lambda args: args[0],
113-
lambda: (
153+
lambda: (
114154
(("hello", "world",),),
115-
# ("john", "doe",),
116-
# (1, "doe",),
117-
# ("john", False,),
118-
# ("john", None,),
119155
),
120156
code='''
121157
static PyTypeObject CustomStructSeqType;
@@ -125,14 +161,14 @@ def compile_module(self, name):
125161
{"element1", "The second element."},
126162
{NULL, NULL,}
127163
};
128-
129-
static PyStructSequence_Desc typeinfo_desc = {
164+
165+
static PyStructSequence_Desc typeinfo_desc = {
130166
"custom.named.tuple",
131167
"Information about some custom struct type",
132168
typeinfo_fields,
133169
2,
134170
};
135-
171+
136172
PyObject* wrap_PyStructSequence_Usage(PyObject* elements) {
137173
Py_ssize_t i = 0;
138174
PyObject* elem = NULL;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.concurrent.Semaphore;
3333
import java.util.logging.Level;
3434

35+
import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass;
3536
import org.graalvm.options.OptionDescriptors;
3637
import org.graalvm.options.OptionKey;
3738
import org.graalvm.options.OptionValues;
@@ -811,7 +812,7 @@ public Shape getEmptyShape() {
811812
return emptyShape;
812813
}
813814

814-
public Shape getShapeForClass(PythonManagedClass klass) {
815+
public Shape getShapeForClass(PythonAbstractClass klass) {
815816
if (singleContextAssumption.isValid()) {
816817
return Shape.newBuilder(getEmptyShape()).addConstantProperty(HiddenAttributes.CLASS, klass, 0).build();
817818
} else {

0 commit comments

Comments
 (0)