Author: Armin Rigo <ar...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit