Skip to content

Commit afa6ab1

Browse files
committed
[GR-12969] Implement PyStructSequence_* C API functions.
PullRequest: graalpython/574
2 parents 7bd2fea + 81dfcfd commit afa6ab1

File tree

16 files changed

+577
-24
lines changed

16 files changed

+577
-24
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,14 @@ void initialize_type_structure(PyTypeObject* structure, PyTypeObject* ptype, pol
8585

8686
unsigned long original_flags = structure->tp_flags;
8787
Py_ssize_t basicsize = structure->tp_basicsize;
88+
allocfunc alloc = structure->tp_alloc;
8889
PyTypeObject* type_handle = truffle_assign_managed(structure, ptype);
8990
// write flags as specified in the dummy to the PythonClass object
9091
type_handle->tp_flags = original_flags | Py_TPFLAGS_READY;
9192
type_handle->tp_basicsize = basicsize;
93+
if (alloc) {
94+
type_handle->tp_alloc = alloc;
95+
}
9296
}
9397

9498
static void initialize_builtin_type(PyTypeObject* structure, const char* typname, polyglot_typeid tid) {
@@ -303,6 +307,11 @@ Py_ssize_t get_tp_basicsize(PyTypeObject* obj) {
303307
return obj->tp_basicsize;
304308
}
305309

310+
/** to be used from Java code only; reads native 'tp_alloc' field */
311+
allocfunc get_tp_alloc(PyTypeObject* obj) {
312+
return obj->tp_alloc;
313+
}
314+
306315
/** to be used from Java code only; returns the type ID for a byte array */
307316
polyglot_typeid get_byte_array_typeid(uint64_t len) {
308317
return polyglot_array_typeid(polyglot_i8_typeid(), len);
@@ -397,6 +406,10 @@ PRIMITIVE_ARRAY_TO_NATIVE(Long, int64_t, i64, polyglot_as_i64);
397406
PRIMITIVE_ARRAY_TO_NATIVE(Double, double, double, polyglot_as_double);
398407
PRIMITIVE_ARRAY_TO_NATIVE(Object, PyObjectPtr, PyObjectPtr, (PyObjectPtr));
399408

409+
Py_ssize_t PyTruffle_Object_Size(PyObject *op) {
410+
return ((PyVarObject*)op)->ob_size;
411+
}
412+
400413
#define ReadMember(object, offset, T) ((T*)(((char*)object) + offset))[0]
401414

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

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ void* wrap_unsupported(void *fun, ...);
263263
wrap_direct : \
264264
wrap_unsupported))))))))
265265

266-
#define PY_TRUFFLE_TYPE(__TYPE_NAME__, __SUPER_TYPE__, __FLAGS__, __SIZE__) {\
266+
#define PY_TRUFFLE_TYPE_WITH_ALLOC(__TYPE_NAME__, __SUPER_TYPE__, __FLAGS__, __SIZE__, __ALLOC__) {\
267267
PyVarObject_HEAD_INIT((__SUPER_TYPE__), 0)\
268268
__TYPE_NAME__, /* tp_name */\
269269
(__SIZE__), /* tp_basicsize */\
@@ -300,12 +300,14 @@ void* wrap_unsupported(void *fun, ...);
300300
0, /* tp_descr_set */\
301301
0, /* tp_dictoffset */\
302302
0, /* tp_init */\
303-
0, /* tp_alloc */\
303+
(__ALLOC__), /* tp_alloc */\
304304
0, /* tp_new */\
305305
0, /* tp_free */\
306306
0, /* tp_is_gc */\
307307
}
308308

309+
#define PY_TRUFFLE_TYPE(__TYPE_NAME__, __SUPER_TYPE__, __FLAGS__, __SIZE__) PY_TRUFFLE_TYPE_WITH_ALLOC(__TYPE_NAME__, __SUPER_TYPE__, __FLAGS__, __SIZE__, 0)
310+
309311
/** to be used from Java code only; returns a type's basic size */
310312
#define BASICSIZE_GETTER(__typename__)extern Py_ssize_t get_ ## __typename__ ## _basicsize() { \
311313
return sizeof(__typename__); \

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

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -40,8 +40,92 @@
4040
*/
4141
#include "capi.h"
4242

43-
/* StructSequences */
43+
/* StructSequences a.k.a. 'namedtuple' */
4444
UPCALL_ID(PyStructSequence_New);
4545
PyObject* PyStructSequence_New(PyTypeObject* o) {
46-
return UPCALL_CEXT_O(_jls_PyStructSequence_New, native_to_java((PyObject*)o));
46+
return UPCALL_CEXT_O(_jls_PyStructSequence_New, native_type_to_java(o));
4747
}
48+
49+
UPCALL_ID(PyStructSequence_InitType2);
50+
int PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc) {
51+
Py_ssize_t n_members = desc->n_in_sequence;
52+
Py_ssize_t i;
53+
54+
memset(type, 0, sizeof(PyTypeObject));
55+
56+
// put field names and doc strings into two tuples
57+
PyObject* field_names = PyTuple_New(n_members);
58+
PyObject* field_docs = PyTuple_New(n_members);
59+
PyStructSequence_Field* fields = desc->fields;
60+
for (i = 0; i < n_members; i++) {
61+
PyTuple_SetItem(field_names, i, polyglot_from_string(fields[i].name, SRC_CS));
62+
PyTuple_SetItem(field_docs, i, polyglot_from_string(fields[i].doc, SRC_CS));
63+
}
64+
65+
// we create the new type managed
66+
PyTypeObject* newType = (PyTypeObject*) UPCALL_CEXT_O(_jls_PyStructSequence_InitType2,
67+
polyglot_from_string(desc->name, SRC_CS),
68+
polyglot_from_string(desc->doc, SRC_CS),
69+
native_to_java(field_names),
70+
native_to_java(field_docs));
71+
72+
// copy generic fields (CPython mem-copies a template)
73+
type->tp_basicsize = sizeof(PyStructSequence) - sizeof(PyObject *);
74+
type->tp_itemsize = sizeof(PyObject *);
75+
type->tp_repr = newType->tp_repr;
76+
type->tp_flags = Py_TPFLAGS_DEFAULT;
77+
type->tp_members = NULL;
78+
type->tp_new = newType->tp_new;
79+
type->tp_base = &PyTuple_Type;
80+
type->tp_alloc = newType->tp_alloc;
81+
82+
// now copy specific fields
83+
type->tp_name = newType->tp_name;
84+
type->tp_doc = newType->tp_doc;
85+
type->tp_dict = newType->tp_dict;
86+
87+
// now initialize the type
88+
if (PyType_Ready(type) < 0)
89+
return -1;
90+
Py_INCREF(type);
91+
92+
return 0;
93+
}
94+
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+
117+
// taken from CPython "Objects/structseq.c"
118+
void PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc) {
119+
(void)PyStructSequence_InitType2(type, desc);
120+
}
121+
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: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@
4040
*/
4141
#include "capi.h"
4242

43-
PyTypeObject PyTuple_Type = PY_TRUFFLE_TYPE("tuple", &PyType_Type, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TUPLE_SUBCLASS, sizeof(PyTupleObject) - sizeof(PyObject *));
43+
/* prototype */
44+
PyObject* PyTruffle_Tuple_Alloc(PyTypeObject* cls, Py_ssize_t nitems);
45+
46+
/* tuple type */
47+
PyTypeObject PyTuple_Type = PY_TRUFFLE_TYPE_WITH_ALLOC("tuple", &PyType_Type, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TUPLE_SUBCLASS, sizeof(PyTupleObject) - sizeof(PyObject *), PyTruffle_Tuple_Alloc);
4448

4549
/* Tuples */
4650
UPCALL_ID(PyTuple_New);
@@ -79,3 +83,70 @@ PyObject* PyTuple_Pack(Py_ssize_t n, ...) {
7983
}
8084
return result;
8185
}
86+
87+
MUST_INLINE
88+
static PyObject * tuple_create(PyObject *iterable) {
89+
if (iterable == NULL) {
90+
return PyTuple_New(0);
91+
}
92+
return PySequence_Tuple(iterable);
93+
}
94+
95+
POLYGLOT_DECLARE_TYPE(PyTupleObject);
96+
PyObject * tuple_subtype_new(PyTypeObject *type, PyObject *iterable) {
97+
PyTupleObject* newobj;
98+
PyObject *tmp, *item;
99+
Py_ssize_t i, n;
100+
101+
assert(PyType_IsSubtype(type, &PyTuple_Type));
102+
tmp = tuple_create(iterable);
103+
if (tmp == NULL) {
104+
return NULL;
105+
}
106+
assert(PyTuple_Check(tmp));
107+
n = PyTuple_GET_SIZE(tmp);
108+
109+
newobj = (PyTupleObject*) type->tp_alloc(type, n);
110+
if (newobj == NULL) {
111+
return NULL;
112+
}
113+
newobj->ob_item = (PyObject **) ((char *)newobj + offsetof(PyTupleObject, ob_item) + sizeof(PyObject **));
114+
newobj = polyglot_from_PyTupleObject(newobj);
115+
for (i = 0; i < n; i++) {
116+
item = PyTuple_GetItem(tmp, i);
117+
Py_INCREF(item);
118+
PyTuple_SetItem((PyObject*)newobj, i, item);
119+
}
120+
Py_DECREF(tmp);
121+
122+
// This polyglot type cast is important such that we can directly read and
123+
// write members of the pointer from Java code.
124+
// Note: the return type is 'PyObject*' to be compatible with CPython
125+
return (PyObject*) newobj;
126+
}
127+
128+
int PyTruffle_Tuple_SetItem(PyObject* tuple, Py_ssize_t position, PyObject* item) {
129+
PyTuple_SET_ITEM(tuple, position, item);
130+
return 0;
131+
}
132+
133+
PyObject* PyTruffle_Tuple_Alloc(PyTypeObject* cls, Py_ssize_t nitems) {
134+
/*
135+
* TODO(fa): For 'PyVarObjects' (i.e. 'nitems > 0') we increase the size by 'sizeof(void *)'
136+
* because this additional pointer can then be used as pointer to the element array.
137+
* CPython usually embeds the array in the struct but Sulong doesn't currently support that.
138+
* So we allocate space for the additional array pointer.
139+
* Also consider any 'PyVarObject' (in particular 'PyTupleObject') if this is fixed.
140+
*/
141+
Py_ssize_t size = cls->tp_basicsize + cls->tp_itemsize * nitems + sizeof(PyObject **);
142+
PyObject* newObj = (PyObject*)PyObject_Malloc(size);
143+
if(cls->tp_dictoffset) {
144+
*((PyObject **) ((char *)newObj + cls->tp_dictoffset)) = NULL;
145+
}
146+
Py_TYPE(newObj) = cls;
147+
if (nitems > 0) {
148+
((PyVarObject*)newObj)->ob_size = nitems;
149+
}
150+
return newObj;
151+
}
152+

0 commit comments

Comments
 (0)