[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