[pypy-commit] cffi cffi-1.0: Needs another level of type caching, to ensure that there "is" only one

arigo noreply at buildbot.pypy.org
Fri Apr 17 16:46:12 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r1739:2c825ace4981
Date: 2015-04-17 16:46 +0200
http://bitbucket.org/cffi/cffi/changeset/2c825ace4981/

Log:	Needs another level of type caching, to ensure that there "is" only
	one of each type around. The _cffi_backend code depends on that.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -132,6 +132,7 @@
 #define CT_WITH_VAR_ARRAY     1048576
 #define CT_IS_UNSIZED_CHAR_A  2097152
 #define CT_LAZY_FIELD_LIST    4194304
+#define CT_USES_LOCAL         8388608
 #define CT_PRIMITIVE_ANY  (CT_PRIMITIVE_SIGNED |        \
                            CT_PRIMITIVE_UNSIGNED |      \
                            CT_PRIMITIVE_CHAR |          \
@@ -3544,7 +3545,7 @@
 
     td->ct_size = sizeof(void *);
     td->ct_length = -1;
-    td->ct_flags = CT_POINTER;
+    td->ct_flags = CT_POINTER | (ctitem->ct_flags & CT_USES_LOCAL);
     if (ctitem->ct_flags & (CT_STRUCT|CT_UNION))
         td->ct_flags |= CT_IS_PTR_TO_OWNED;
     if (ctitem->ct_flags & CT_VOID)
@@ -3633,7 +3634,7 @@
     td->ct_stuff = (PyObject *)ctptr;
     td->ct_size = arraysize;
     td->ct_length = length;
-    td->ct_flags = flags;
+    td->ct_flags = flags | (ctptr->ct_flags & CT_USES_LOCAL);
     return (PyObject *)td;
 }
 
@@ -3665,7 +3666,7 @@
 
     td->ct_size = -1;
     td->ct_length = -1;
-    td->ct_flags = flag | CT_IS_OPAQUE;
+    td->ct_flags = flag | CT_IS_OPAQUE | CT_USES_LOCAL;
     td->ct_extra = NULL;
     memcpy(td->ct_name, name, namelen + 1);
     td->ct_name_position = namelen;
@@ -4348,6 +4349,8 @@
                                           int ellipsis)
 {
     CTypeDescrObject *fct;
+    Py_ssize_t i, nargs;
+    int all_flags;
 
     fb->nb_bytes = 0;
     fb->bufferp = NULL;
@@ -4369,9 +4372,16 @@
         goto error;
     assert(fb->bufferp == fct->ct_name + fb->nb_bytes);
 
+    all_flags = fresult->ct_flags;
+    nargs = PyTuple_GET_SIZE(fargs);
+    for (i = 0; i < nargs; i++) {
+        CTypeDescrObject *farg = (CTypeDescrObject *)PyTuple_GET_ITEM(fargs, i);
+        all_flags |= farg->ct_flags;
+    }
+
     fct->ct_extra = NULL;
     fct->ct_size = sizeof(void(*)(void));
-    fct->ct_flags = CT_FUNCTIONPTR;
+    fct->ct_flags = CT_FUNCTIONPTR | (all_flags & CT_USES_LOCAL);
     return fct;
 
  error:
@@ -4484,8 +4494,6 @@
         Py_INCREF(o);
         PyTuple_SET_ITEM(fct->ct_stuff, 2 + i, o);
     }
-    fct->ct_size = sizeof(void(*)(void));
-    fct->ct_flags = CT_FUNCTIONPTR;
     return (PyObject *)fct;
 
  error:
@@ -4825,7 +4833,7 @@
     td->ct_size = basetd->ct_size;
     td->ct_length = basetd->ct_length;   /* alignment */
     td->ct_extra = basetd->ct_extra;     /* ffi type  */
-    td->ct_flags = basetd->ct_flags | CT_IS_ENUM;
+    td->ct_flags = basetd->ct_flags | CT_IS_ENUM | CT_USES_LOCAL;
     td->ct_name_position = name_size - 1;
     return (PyObject *)td;
 
diff --git a/new/cffi1_module.c b/new/cffi1_module.c
--- a/new/cffi1_module.c
+++ b/new/cffi1_module.c
@@ -55,7 +55,7 @@
     if (ffi == NULL || PyModule_AddObject(m, "ffi", (PyObject *)ffi) < 0)
         return -1;
 
-    LibObject *lib = lib_internal_new(ctx, module_name);
+    LibObject *lib = lib_internal_new(ffi->types_builder, module_name);
     if (lib == NULL || PyModule_AddObject(m, "lib", (PyObject *)lib) < 0)
         return -1;
 
diff --git a/new/ffi_obj.c b/new/ffi_obj.c
--- a/new/ffi_obj.c
+++ b/new/ffi_obj.c
@@ -20,10 +20,10 @@
 
 struct FFIObject_s {
     PyObject_HEAD
-    PyObject *types_dict;
     PyObject *gc_wrefs;
     struct _cffi_parse_info_s info;
     int ctx_is_static;
+    builder_c_t *types_builder;
     _cffi_opcode_t internal_output[FFI_COMPLEXITY_OUTPUT];
 };
 
@@ -31,10 +31,6 @@
                                    const struct _cffi_type_context_s *ctx,
                                    int ctx_is_static)
 {
-    PyObject *dict = PyDict_New();
-    if (dict == NULL)
-        return NULL;
-
     FFIObject *ffi;
     if (ctx_is_static) {
         ffi = (FFIObject *)PyObject_GC_New(FFIObject, ffitype);
@@ -44,11 +40,14 @@
     else {
         ffi = (FFIObject *)ffitype->tp_alloc(ffitype, 0);
     }
-    if (ffi == NULL) {
-        Py_DECREF(dict);
+    if (ffi == NULL)
+        return NULL;
+
+    ffi->types_builder = new_builder_c(ctx);
+    if (ffi->types_builder == NULL) {
+        Py_DECREF(ffi);
         return NULL;
     }
-    ffi->types_dict = dict;
     ffi->gc_wrefs = NULL;
     ffi->info.ctx = ctx;
     ffi->info.output = ffi->internal_output;
@@ -60,29 +59,17 @@
 static void ffi_dealloc(FFIObject *ffi)
 {
     PyObject_GC_UnTrack(ffi);
-    Py_DECREF(ffi->types_dict);
     Py_XDECREF(ffi->gc_wrefs);
 
-    if (!ffi->ctx_is_static) {
-        const void *mem[] = {ffi->info.ctx->types,
-                             ffi->info.ctx->globals,
-                             ffi->info.ctx->struct_unions,
-                             ffi->info.ctx->fields,
-                             ffi->info.ctx->enums,
-                             ffi->info.ctx->typenames,
-                             ffi->info.ctx};
-        int i;
-        for (i = 0; i < sizeof(mem) / sizeof(*mem); i++) {
-            if (mem[i] != NULL)
-                PyMem_Free((void *)mem[i]);
-        }
-    }
+    if (!ffi->ctx_is_static)
+        free_builder_c(ffi->types_builder);
+
     Py_TYPE(ffi)->tp_free((PyObject *)ffi);
 }
 
 static int ffi_traverse(FFIObject *ffi, visitproc visit, void *arg)
 {
-    Py_VISIT(ffi->types_dict);
+    Py_VISIT(ffi->types_builder->types_dict);
     Py_VISIT(ffi->gc_wrefs);
     return 0;
 }
@@ -128,9 +115,12 @@
        Does not return a new reference!
     */
     if ((accept & ACCEPT_STRING) && PyText_Check(arg)) {
-        PyObject *x = PyDict_GetItem(ffi->types_dict, arg);
-        if (x != NULL && CTypeDescr_Check(x))
+        PyObject *types_dict = ffi->types_builder->types_dict;
+        PyObject *x = PyDict_GetItem(types_dict, arg);
+        if (x != NULL) {
+            assert(CTypeDescr_Check(x));
             return (CTypeDescrObject *)x;
+        }
 
         char *input_text = PyText_AS_UTF8(arg);
         int index = parse_c_type(&ffi->info, input_text);
@@ -143,23 +133,20 @@
                          input_text, spaces);
             return NULL;
         }
-        CTypeDescrObject *ct = realize_c_type(ffi->info.ctx,
+        CTypeDescrObject *ct = realize_c_type(ffi->types_builder,
                                               ffi->info.output, index);
-        if (ct == NULL)
-            return NULL;
-
-        char *normalized_text = ct->ct_name;
-        x = PyDict_GetItemString(ffi->types_dict, normalized_text);
-        if (x == NULL) {
-            PyDict_SetItemString(ffi->types_dict, normalized_text,
-                                 (PyObject *)ct);
+        if (ct != NULL) {
+            /* Cache under the name given by 'arg', in addition to the
+               fact that the same ct is probably already cached under
+               its standardized name.  In a few cases, it is not, e.g.
+               if it is a primitive; for the purpose of this function,
+               the important point is the following line, which makes
+               sure that in any case the next _ffi_type() with the same
+               'arg' will succeed early, in PyDict_GetItem() above.
+            */
+            if (PyDict_SetItem(types_dict, arg, (PyObject *)ct) < 0)
+                return NULL;
         }
-        else {
-            Py_INCREF(x);
-            Py_DECREF(ct);
-            ct = (CTypeDescrObject *)x;
-        }
-        PyDict_SetItem(ffi->types_dict, arg, (PyObject *)ct);
         return ct;
     }
     else if ((accept & ACCEPT_CTYPE) && CTypeDescr_Check(arg)) {
diff --git a/new/lib_obj.c b/new/lib_obj.c
--- a/new/lib_obj.c
+++ b/new/lib_obj.c
@@ -13,13 +13,13 @@
 
 struct CPyExtFunc_s {
     PyMethodDef md;
-    const struct _cffi_type_context_s *ctx;
+    builder_c_t *types_builder;
     int type_index;
 };
 
 struct LibObject_s {
     PyObject_HEAD
-    const struct _cffi_type_context_s *l_ctx;  /* ctx object */
+    builder_c_t *l_types_builder; /* same as the one on the ffi object */
     PyObject *l_dict;           /* content, built lazily */
     PyObject *l_libname;        /* some string that gives the name of the lib */
 };
@@ -59,7 +59,7 @@
     if (xfunc->md.ml_name == NULL)
         goto no_memory;
 
-    xfunc->ctx = lib->l_ctx;
+    xfunc->types_builder = lib->l_types_builder;
     xfunc->type_index = _CFFI_GETARG(g->type_op);
 
     return PyCFunction_NewEx(&xfunc->md, NULL, lib->l_libname);
@@ -73,17 +73,11 @@
 {
     /* does not return a new reference! */
 
-    if (lib->l_ctx == NULL) {
-        PyErr_Format(FFIError, "lib '%.200s' is already closed",
-                     PyText_AS_UTF8(lib->l_libname));
-        return NULL;
-    }
-
     char *s = PyText_AsUTF8(name);
     if (s == NULL)
         return NULL;
 
-    int index = search_in_globals(lib->l_ctx, s, strlen(s));
+    int index = search_in_globals(&lib->l_types_builder->ctx, s, strlen(s));
     if (index < 0) {
         PyErr_Format(PyExc_AttributeError,
                      "lib '%.200s' has no function,"
@@ -93,7 +87,7 @@
         return NULL;
     }
 
-    const struct _cffi_global_s *g = &lib->l_ctx->globals[index];
+    const struct _cffi_global_s *g = &lib->l_types_builder->ctx.globals[index];
     PyObject *x;
     CTypeDescrObject *ct;
 
@@ -136,7 +130,8 @@
     {
         /* a constant which is not of integer type */
         char *data;
-        ct = realize_c_type(lib->l_ctx, lib->l_ctx->types,
+        ct = realize_c_type(lib->l_types_builder,
+                            lib->l_types_builder->ctx.types,
                             _CFFI_GETARG(g->type_op));
         if (ct == NULL)
             return NULL;
@@ -151,7 +146,8 @@
 
     case _CFFI_OP_GLOBAL_VAR:
         /* global variable of the exact type specified here */
-        ct = realize_c_type(lib->l_ctx, lib->l_ctx->types,
+        ct = realize_c_type(lib->l_types_builder,
+                            lib->l_types_builder->ctx.types,
                             _CFFI_GETARG(g->type_op));
         if (ct == NULL)
             return NULL;
@@ -215,8 +211,8 @@
 
 static PyObject *lib_dir(LibObject *lib, PyObject *noarg)
 {
-    const struct _cffi_global_s *g = lib->l_ctx->globals;
-    int total = lib->l_ctx->num_globals;
+    const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals;
+    int total = lib->l_types_builder->ctx.num_globals;
 
     PyObject *lst = PyList_New(total);
     if (lst == NULL)
@@ -277,7 +273,7 @@
     offsetof(LibObject, l_dict),                /* tp_dictoffset */
 };
 
-static LibObject *lib_internal_new(const struct _cffi_type_context_s *ctx,
+static LibObject *lib_internal_new(builder_c_t *types_builder,
                                    char *module_name)
 {
     LibObject *lib;
@@ -295,7 +291,7 @@
     if (lib == NULL)
         return NULL;
 
-    lib->l_ctx = ctx;
+    lib->l_types_builder = types_builder;
     lib->l_dict = dict;
     lib->l_libname = libname;
     return lib;
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
@@ -71,7 +71,7 @@
     int type_index;          // -> _cffi_types, on a OP_STRUCT_UNION
     int flags;               // CT_UNION?  CT_IS_OPAQUE?
     size_t size;
-    size_t alignment;
+    int alignment;
     int first_field_index;   // -> _cffi_fields array
     int num_fields;
 };
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
@@ -1,8 +1,118 @@
+
+typedef struct {
+    struct _cffi_type_context_s ctx;   /* inlined substructure */
+    PyObject *types_dict;
+} builder_c_t;
+
 
 static PyObject *all_primitives[_CFFI__NUM_PRIM];
 
+static PyObject *fetch_global_types_dict(void)
+{
+    static PyObject *result = NULL;
+    if (!result)
+        result = PyDict_New();
+    return result;
+}
 
-PyObject *build_primitive_type(int num)
+static void free_builder_c(builder_c_t *builder)
+{
+    Py_XDECREF(builder->types_dict);
+
+    const void *mem[] = {builder->ctx.types,
+                         builder->ctx.globals,
+                         builder->ctx.struct_unions,
+                         builder->ctx.fields,
+                         builder->ctx.enums,
+                         builder->ctx.typenames};
+    int i;
+    for (i = 0; i < sizeof(mem) / sizeof(*mem); i++) {
+        if (mem[i] != NULL)
+            PyMem_Free((void *)mem[i]);
+    }
+    PyMem_Free(builder);
+}
+
+static builder_c_t *new_builder_c(const struct _cffi_type_context_s *ctx)
+{
+    PyObject *ldict = PyDict_New();
+    if (ldict == NULL)
+        return NULL;
+
+    builder_c_t *builder = PyMem_Malloc(sizeof(builder_c_t));
+    if (builder == NULL) {
+        Py_DECREF(ldict);
+        PyErr_NoMemory();
+        return NULL;
+    }
+    builder->ctx = *ctx;
+    builder->types_dict = ldict;
+    return builder;
+}
+
+static PyObject *get_unique_type(builder_c_t *builder, PyObject *x)
+{
+    /* Replace the CTypeDescrObject 'x' with a standardized one.
+       This either just returns x, or x is decrefed and a new reference
+       to the standard equivalent is returned.
+
+       In this function, 'x' always contains a reference that must be
+       decrefed, and 'y' never does.
+    */
+    CTypeDescrObject *ct = (CTypeDescrObject *)x;
+
+    /* XXX maybe change the type of ct_name to be a real 'PyObject *'? */
+    PyObject *name = PyString_FromString(ct->ct_name);
+    if (name == NULL)
+        goto no_memory;
+
+    PyObject *y = PyDict_GetItem(builder->types_dict, name);
+    if (y != NULL) {
+        /* Already found the same ct_name in the dict.  Return the old one. */
+        Py_INCREF(y);
+        Py_DECREF(x);
+        x = y;
+        goto done;
+    }
+
+    if (!(ct->ct_flags & CT_USES_LOCAL)) {
+        /* The type is not "local", i.e. does not make use of any struct,
+           union or enum.  This means it should be shared across independent
+           ffi instances.  Look it up and possibly add it to the global
+           types dict.
+        */
+        PyObject *gdict = fetch_global_types_dict();
+        if (gdict == NULL)
+            goto no_memory;
+
+        y = PyDict_GetItem(gdict, name);
+        if (y != NULL) {
+            Py_INCREF(y);
+            Py_DECREF(x);
+            x = y;
+        }
+        else {
+            /* Not found in the global dictionary.  Put it there. */
+            if (PyDict_SetItem(gdict, name, x) < 0)
+                goto no_memory;
+        }
+    }
+
+    /* Set x in the local dict. */
+    if (PyDict_SetItem(builder->types_dict, name, x) < 0)
+        goto no_memory;
+
+ done:
+    Py_DECREF(name);
+    return x;
+
+ no_memory:
+    Py_XDECREF(name);
+    Py_DECREF(x);
+    return NULL;
+}
+
+static PyObject *build_primitive_type(int num)
 {
     /* XXX too many translations between here and new_primitive_type() */
     static const char *primitive_name[] = {
@@ -58,7 +168,7 @@
 
 
 static PyObject *
-_realize_c_type_or_func(const struct _cffi_type_context_s *ctx,
+_realize_c_type_or_func(builder_c_t *builder,
                         _cffi_opcode_t opcodes[], int index);  /* forward */
 
 
@@ -67,10 +177,9 @@
    reference.
 */
 static CTypeDescrObject *
-realize_c_type(const struct _cffi_type_context_s *ctx,
-               _cffi_opcode_t opcodes[], int index)
+realize_c_type(builder_c_t *builder, _cffi_opcode_t opcodes[], int index)
 {
-    PyObject *x = _realize_c_type_or_func(ctx, opcodes, index);
+    PyObject *x = _realize_c_type_or_func(builder, opcodes, index);
     if (x == NULL || CTypeDescr_Check(x)) {
         return (CTypeDescrObject *)x;
     }
@@ -91,7 +200,7 @@
 }
 
 static PyObject *
-_realize_c_type_or_func(const struct _cffi_type_context_s *ctx,
+_realize_c_type_or_func(builder_c_t *builder,
                         _cffi_opcode_t opcodes[], int index)
 {
     PyObject *x, *y, *z;
@@ -114,11 +223,12 @@
         break;
 
     case _CFFI_OP_POINTER:
-        y = _realize_c_type_or_func(ctx, opcodes, _CFFI_GETARG(op));
+        y = _realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op));
         if (y == NULL)
             return NULL;
         if (CTypeDescr_Check(y)) {
             x = new_pointer_type((CTypeDescrObject *)y);
+            x = get_unique_type(builder, x);
         }
         else {
             assert(PyTuple_Check(y));   /* from _CFFI_OP_FUNCTION */
@@ -132,14 +242,16 @@
         length = (Py_ssize_t)opcodes[index + 1];
         /* fall-through */
     case _CFFI_OP_OPEN_ARRAY:
-        y = (PyObject *)realize_c_type(ctx, opcodes, _CFFI_GETARG(op));
+        y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op));
         if (y == NULL)
             return NULL;
         z = new_pointer_type((CTypeDescrObject *)y);
+        z = get_unique_type(builder, z);
         Py_DECREF(y);
         if (z == NULL)
             return NULL;
         x = new_array_type((CTypeDescrObject *)z, length);
+        x = get_unique_type(builder, x);
         Py_DECREF(z);
         break;
 
@@ -148,8 +260,8 @@
         const struct _cffi_struct_union_s *s;
         _cffi_opcode_t op2;
 
-        s = &ctx->struct_unions[_CFFI_GETARG(op)];
-        op2 = ctx->types[s->type_index];
+        s = &builder->ctx.struct_unions[_CFFI_GETARG(op)];
+        op2 = builder->ctx.types[s->type_index];
         if ((((uintptr_t)op2) & 1) == 0) {
             x = (PyObject *)op2;
             Py_INCREF(x);
@@ -174,7 +286,7 @@
                 ct->ct_length = s->alignment;
                 ct->ct_flags &= ~CT_IS_OPAQUE;
                 ct->ct_flags |= CT_LAZY_FIELD_LIST;
-                ct->ct_extra = (void *)ctx;
+                ct->ct_extra = builder;
             }
 
             /* We are going to update the "primary" OP_STRUCT_OR_UNION
@@ -184,7 +296,7 @@
                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 = ctx->types;
+            opcodes = builder->ctx.types;
             index = s->type_index;
         }
         break;
@@ -195,7 +307,7 @@
         PyObject *fargs;
         int i, base_index, num_args;
 
-        y = (PyObject *)realize_c_type(ctx, opcodes, _CFFI_GETARG(op));
+        y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op));
         if (y == NULL)
             return NULL;
 
@@ -212,7 +324,7 @@
         }
 
         for (i = 0; i < num_args; i++) {
-            z = (PyObject *)realize_c_type(ctx, opcodes, base_index + i);
+            z = (PyObject *)realize_c_type(builder, opcodes, base_index + i);
             if (z == NULL) {
                 Py_DECREF(fargs);
                 Py_DECREF(y);
@@ -222,6 +334,7 @@
         }
 
         z = new_function_type(fargs, (CTypeDescrObject *)y, 0, FFI_DEFAULT_ABI);
+        z = get_unique_type(builder, z);
         Py_DECREF(fargs);
         Py_DECREF(y);
         if (z == NULL)
@@ -234,7 +347,7 @@
     }
 
     case _CFFI_OP_NOOP:
-        x = _realize_c_type_or_func(ctx, opcodes, _CFFI_GETARG(op));
+        x = _realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op));
         break;
 
     case _CFFI_OP_TYPENAME:
@@ -242,8 +355,8 @@
         /* essential: the TYPENAME opcode resolves the type index looked
            up in the 'ctx->typenames' array, but it does so in 'ctx->types'
            instead of in 'opcodes'! */
-        int type_index = ctx->typenames[_CFFI_GETARG(op)].type_index;
-        x = _realize_c_type_or_func(ctx, ctx->types, type_index);
+        int type_index = builder->ctx.typenames[_CFFI_GETARG(op)].type_index;
+        x = _realize_c_type_or_func(builder, builder->ctx.types, type_index);
         break;
     }
 
@@ -252,7 +365,7 @@
         return NULL;
     }
 
-    if (x != NULL && opcodes == ctx->types) {
+    if (x != NULL && opcodes == builder->ctx.types) {
         assert((((uintptr_t)x) & 1) == 0);
         assert((((uintptr_t)opcodes[index]) & 1) == 1);
         Py_INCREF(x);
@@ -268,8 +381,8 @@
     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);
+        builder_c_t *builder = ct->ct_extra;
+        assert(builder != NULL);
 
         char *p = ct->ct_name;
         if (memcmp(p, "struct ", 7) == 0)
@@ -277,12 +390,13 @@
         else if (memcmp(p, "union ", 6) == 0)
             p += 6;
 
-        int n = search_in_struct_unions(ctx, p, strlen(p));
+        int n = search_in_struct_unions(&builder->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];
+        const struct _cffi_struct_union_s *s = &builder->ctx.struct_unions[n];
+        const struct _cffi_field_s *fld =
+            &builder->ctx.fields[s->first_field_index];
 
         /* XXX painfully build all the Python objects that are the args
            to b_complete_struct_or_union() */
@@ -300,7 +414,8 @@
             switch (_CFFI_GETOP(op)) {
 
             case _CFFI_OP_NOOP:
-                ctf = realize_c_type(ctx, ctx->types, _CFFI_GETARG(op));
+                ctf = realize_c_type(builder, builder->ctx.types,
+                                     _CFFI_GETARG(op));
                 break;
 
             default:
@@ -332,7 +447,7 @@
         ct->ct_flags &= ~CT_IS_OPAQUE;
         Py_DECREF(args);
         if (res == NULL) {
-            ct->ct_extra = (void *)ctx;
+            ct->ct_extra = builder;
             return -1;
         }
 
diff --git a/new/test_ffi_obj.py b/new/test_ffi_obj.py
--- a/new/test_ffi_obj.py
+++ b/new/test_ffi_obj.py
@@ -19,3 +19,19 @@
 
 def test_ffi_no_argument():
     py.test.raises(TypeError, _cffi1_backend.FFI, 42)
+
+def test_ffi_cache_type():
+    ffi = _cffi1_backend.FFI()
+    t1 = ffi.typeof("int **")
+    t2 = ffi.typeof("int *")
+    assert t2.item is t1.item.item
+    assert t2 is t1.item
+    assert ffi.typeof("int[][10]") is ffi.typeof("int[][10]")
+    assert ffi.typeof("int(*)()") is ffi.typeof("int(*)()")
+
+def test_ffi_cache_type_globally():
+    ffi1 = _cffi1_backend.FFI()
+    ffi2 = _cffi1_backend.FFI()
+    t1 = ffi1.typeof("int *")
+    t2 = ffi2.typeof("int *")
+    assert t1 is t2
diff --git a/new/test_recompiler.py b/new/test_recompiler.py
--- a/new/test_recompiler.py
+++ b/new/test_recompiler.py
@@ -173,14 +173,31 @@
     ffi = FFI()
     ffi.cdef("""struct foo_s { int b; short a; };
                 struct bar_s { struct foo_s *f; };""")
-    lib = verify(ffi, 'test_verify_struct',
+    ffi, lib = verify2(ffi, 'test_verify_struct',
                  """struct foo_s { short a; int b; };
                     struct bar_s { struct foo_s *f; };""")
+    ffi.typeof("struct bar_s *")
     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
+
+def test_type_caching():
+    ffi1 = FFI(); ffi1.cdef("struct foo_s;")
+    ffi2 = FFI(); ffi2.cdef("struct foo_s;")    # different one!
+    ffi1, lib1 = verify2(ffi1, 'test_type_caching_1', 'struct foo_s;')
+    ffi2, lib2 = verify2(ffi2, 'test_type_caching_2', 'struct foo_s;')
+    # shared types
+    assert ffi1.typeof("long") is ffi2.typeof("long")
+    assert ffi1.typeof("long**") is ffi2.typeof("long * *")
+    assert ffi1.typeof("long(*)(int, ...)") is ffi2.typeof("long(*)(int, ...)")
+    # non-shared types
+    assert ffi1.typeof("struct foo_s") is not ffi2.typeof("struct foo_s")
+    assert ffi1.typeof("struct foo_s *") is not ffi2.typeof("struct foo_s *")
+    assert ffi1.typeof("struct foo_s*(*)()") is not (
+        ffi2.typeof("struct foo_s*(*)()"))
+    assert ffi1.typeof("void(*)(struct foo_s*)") is not (
+        ffi2.typeof("void(*)(struct foo_s*)"))


More information about the pypy-commit mailing list