[pypy-commit] cffi default: Implement caching of the types across multiple FFI instances. The types
arigo
noreply at buildbot.pypy.org
Mon Jul 9 11:26:53 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r598:331d828994cd
Date: 2012-07-08 16:38 +0200
http://bitbucket.org/cffi/cffi/changeset/331d828994cd/
Log: Implement caching of the types across multiple FFI instances. The
types are shared as long as they don't depend on a particular name
(i.e. if they contain no struct or union or enum).
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -1,3 +1,4 @@
+import weakref
class BaseType(object):
@@ -46,7 +47,7 @@
return 'void' + replace_with
def new_backend_type(self, ffi):
- return ffi._backend.new_void_type()
+ return global_cache(ffi, 'new_void_type')
void_type = VoidType()
@@ -72,7 +73,7 @@
return self.name in ('double', 'float')
def new_backend_type(self, ffi):
- return ffi._backend.new_primitive_type(self.name)
+ return global_cache(ffi, 'new_primitive_type', self.name)
class BaseFunctionType(BaseType):
@@ -120,7 +121,8 @@
return args
def new_backend_type(self, ffi, result, *args):
- return ffi._backend.new_function_type(args, result, self.ellipsis)
+ return global_cache(ffi, 'new_function_type',
+ args, result, self.ellipsis)
class PointerType(BaseType):
@@ -136,7 +138,7 @@
return (ffi._get_cached_btype(self.totype),)
def new_backend_type(self, ffi, BItem):
- return ffi._backend.new_pointer_type(BItem)
+ return global_cache(ffi, 'new_pointer_type', BItem)
class ConstPointerType(PointerType):
@@ -172,7 +174,7 @@
return (ffi._get_cached_btype(PointerType(self.item)),)
def new_backend_type(self, ffi, BPtrItem):
- return ffi._backend.new_array_type(BPtrItem, self.length)
+ return global_cache(ffi, 'new_array_type', BPtrItem, self.length)
class StructOrUnion(BaseType):
@@ -293,3 +295,21 @@
tp = StructType('$%s' % name, None, None, None)
tp.forcename = name
return tp
+
+def global_cache(ffi, funcname, *args):
+ try:
+ return ffi._backend.__typecache[args]
+ except KeyError:
+ pass
+ except AttributeError:
+ # initialize the __typecache attribute, either at the module level
+ # if ffi._backend is a module, or at the class level if ffi._backend
+ # is some instance.
+ ModuleType = type(weakref)
+ if isinstance(ffi._backend, ModuleType):
+ ffi._backend.__typecache = weakref.WeakValueDictionary()
+ else:
+ type(ffi._backend).__typecache = weakref.WeakValueDictionary()
+ res = getattr(ffi._backend, funcname)(*args)
+ ffi._backend.__typecache[args] = res
+ return res
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -1042,3 +1042,22 @@
f = ffi.callback("long long cb(long i, ...)", cb)
res = f(10, ffi.cast("int", 100), ffi.cast("long long", 1000))
assert res == 20 + 300 + 5000
+
+ def test_unique_types(self):
+ ffi1 = FFI(backend=self.Backend())
+ ffi2 = FFI(backend=self.Backend())
+ assert ffi1.typeof("char") is ffi2.typeof("char ")
+ assert ffi1.typeof("long") is ffi2.typeof("signed long int")
+ assert ffi1.typeof("double *") is ffi2.typeof("double*")
+ assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *")
+ assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]")
+ assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]")
+ assert ffi1.typeof("void") is ffi2.typeof("void")
+ assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)")
+ #
+ # these depend on user-defined data, so should not be shared
+ assert ffi1.typeof("struct foo") is not ffi2.typeof("struct foo")
+ assert ffi1.typeof("union foo *") is not ffi2.typeof("union foo*")
+ assert ffi1.typeof("enum foo") is not ffi2.typeof("enum foo")
+ # sanity check: twice 'ffi1'
+ assert ffi1.typeof("struct foo*") is ffi1.typeof("struct foo *")
diff --git a/testing/test_cdata.py b/testing/test_cdata.py
--- a/testing/test_cdata.py
+++ b/testing/test_cdata.py
@@ -13,17 +13,17 @@
return "fake library"
def new_primitive_type(self, name):
- return FakePrimitiveType(name)
+ return FakeType("primitive " + name)
def new_void_type(self):
- return "void!"
+ return FakeType("void")
def new_pointer_type(self, x):
- return 'ptr-to-%r!' % (x,)
+ return FakeType('ptr-to-%r' % (x,))
def cast(self, x, y):
return 'casted!'
-class FakePrimitiveType(object):
+class FakeType(object):
def __init__(self, cdecl):
self.cdecl = cdecl
@@ -31,5 +31,5 @@
def test_typeof():
ffi = FFI(backend=FakeBackend())
clong = ffi.typeof("signed long int")
- assert isinstance(clong, FakePrimitiveType)
- assert clong.cdecl == 'long'
+ assert isinstance(clong, FakeType)
+ assert clong.cdecl == 'primitive long'
diff --git a/testing/test_parsing.py b/testing/test_parsing.py
--- a/testing/test_parsing.py
+++ b/testing/test_parsing.py
@@ -17,14 +17,17 @@
return FakeLibrary()
def new_function_type(self, args, result, has_varargs):
- return '<func (%s), %s, %s>' % (', '.join(args), result, has_varargs)
+ args = [arg.cdecl for arg in args]
+ result = result.cdecl
+ return FakeType(
+ '<func (%s), %s, %s>' % (', '.join(args), result, has_varargs))
def new_primitive_type(self, name):
assert name == name.lower()
- return '<%s>' % name
+ return FakeType('<%s>' % name)
def new_pointer_type(self, itemtype):
- return '<pointer to %s>' % (itemtype,)
+ return FakeType('<pointer to %s>' % (itemtype,))
def new_struct_type(self, name):
return FakeStruct(name)
@@ -34,18 +37,24 @@
s.fields = fields
def new_array_type(self, ptrtype, length):
- return '<array %s x %s>' % (ptrtype, length)
+ return FakeType('<array %s x %s>' % (ptrtype, length))
def new_void_type(self):
- return "<void>"
+ return FakeType("<void>")
def cast(self, x, y):
return 'casted!'
+class FakeType(object):
+ def __init__(self, cdecl):
+ self.cdecl = cdecl
+ def __str__(self):
+ return self.cdecl
+
class FakeStruct(object):
def __init__(self, name):
self.name = name
def __str__(self):
- return ', '.join([y + x for x, y, z in self.fields])
+ return ', '.join([str(y) + str(x) for x, y, z in self.fields])
class FakeLibrary(object):
@@ -55,7 +64,7 @@
class FakeFunction(object):
def __init__(self, BType, name):
- self.BType = BType
+ self.BType = str(BType)
self.name = name
@@ -99,7 +108,7 @@
UInt foo(void);
""")
C = ffi.dlopen(None)
- assert ffi.typeof("UIntReally") == '<unsigned int>'
+ assert str(ffi.typeof("UIntReally")) == '<unsigned int>'
assert C.foo.BType == '<func (), <unsigned int>, False>'
def test_typedef_more_complex():
@@ -110,7 +119,7 @@
""")
C = ffi.dlopen(None)
assert str(ffi.typeof("foo_t")) == '<int>a, <int>b'
- assert ffi.typeof("foo_p") == '<pointer to <int>a, <int>b>'
+ assert str(ffi.typeof("foo_p")) == '<pointer to <int>a, <int>b>'
assert C.foo.BType == ('<func (<pointer to <pointer to '
'<int>a, <int>b>>), <int>, False>')
@@ -121,7 +130,7 @@
""")
type = ffi._parser.parse_type("array_t", force_pointer=True)
BType = ffi._get_cached_btype(type)
- assert BType == '<array <pointer to <int>> x 5>'
+ assert str(BType) == '<array <pointer to <int>> x 5>'
def test_typedef_array_convert_array_to_pointer():
ffi = FFI(backend=FakeBackend())
@@ -130,7 +139,7 @@
""")
type = ffi._parser.parse_type("fn_t")
BType = ffi._get_cached_btype(type)
- assert BType == '<func (<pointer to <int>>), <int>, False>'
+ assert str(BType) == '<func (<pointer to <int>>), <int>, False>'
def test_remove_comments():
ffi = FFI(backend=FakeBackend())
More information about the pypy-commit
mailing list