[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