[pypy-commit] cffi cffi-1.0: Force the field list of structs lazily.
arigo
noreply at buildbot.pypy.org
Thu Apr 16 17:20:13 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r1737:7709aec1791f
Date: 2015-04-16 17:20 +0200
http://bitbucket.org/cffi/cffi/changeset/7709aec1791f/
Log: Force the field list of structs lazily.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -131,6 +131,7 @@
#define CT_IS_VOID_PTR 524288
#define CT_WITH_VAR_ARRAY 1048576
#define CT_IS_UNSIZED_CHAR_A 2097152
+#define CT_LAZY_FIELD_LIST 4194304
#define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \
CT_PRIMITIVE_UNSIGNED | \
CT_PRIMITIVE_CHAR | \
@@ -420,12 +421,21 @@
static PyObject *
get_field_name(CTypeDescrObject *ct, CFieldObject *cf); /* forward */
+#define force_lazy_struct(ct) \
+ ((ct)->ct_stuff != NULL ? 1 : do_realize_lazy_struct(ct))
+
+static int do_realize_lazy_struct(CTypeDescrObject *ct);
+/* forward, implemented in realize_c_type.c */
+
static PyObject *ctypeget_fields(CTypeDescrObject *ct, void *context)
{
if (ct->ct_flags & (CT_STRUCT | CT_UNION)) {
if (!(ct->ct_flags & CT_IS_OPAQUE)) {
CFieldObject *cf;
- PyObject *res = PyList_New(0);
+ PyObject *res;
+ if (force_lazy_struct(ct) < 0)
+ return NULL;
+ res = PyList_New(0);
if (res == NULL)
return NULL;
for (cf = (CFieldObject *)ct->ct_extra;
@@ -1217,6 +1227,9 @@
{
const char *expected;
+ if (force_lazy_struct(ct) < 0)
+ return -1;
+
if (ct->ct_flags & CT_UNION) {
Py_ssize_t n = PyObject_Size(init);
if (n < 0)
@@ -2220,18 +2233,25 @@
if (ct->ct_flags & CT_POINTER)
ct = ct->ct_itemdescr;
- if ((ct->ct_flags & (CT_STRUCT|CT_UNION)) && ct->ct_stuff != NULL) {
- cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
- if (cf != NULL) {
- /* read the field 'cf' */
- char *data = cd->c_data + cf->cf_offset;
- if (cf->cf_bitshift == BS_REGULAR)
- return convert_to_object(data, cf->cf_type);
- else if (cf->cf_bitshift == BS_EMPTY_ARRAY)
- return new_simple_cdata(data,
- (CTypeDescrObject *)cf->cf_type->ct_stuff);
- else
- return convert_to_object_bitfield(data, cf);
+ if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
+ switch (force_lazy_struct(ct)) {
+ case 1:
+ cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
+ if (cf != NULL) {
+ /* read the field 'cf' */
+ char *data = cd->c_data + cf->cf_offset;
+ if (cf->cf_bitshift == BS_REGULAR)
+ return convert_to_object(data, cf->cf_type);
+ else if (cf->cf_bitshift == BS_EMPTY_ARRAY)
+ return new_simple_cdata(data,
+ (CTypeDescrObject *)cf->cf_type->ct_stuff);
+ else
+ return convert_to_object_bitfield(data, cf);
+ }
+ case -1:
+ return NULL;
+ default:
+ ;
}
}
return PyObject_GenericGetAttr((PyObject *)cd, attr);
@@ -2246,18 +2266,25 @@
if (ct->ct_flags & CT_POINTER)
ct = ct->ct_itemdescr;
- if ((ct->ct_flags & (CT_STRUCT|CT_UNION)) && ct->ct_stuff != NULL) {
- cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
- if (cf != NULL) {
- /* write the field 'cf' */
- if (value != NULL) {
- return convert_field_from_object(cd->c_data, cf, value);
+ if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
+ switch (force_lazy_struct(ct)) {
+ case 1:
+ cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
+ if (cf != NULL) {
+ /* write the field 'cf' */
+ if (value != NULL) {
+ return convert_field_from_object(cd->c_data, cf, value);
+ }
+ else {
+ PyErr_SetString(PyExc_AttributeError,
+ "cannot delete struct field");
+ return -1;
+ }
}
- else {
- PyErr_SetString(PyExc_AttributeError,
- "cannot delete struct field");
- return -1;
- }
+ case -1:
+ return -1;
+ default:
+ ;
}
}
return PyObject_GenericSetAttr((PyObject *)cd, attr, value);
@@ -3639,6 +3666,7 @@
td->ct_size = -1;
td->ct_length = -1;
td->ct_flags = flag | CT_IS_OPAQUE;
+ td->ct_extra = NULL;
memcpy(td->ct_name, name, namelen + 1);
td->ct_name_position = namelen;
return (PyObject *)td;
@@ -4102,6 +4130,8 @@
here, so better safe (and forbid it) than sorry (and maybe
crash).
*/
+ if (force_lazy_struct(ct) < 0)
+ return NULL;
if (ct->ct_flags & CT_CUSTOM_FIELD_POS) {
PyErr_SetString(PyExc_TypeError,
"cannot pass as an argument a struct that was completed "
@@ -4876,10 +4906,12 @@
if (PyTextAny_Check(fieldname)) {
if (!following && (ct->ct_flags & CT_POINTER))
ct = ct->ct_itemdescr;
- if (!(ct->ct_flags & (CT_STRUCT|CT_UNION)) || ct->ct_stuff == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "with a field name argument, expected an "
- "initialized struct or union ctype");
+ if (!(ct->ct_flags & (CT_STRUCT|CT_UNION)) ||
+ force_lazy_struct(ct) <= 0) {
+ if (!PyErr_Occurred())
+ PyErr_SetString(PyExc_TypeError,
+ "with a field name argument, expected an "
+ "initialized struct or union ctype");
return NULL;
}
cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname);
diff --git a/new/ffi_obj.c b/new/ffi_obj.c
--- a/new/ffi_obj.c
+++ b/new/ffi_obj.c
@@ -301,7 +301,7 @@
static CFieldObject *_ffi_field(CTypeDescrObject *ct, const char *fieldname)
{
CFieldObject *cf;
- if (ct->ct_stuff == NULL) {
+ if (force_lazy_struct(ct) == NULL) {
PyErr_Format(PyExc_TypeError, "'%s' is incomplete", ct->ct_name);
return NULL;
}
diff --git a/new/manual.c b/new/manual.c
--- a/new/manual.c
+++ b/new/manual.c
@@ -124,8 +124,8 @@
static const struct _cffi_type_context_s _cffi_type_context = {
_cffi_types,
_cffi_globals,
+ _cffi_fields,
_cffi_struct_unions,
- _cffi_fields,
NULL,
NULL,
5, /* num_globals */
diff --git a/new/parse_c_type.h b/new/parse_c_type.h
--- a/new/parse_c_type.h
+++ b/new/parse_c_type.h
@@ -99,8 +99,8 @@
struct _cffi_type_context_s {
_cffi_opcode_t *types;
const struct _cffi_global_s *globals;
+ const struct _cffi_field_s *fields;
const struct _cffi_struct_union_s *struct_unions;
- const struct _cffi_field_s *fields;
const struct _cffi_enum_s *enums;
const struct _cffi_typename_s *typenames;
int num_globals;
@@ -120,3 +120,5 @@
int parse_c_type(struct _cffi_parse_info_s *info, const char *input);
int search_in_globals(const struct _cffi_type_context_s *ctx,
const char *search, size_t search_len);
+int search_in_struct_unions(const struct _cffi_type_context_s *ctx,
+ const char *search, size_t search_len);
diff --git a/new/realize_c_type.c b/new/realize_c_type.c
--- a/new/realize_c_type.c
+++ b/new/realize_c_type.c
@@ -167,6 +167,16 @@
}
strcat(name, s->name);
x = new_struct_or_union_type(name, flags);
+
+ if (s->first_field_index >= 0) {
+ CTypeDescrObject *ct = (CTypeDescrObject *)x;
+ ct->ct_size = s->size;
+ ct->ct_length = s->alignment;
+ ct->ct_flags &= ~CT_IS_OPAQUE;
+ ct->ct_flags |= CT_LAZY_FIELD_LIST;
+ ct->ct_extra = (void *)ctx;
+ }
+
/* We are going to update the "primary" OP_STRUCT_OR_UNION
slot below, which may be the same or a different one as
the "current" slot. If it is a different one, the
@@ -250,3 +260,89 @@
}
return x;
};
+
+static int do_realize_lazy_struct(CTypeDescrObject *ct)
+{
+ assert(ct->ct_flags & (CT_STRUCT | CT_UNION));
+
+ if (ct->ct_flags & CT_LAZY_FIELD_LIST) {
+ assert(!(ct->ct_flags & CT_IS_OPAQUE));
+
+ const struct _cffi_type_context_s *ctx = ct->ct_extra;
+ assert(ctx != NULL);
+
+ char *p = ct->ct_name;
+ if (memcmp(p, "struct ", 7) == 0)
+ p += 7;
+ else if (memcmp(p, "union ", 6) == 0)
+ p += 6;
+
+ int n = search_in_struct_unions(ctx, p, strlen(p));
+ if (n < 0)
+ Py_FatalError("lost a struct/union!");
+
+ const struct _cffi_struct_union_s *s = &ctx->struct_unions[n];
+ const struct _cffi_field_s *fld = &ctx->fields[s->first_field_index];
+
+ /* XXX painfully build all the Python objects that are the args
+ to b_complete_struct_or_union() */
+
+ PyObject *fields = PyList_New(s->num_fields);
+ if (fields == NULL)
+ return -1;
+
+ int i;
+ for (i = 0; i < s->num_fields; i++, fld++) {
+ _cffi_opcode_t op = fld->field_type_op;
+ PyObject *f;
+ CTypeDescrObject *ctf;
+
+ switch (_CFFI_GETOP(op)) {
+
+ case _CFFI_OP_NOOP:
+ ctf = realize_c_type(ctx, ctx->types, _CFFI_GETARG(op));
+ break;
+
+ default:
+ PyErr_Format(PyExc_NotImplementedError, "field op=%d",
+ (int)_CFFI_GETOP(op));
+ return -1;
+ }
+
+ f = Py_BuildValue("(sOin)", fld->name, ctf,
+ (int)-1, (Py_ssize_t)fld->field_offset);
+ if (f == NULL) {
+ Py_DECREF(fields);
+ return -1;
+ }
+ PyList_SET_ITEM(fields, i, f);
+ }
+
+ PyObject *args = Py_BuildValue("(OOOnn)", ct, fields,
+ Py_None,
+ (Py_ssize_t)s->size,
+ (Py_ssize_t)s->alignment);
+ Py_DECREF(fields);
+ if (args == NULL)
+ return -1;
+
+ ct->ct_extra = NULL;
+ ct->ct_flags |= CT_IS_OPAQUE;
+ PyObject *res = b_complete_struct_or_union(NULL, args);
+ ct->ct_flags &= ~CT_IS_OPAQUE;
+ Py_DECREF(args);
+ if (res == NULL) {
+ ct->ct_extra = (void *)ctx;
+ return -1;
+ }
+
+ assert(ct->ct_stuff != NULL);
+ ct->ct_flags &= ~CT_LAZY_FIELD_LIST;
+ Py_DECREF(res);
+ return 1;
+ }
+ else {
+ assert(ct->ct_flags & CT_IS_OPAQUE);
+ return 0;
+ }
+}
diff --git a/new/recompiler.py b/new/recompiler.py
--- a/new/recompiler.py
+++ b/new/recompiler.py
@@ -126,7 +126,7 @@
self._generate("decl")
#
# the declaration of '_cffi_globals' and '_cffi_typenames'
- ALL_STEPS = ["global", "struct_union", "field", "enum", "typename"]
+ ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
nums = {}
self._lsts = {}
for step_name in ALL_STEPS:
@@ -139,6 +139,8 @@
lst.sort() # sort by name, which is at the start of each line
prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (
step_name, step_name))
+ if step_name == 'field':
+ self._fix_final_field_list(lst)
for line in lst:
prnt(line)
prnt('};')
@@ -382,15 +384,35 @@
if isinstance(tp, model.UnionType):
flags = 'CT_UNION'
if tp.fldtypes is not None:
+ c_field = [name]
+ for fldname, fldtype in zip(tp.fldnames, tp.fldtypes):
+ spaces = " " * len(fldname)
+ c_field.append(
+ ' { "%s", offsetof(%s, %s),\n' % (
+ fldname, tp.get_c_name(''), fldname) +
+ ' %s sizeof(((%s)0)->%s),\n' % (
+ spaces, tp.get_c_name('*'), fldname) +
+ ' %s _CFFI_OP(_CFFI_OP_NOOP, %s) },' % (
+ spaces, self._typesdict[fldtype]))
+ self._lsts["field"].append('\n'.join(c_field))
size_align = ('\n' +
' sizeof(%s %s),\n' % (tp.kind, name) +
' offsetof(struct _cffi_align_%s, y),\n' % (name,) +
- ' 0, 0 },')
+ ' _cffi_FIELDS_FOR_%s, %d },' % (name, len(tp.fldtypes),))
else:
size_align = ' -1, -1, -1, 0 /* opaque */ },'
self._lsts["struct_union"].append(
' { "%s", %d, %s,' % (name, type_index, flags) + size_align)
+ def _fix_final_field_list(self, lst):
+ count = 0
+ for i in range(len(lst)):
+ struct_fields = lst[i]
+ name = struct_fields.split('\n')[0]
+ define_macro = '#define _cffi_FIELDS_FOR_%s %d' % (name, count)
+ lst[i] = define_macro + struct_fields[len(name):]
+ count += lst[i].count('\n { "')
+
_generate_cpy_union_collecttype = _generate_cpy_struct_collecttype
_generate_cpy_union_decl = _generate_cpy_struct_decl
_generate_cpy_union_ctx = _generate_cpy_struct_ctx
diff --git a/new/test_recompiler.py b/new/test_recompiler.py
--- a/new/test_recompiler.py
+++ b/new/test_recompiler.py
@@ -170,13 +170,17 @@
assert ffi.typeof("union foo_s").cname == "union foo_s"
def test_verify_struct():
- py.test.skip("XXX in-progress:")
ffi = FFI()
- ffi.cdef("struct foo_s { int b; short a; };")
+ ffi.cdef("""struct foo_s { int b; short a; };
+ struct bar_s { struct foo_s *f; };""")
lib = verify(ffi, 'test_verify_struct',
- "struct foo_s { short a; int b; };")
+ """struct foo_s { short a; int b; };
+ struct bar_s { struct foo_s *f; };""")
p = ffi.new("struct foo_s *", {'a': -32768, 'b': -2147483648})
assert p.a == -32768
assert p.b == -2147483648
py.test.raises(OverflowError, "p.a -= 1")
py.test.raises(OverflowError, "p.b -= 1")
+ py.test.skip("XXX in-progress:")
+ q = ffi.new("struct bar_s *", {'f': p})
+ assert q.f == p
More information about the pypy-commit
mailing list