[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