[pypy-commit] cffi cffi-1.0: Careful logic to support "unnamed" structs at the same place as before.

arigo noreply at buildbot.pypy.org
Tue Apr 21 13:20:21 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r1774:8f7f19808230
Date: 2015-04-21 13:20 +0200
http://bitbucket.org/cffi/cffi/changeset/8f7f19808230/

Log:	Careful logic to support "unnamed" structs at the same place as
	before.

diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -19,6 +19,8 @@
     basestring = str
 
 
+FFIError = _cffi1_backend.FFI.error
+
 class CDefError(Exception):
     def __str__(self):
         try:
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
@@ -307,24 +307,38 @@
             strcat(name, s->name);
             x = new_struct_or_union_type(name, flags);
 
+            CTypeDescrObject *ct = NULL;
             if (s->first_field_index >= 0) {
-                CTypeDescrObject *ct = (CTypeDescrObject *)x;
-                ct->ct_size = s->size;
+                ct = (CTypeDescrObject *)x;
+                ct->ct_size = (Py_ssize_t)s->size;
                 ct->ct_length = s->alignment;
                 ct->ct_flags &= ~CT_IS_OPAQUE;
                 ct->ct_flags |= CT_LAZY_FIELD_LIST;
                 ct->ct_extra = builder;
             }
 
-            /* 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
-               current slot is not updated.  But in this case, the
-               next time we walk the same current slot, we'll find the
-               'x' object in the primary slot (op2, above) and then we
-               will update the current slot. */
-            opcodes = builder->ctx.types;
-            index = s->type_index;
+            /* Update the "primary" OP_STRUCT_OR_UNION slot, which
+               may be the same or a different slot than the "current" one */
+            assert((((uintptr_t)x) & 1) == 0);
+            assert(builder->ctx.types[s->type_index] == op2);
+            Py_INCREF(x);
+            builder->ctx.types[s->type_index] = x;
+
+            if (s->size == (size_t)-2) {
+                /* oops, this struct is unnamed and we couldn't generate
+                   a C expression to get its size.  We have to rely on
+                   complete_struct_or_union() to compute it now. */
+                assert(ct != NULL);
+                if (do_realize_lazy_struct(ct) < 0) {
+                    builder->ctx.types[s->type_index] = op2;
+                    return NULL;
+                }
+            }
+
+            /* Done, leave without updating the "current" slot because
+               it may be done already above.  If not, never mind, the
+               next call to realize_c_type() will do it. */
+            return x;
         }
         break;
     }
@@ -451,7 +465,12 @@
                 return -1;
             }
 
-            if (detect_custom_layout(ct, SF_STD_FIELD_POS,
+            if (fld->field_offset == (size_t)-1) {
+                /* unnamed struct, with field positions and sizes entirely
+                   determined by complete_struct_or_union() and not checked */
+                assert(fld->field_size == -1);
+            }
+            else if (detect_custom_layout(ct, SF_STD_FIELD_POS,
                                      ctf->ct_size, fld->field_size,
                                      "wrong size for field '",
                                      fld->name, "'") < 0)
diff --git a/new/recompiler.py b/new/recompiler.py
--- a/new/recompiler.py
+++ b/new/recompiler.py
@@ -70,6 +70,10 @@
             self._typesdict[tp] = None
             if isinstance(tp, model.FunctionPtrType):
                 self._do_collect_type(tp.as_raw_function())
+            elif isinstance(tp, model.StructOrUnion):
+                if tp.fldtypes is not None:
+                    for name1, tp1 in zip(tp.fldnames, tp.fldtypes):
+                        self._do_collect_type(self._field_type(tp, name1, tp1))
             else:
                 for _, x in tp._get_items():
                     self._do_collect_type(x)
@@ -118,6 +122,7 @@
         #
         # the declaration of '_cffi_types'
         prnt('static void *_cffi_types[] = {')
+        self.cffi_types = tuple(self.cffi_types)    # don't change any more
         typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
         for i, op in enumerate(self.cffi_types):
             comment = ''
@@ -139,7 +144,9 @@
         self._lsts = {}
         for step_name in ALL_STEPS:
             self._lsts[step_name] = []
+        self._seen_struct_unions = set()
         self._generate("ctx")
+        self._add_missing_struct_unions()
         for step_name in ALL_STEPS:
             lst = self._lsts[step_name]
             nums[step_name] = len(lst)
@@ -284,9 +291,9 @@
         self._lsts["typename"].append(
             '  { "%s", %d },' % (name, type_index))
         if getattr(tp, "origin", None) == "unknown_type":
-            self._generate_cpy_struct_ctx(tp, tp.name)
+            self._struct_ctx(tp, tp.name, approxname=None)
         elif isinstance(tp, model.NamedPointerType):
-            self._generate_cpy_struct_ctx(tp.totype, tp.totype.name)
+            self._struct_ctx(tp.totype, tp.totype.name, approxname=None)
 
     # ----------
     # function declarations
@@ -393,18 +400,14 @@
             tp_field = tp_field.resolve_length(actual_length)
         return tp_field
 
-    def _generate_cpy_struct_collecttype(self, tp, name):
+    def _struct_collecttype(self, tp):
         self._do_collect_type(tp)
-        if tp.fldtypes is not None:
-            for name1, tp1 in zip(tp.fldnames, tp.fldtypes):
-                self._do_collect_type(self._field_type(tp, name1, tp1))
 
-    def _generate_cpy_struct_decl(self, tp, name):
+    def _struct_decl(self, tp, cname, approxname):
         if tp.fldtypes is None:
             return
-        cname = ('%s %s' % (tp.kind, name)).strip()
         prnt = self._prnt
-        checkfuncname = '_cffi_checkfld_%s' % (name,)
+        checkfuncname = '_cffi_checkfld_%s' % (approxname,)
         prnt('__attribute__((unused))')
         prnt('static void %s(%s *p)' % (checkfuncname, cname))
         prnt('{')
@@ -423,10 +426,10 @@
                 except ffiplatform.VerificationError as e:
                     prnt('  /* %s */' % str(e))   # cannot verify it, ignore
         prnt('}')
-        prnt('struct _cffi_align_%s { char x; %s y; };' % (name, cname))
+        prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))
         prnt()
 
-    def _generate_cpy_struct_ctx(self, tp, name):
+    def _struct_ctx(self, tp, cname, approxname):
         type_index = self._typesdict[tp]
         flags = []
         if tp.partial:
@@ -435,45 +438,104 @@
             flags.append('CT_UNION')
         flags = ('|'.join(flags)) or '0'
         if tp.fldtypes is not None:
-            c_field = [name]
+            c_field = [approxname]
             for fldname, fldtype in zip(tp.fldnames, tp.fldtypes):
                 fldtype = self._field_type(tp, fldname, fldtype)
                 spaces = " " * len(fldname)
-                if (isinstance(fldtype, model.ArrayType) and
-                       fldtype.length is None):
+                # cname is None for _add_missing_struct_unions() only
+                if cname is None or (
+                        isinstance(fldtype, model.ArrayType) and
+                        fldtype.length is None):
                     size = '(size_t)-1'
                 else:
                     size = 'sizeof(((%s)0)->%s)' % (tp.get_c_name('*'), fldname)
+                if cname is None:
+                    offset = '(size_t)-1'
+                else:
+                    offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname)
                 c_field.append(
-                    '  { "%s", offsetof(%s, %s),\n' % (
-                            fldname, tp.get_c_name(''), fldname) +
+                    '  { "%s", %s,\n' % (fldname, offset) +
                     '     %s   %s,\n' % (spaces, size) +
                     '     %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,) +
-                '    _cffi_FIELDS_FOR_%s, %d },' % (name, len(tp.fldtypes),))
+            #
+            if cname is None:  # unknown name, for _add_missing_struct_unions
+                size_align = (' (size_t)-2, -2, /* unnamed */\n' +
+                    '    _cffi_FIELDS_FOR_%s, %d },' % (approxname,
+                                                        len(tp.fldtypes),))
+            else:
+                size_align = ('\n' +
+                    '    sizeof(%s),\n' % (cname,) +
+                    '    offsetof(struct _cffi_align_%s, y),\n'% (approxname,) +
+                    '    _cffi_FIELDS_FOR_%s, %d },' % (approxname,
+                                                        len(tp.fldtypes),))
         else:
             size_align = ' (size_t)-1, -1, -1, 0 /* opaque */ },'
         self._lsts["struct_union"].append(
-            '  { "%s", %d, %s,' % (name, type_index, flags) + size_align)
+            '  { "%s", %d, %s,' % (tp.name, type_index, flags) + size_align)
+        self._seen_struct_unions.add(tp)
+
+    def _add_missing_struct_unions(self):
+        # not very nice, but some struct declarations might be missing
+        # because they don't have any known C name.  Check that they are
+        # not partial (we can't complete or verify them!) and emit them
+        # anonymously.
+        for tp in list(self._struct_unions):
+            if tp not in self._seen_struct_unions:
+                if tp.partial:
+                    raise NotImplementedError("internal inconsistency: %r is "
+                                              "partial but was not seen at "
+                                              "this point" % (tp,))
+                assert tp.name.startswith('$') and tp.name[1:].isdigit()
+                self._struct_ctx(tp, None, tp.name[1:])
 
     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):]
+            pname = struct_fields.split('\n')[0]
+            define_macro = '#define _cffi_FIELDS_FOR_%s  %d' % (pname, count)
+            lst[i] = define_macro + struct_fields[len(pname):]
             count += lst[i].count('\n  { "')
 
+    def _generate_cpy_struct_collecttype(self, tp, name):
+        self._struct_collecttype(tp)
     _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype
+
+    def _generate_cpy_struct_decl(self, tp, name):
+        cname = tp.get_c_name('')
+        self._struct_decl(tp, cname, cname.replace(' ', '_'))
     _generate_cpy_union_decl = _generate_cpy_struct_decl
+
+    def _generate_cpy_struct_ctx(self, tp, name, prefix='s'):
+        cname = tp.get_c_name('')
+        self._struct_ctx(tp, cname, cname.replace(' ', '_'))
     _generate_cpy_union_ctx = _generate_cpy_struct_ctx
 
     # ----------
+    # 'anonymous' declarations.  These are produced for anonymous structs
+    # or unions; the 'name' is obtained by a typedef.
+
+    def _generate_cpy_anonymous_collecttype(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_collecttype(tp, name)
+        else:
+            self._struct_collecttype(tp)
+
+    def _generate_cpy_anonymous_decl(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_decl(tp, name, '')
+        else:
+            self._struct_decl(tp, name, 'typedef_' + name)
+
+    def _generate_cpy_anonymous_ctx(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_ctx(tp, name, '')
+        else:
+            self._struct_ctx(tp, name, 'typedef_' + name)
+
+    # ----------
     # constants, declared with "static const ..."
 
     def _generate_cpy_const(self, is_int, name, tp=None, category='const',
diff --git a/new/test_recompiler.py b/new/test_recompiler.py
--- a/new/test_recompiler.py
+++ b/new/test_recompiler.py
@@ -83,6 +83,10 @@
                      "(PRIMITIVE 7)(PRIMITIVE 9)"
                      "(STRUCT_UNION 0)")
 
+def test_anonymous_struct_with_typedef():
+    check_type_table("typedef struct { int a; long b; } foo_t;",
+                     "(STRUCT_UNION 0)(PRIMITIVE 7)(PRIMITIVE 9)")
+
 
 def test_math_sin():
     import math
@@ -291,3 +295,19 @@
     # 'x' is another <built-in method> object on lib, made very indirectly
     x = type(lib).__dir__.__get__(lib)
     py.test.raises(TypeError, ffi.typeof, x)
+
+def test_verify_anonymous_struct_with_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int a; long b; ...; } foo_t;")
+    verify(ffi, 'test_verify_anonymous_struct_with_typedef',
+           "typedef struct { long b; int hidden, a; } foo_t;")
+    p = ffi.new("foo_t *", {'b': 42})
+    assert p.b == 42
+
+def test_verify_anonymous_struct_with_star_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int a; long b; } *foo_t;")
+    verify(ffi, 'test_verify_anonymous_struct_with_star_typedef',
+           "typedef struct { int a; long b; } *foo_t;")
+    p = ffi.new("foo_t", {'b': 42})
+    assert p.b == 42


More information about the pypy-commit mailing list