[pypy-commit] pypy cffi-1.0: in-progress
arigo
noreply at buildbot.pypy.org
Sat May 2 21:02:28 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r76984:590c388e5920
Date: 2015-05-02 21:02 +0200
http://bitbucket.org/pypy/pypy/changeset/590c388e5920/
Log: in-progress
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -46,6 +46,9 @@
'FFI_DEFAULT_ABI': 'ctypefunc._get_abi(space, "FFI_DEFAULT_ABI")',
'FFI_CDECL': 'ctypefunc._get_abi(space,"FFI_DEFAULT_ABI")',#win32 name
+
+ # CFFI 1.0
+ 'FFI': 'ffi_obj.W_FFIObject',
}
if sys.platform == 'win32':
interpleveldefs['getwinerror'] = 'cerrno.getwinerror'
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -0,0 +1,70 @@
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from rpython.rlib import jit, rgc
+
+from pypy.module._cffi_backend import parse_c_type, realize_c_type
+
+
+ACCEPT_STRING = 1
+ACCEPT_CTYPE = 2
+ACCEPT_CDATA = 4
+ACCEPT_ALL = ACCEPT_STRING | ACCEPT_CTYPE | ACCEPT_CDATA
+CONSIDER_FN_AS_FNPTR = 8
+
+
+class W_FFIObject(W_Root):
+
+ def __init__(self, space, src_ctx=parse_c_type.NULL_CTX):
+ self.space = space
+ self.types_dict = {}
+ self.ctxobj = parse_c_type.allocate_ctxobj(src_ctx)
+ if src_ctx:
+ self.cached_types = [None] * parse_c_type.get_num_types(src_ctx)
+ else:
+ self.cached_types = None
+
+ @rgc.must_be_light_finalizer
+ def __del__(self):
+ parse_c_type.free_ctxobj(self.ctxobj)
+
+ @jit.elidable
+ def parse_string_to_type(self, x):
+ try:
+ return self.types_dict[x]
+ except KeyError:
+ pass
+
+ index = parse_c_type.parse_c_type(self.ctxobj.info, x)
+ if index < 0:
+ xxxx
+ ct = realize_c_type.realize_c_type(self, self.ctxobj.info.c_output,
+ index)
+ self.types_dict[x] = ct
+ return ct
+
+ def ffi_type(self, w_x, accept):
+ space = self.space
+ if (accept & ACCEPT_STRING) and space.isinstance_w(w_x, space.w_str):
+ return self.parse_string_to_type(space.str_w(w_x))
+ yyyy
+
+ def descr_new(self):
+ XXX
+
+ def descr_typeof(self, w_x):
+ return self.ffi_type(w_x, ACCEPT_STRING | ACCEPT_CDATA)
+
+
+#@unwrap_spec()
+def W_FFIObject___new__(space, w_subtype):
+ r = space.allocate_instance(W_FFIObject, w_subtype)
+ r.__init__(space)
+ return space.wrap(r)
+
+W_FFIObject.typedef = TypeDef(
+ 'CompiledFFI',
+ __new__ = interp2app(W_FFIObject___new__),
+ new = interp2app(W_FFIObject.descr_new),
+ typeof = interp2app(W_FFIObject.descr_typeof),
+ )
diff --git a/pypy/module/_cffi_backend/parse_c_type.py b/pypy/module/_cffi_backend/parse_c_type.py
--- a/pypy/module/_cffi_backend/parse_c_type.py
+++ b/pypy/module/_cffi_backend/parse_c_type.py
@@ -2,6 +2,7 @@
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.translator import cdir
from rpython.translator.tool.cbuild import ExternalCompilationInfo
+from rpython.rlib.objectmodel import specialize
src_dir = py.path.local(os.path.dirname(__file__)) / 'src'
@@ -51,7 +52,8 @@
('num_struct_unions', rffi.INT),
('num_enums', rffi.INT),
('num_typenames', rffi.INT),
- ('includes', rffi.CCHARPP))
+ ('includes', rffi.CCHARPP),
+ ('num_types', rffi.INT))
PINFO = rffi.CStructPtr('struct _cffi_parse_info_s',
('ctx', PCTX),
@@ -60,4 +62,37 @@
('error_location', rffi.SIZE_T),
('error_message', rffi.CCHARP))
-parse_c_type = llexternal('parse_c_type', [PINFO, rffi.CCHARP], rffi.INT)
+ll_parse_c_type = llexternal('parse_c_type', [PINFO, rffi.CCHARP], rffi.INT)
+
+def parse_c_type(info, input):
+ p_input = rffi.str2charp(input)
+ try:
+ res = ll_parse_c_type(info, p_input)
+ finally:
+ rffi.free_charp(p_input)
+ return rffi.cast(lltype.Signed, res)
+
+NULL_CTX = lltype.nullptr(PCTX.TO)
+FFI_COMPLEXITY_OUTPUT = 1200 # xxx should grow as needed
+internal_output = lltype.malloc(rffi.VOIDPP.TO, FFI_COMPLEXITY_OUTPUT,
+ flavor='raw', zero=True, immortal=True)
+PCTXOBJ = lltype.Ptr(lltype.Struct('cffi_ctxobj',
+ ('ctx', PCTX.TO),
+ ('info', PINFO.TO)))
+
+def allocate_ctxobj(src_ctx):
+ p = lltype.malloc(PCTXOBJ.TO, flavor='raw', zero=True)
+ if src_ctx:
+ rffi.c_memcpy(rffi.cast(rffi.VOIDP, p.ctx),
+ rffi.cast(rffi.VOIDP, src_ctx),
+ rffi.cast(rffi.SIZE_T, rffi.sizeof(PCTX.TO)))
+ p.info.c_ctx = p.ctx
+ p.info.c_output = internal_output
+ rffi.setintfield(p.info, 'c_output_size', FFI_COMPLEXITY_OUTPUT)
+ return p
+
+def free_ctxobj(p):
+ lltype.free(p, flavor='raw')
+
+def get_num_types(src_ctx):
+ return rffi.getintfield(src_ctx, 'c_num_types')
diff --git a/pypy/module/_cffi_backend/realize_c_type.py b/pypy/module/_cffi_backend/realize_c_type.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/realize_c_type.py
@@ -0,0 +1,114 @@
+from rpython.rtyper.lltypesystem import rffi
+from pypy.interpreter.error import oefmt
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend import cffi_opcode, newtype
+
+
+def getop(op):
+ return rffi.cast(rffi.SIGNED, op) & 0xFF
+
+def getarg(op):
+ return rffi.cast(rffi.SIGNED, op) >> 8
+
+
+
+class RealizeCache:
+ NAMES = [None,
+ "_Bool",
+ "char",
+ "signed char",
+ "unsigned char",
+ "short",
+ "unsigned short",
+ "int",
+ "unsigned int",
+ "long",
+ "unsigned long",
+ "long long",
+ "unsigned long long",
+ "float",
+ "double",
+ "long double",
+ "wchar_t",
+ "int8_t",
+ "uint8_t",
+ "int16_t",
+ "uint16_t",
+ "int32_t",
+ "uint32_t",
+ "int64_t",
+ "uint64_t",
+ "intptr_t",
+ "uintptr_t",
+ "ptrdiff_t",
+ "size_t",
+ "ssize_t",
+ "int_least8_t",
+ "uint_least8_t",
+ "int_least16_t",
+ "uint_least16_t",
+ "int_least32_t",
+ "uint_least32_t",
+ "int_least64_t",
+ "uint_least64_t",
+ "int_fast8_t",
+ "uint_fast8_t",
+ "int_fast16_t",
+ "uint_fast16_t",
+ "int_fast32_t",
+ "uint_fast32_t",
+ "int_fast64_t",
+ "uint_fast64_t",
+ "intmax_t",
+ "uintmax_t",
+ ]
+ def __init__(self, space):
+ self.all_primitives = [None] * cffi_opcode._NUM_PRIM
+
+def get_primitive_type(space, num):
+ realize_cache = space.fromcache(RealizeCache)
+ w_ctype = realize_cache.all_primitives[num]
+ if w_ctype is None:
+ if num == cffi_opcode.PRIM_VOID:
+ w_ctype = newtype.new_void_type()
+ elif 0 <= num < len(RealizeCache.NAMES) and RealizeCache.NAMES[num]:
+ w_ctype = newtype.new_primitive_type(space, RealizeCache.NAMES[num])
+ else:
+ raise oefmt(ffi.space.w_NotImplementedError, "prim=%d", case)
+ realize_cache.all_primitives[num] = w_ctype
+ return w_ctype
+
+
+def realize_c_type(ffi, opcodes, index):
+ """Interpret an opcodes[] array. If opcodes == ffi.ctxobj.ctx.c_types,
+ store all the intermediate types back in the opcodes[].
+ """
+ x = _realize_c_type_or_func(ffi, opcodes, index)
+ if isinstance(x, W_CType):
+ return x
+ else:
+ xxxx
+
+
+def _realize_c_type_or_func(ffi, opcodes, index):
+ op = opcodes[index]
+
+ from_ffi = False
+ #...
+
+ case = getop(op)
+ if case == cffi_opcode.OP_PRIMITIVE:
+ x = get_primitive_type(ffi.space, getarg(op))
+ elif case == cffi_opcode.OP_POINTER:
+ y = _realize_c_type_or_func(ffi, opcodes, getarg(op))
+ if isinstance(y, W_CType):
+ x = newtype.new_pointer_type(ffi.space, y)
+ else:
+ yyyyyyyyy
+ else:
+ raise oefmt(ffi.space.w_NotImplementedError, "op=%d", case)
+
+ if from_ffi:
+ yyyy # ...
+
+ return x
diff --git a/pypy/module/_cffi_backend/src/parse_c_type.h b/pypy/module/_cffi_backend/src/parse_c_type.h
--- a/pypy/module/_cffi_backend/src/parse_c_type.h
+++ b/pypy/module/_cffi_backend/src/parse_c_type.h
@@ -130,6 +130,7 @@
int num_enums;
int num_typenames;
const char *const *includes;
+ int num_types;
};
struct _cffi_parse_info_s {
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py b/pypy/module/_cffi_backend/test/test_ffi_obj.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -0,0 +1,136 @@
+
+class AppTestFFIObj:
+ spaceconfig = dict(usemodules=('_cffi_backend', ))
+
+ def test_ffi_new(self):
+ import _cffi_backend as _cffi1_backend
+ ffi = _cffi1_backend.FFI()
+ p = ffi.new("int *")
+ p[0] = -42
+ assert p[0] == -42
+
+ def test_ffi_subclass(self):
+ import _cffi_backend as _cffi1_backend
+ class FOO(_cffi1_backend.FFI):
+ def __init__(self, x):
+ self.x = x
+ foo = FOO(42)
+ assert foo.x == 42
+ p = foo.new("int *")
+ assert p[0] == 0
+
+ def test_ffi_no_argument(self):
+ import _cffi_backend as _cffi1_backend
+ py.test.raises(TypeError, _cffi1_backend.FFI, 42)
+
+ def test_ffi_cache_type(self):
+ import _cffi_backend as _cffi1_backend
+ 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(self):
+ ffi1 = _cffi1_backend.FFI()
+ ffi2 = _cffi1_backend.FFI()
+ t1 = ffi1.typeof("int *")
+ t2 = ffi2.typeof("int *")
+ assert t1 is t2
+
+ def test_ffi_invalid(self):
+ ffi = _cffi1_backend.FFI()
+ # array of 10 times an "int[]" is invalid
+ py.test.raises(ValueError, ffi.typeof, "int[10][]")
+
+ def test_ffi_docstrings(self):
+ # check that all methods of the FFI class have a docstring.
+ check_type = type(_cffi1_backend.FFI.new)
+ for methname in dir(_cffi1_backend.FFI):
+ if not methname.startswith('_'):
+ method = getattr(_cffi1_backend.FFI, methname)
+ if isinstance(method, check_type):
+ assert method.__doc__, "method FFI.%s() has no docstring" % (
+ methname,)
+
+ def test_ffi_NULL(self):
+ NULL = _cffi1_backend.FFI.NULL
+ assert _cffi1_backend.FFI().typeof(NULL).cname == "void *"
+
+ def test_ffi_string(self):
+ ffi = _cffi1_backend.FFI()
+ p = ffi.new("char[]", b"foobar\x00baz")
+ assert ffi.string(p) == b"foobar"
+
+ def test_ffi_errno(self):
+ # xxx not really checking errno, just checking that we can read/write it
+ ffi = _cffi1_backend.FFI()
+ ffi.errno = 42
+ assert ffi.errno == 42
+
+ def test_ffi_alignof(self):
+ ffi = _cffi1_backend.FFI()
+ assert ffi.alignof("int") == 4
+ assert ffi.alignof("int[]") == 4
+ assert ffi.alignof("int[41]") == 4
+ assert ffi.alignof("short[41]") == 2
+ assert ffi.alignof(ffi.new("int[41]")) == 4
+ assert ffi.alignof(ffi.new("int[]", 41)) == 4
+
+ def test_ffi_sizeof(self):
+ ffi = _cffi1_backend.FFI()
+ assert ffi.sizeof("int") == 4
+ py.test.raises(ffi.error, ffi.sizeof, "int[]")
+ assert ffi.sizeof("int[41]") == 41 * 4
+ assert ffi.sizeof(ffi.new("int[41]")) == 41 * 4
+ assert ffi.sizeof(ffi.new("int[]", 41)) == 41 * 4
+
+ def test_ffi_callback(self):
+ ffi = _cffi1_backend.FFI()
+ assert ffi.callback("int(int)", lambda x: x + 42)(10) == 52
+ assert ffi.callback("int(*)(int)", lambda x: x + 42)(10) == 52
+ assert ffi.callback("int(int)", lambda x: x + "", -66)(10) == -66
+ assert ffi.callback("int(int)", lambda x: x + "", error=-66)(10) == -66
+
+ def test_ffi_callback_decorator(self):
+ ffi = _cffi1_backend.FFI()
+ assert ffi.callback(ffi.typeof("int(*)(int)"))(lambda x: x + 42)(10) == 52
+ deco = ffi.callback("int(int)", error=-66)
+ assert deco(lambda x: x + "")(10) == -66
+ assert deco(lambda x: x + 42)(10) == 52
+
+ def test_ffi_getctype(self):
+ ffi = _cffi1_backend.FFI()
+ assert ffi.getctype("int") == "int"
+ assert ffi.getctype("int", 'x') == "int x"
+ assert ffi.getctype("int*") == "int *"
+ assert ffi.getctype("int*", '') == "int *"
+ assert ffi.getctype("int*", 'x') == "int * x"
+ assert ffi.getctype("int", '*') == "int *"
+ assert ffi.getctype("int", ' * x ') == "int * x"
+ assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *"
+ assert ffi.getctype("int", '[5]') == "int[5]"
+ assert ffi.getctype("int[5]", '[6]') == "int[6][5]"
+ assert ffi.getctype("int[5]", '(*)') == "int(*)[5]"
+ # special-case for convenience: automatically put '()' around '*'
+ assert ffi.getctype("int[5]", '*') == "int(*)[5]"
+ assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]"
+ assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]"
+
+ def test_addressof(self):
+ ffi = _cffi1_backend.FFI()
+ a = ffi.new("int[10]")
+ b = ffi.addressof(a, 5)
+ b[2] = -123
+ assert a[7] == -123
+
+ def test_handle(self):
+ ffi = _cffi1_backend.FFI()
+ x = [2, 4, 6]
+ xp = ffi.new_handle(x)
+ assert ffi.typeof(xp) == ffi.typeof("void *")
+ assert ffi.from_handle(xp) is x
+ yp = ffi.new_handle([6, 4, 2])
+ assert ffi.from_handle(yp) == [6, 4, 2]
diff --git a/pypy/module/_cffi_backend/test/test_parse_c_type.py b/pypy/module/_cffi_backend/test/test_parse_c_type.py
--- a/pypy/module/_cffi_backend/test/test_parse_c_type.py
+++ b/pypy/module/_cffi_backend/test/test_parse_c_type.py
@@ -92,9 +92,7 @@
rffi.setintfield(info, 'c_output_size', OUTPUT_SIZE)
for j in range(OUTPUT_SIZE):
out[j] = rffi.cast(rffi.VOIDP, -424242)
- p_input = rffi.str2charp(input.encode('ascii'))
- res = parse_c_type.parse_c_type(info, p_input)
- rffi.free_charp(p_input)
+ res = parse_c_type.parse_c_type(info, input.encode('ascii'))
if res < 0:
raise ParseError(rffi.charp2str(info.c_error_message).decode('ascii'),
rffi.getintfield(info, 'c_error_location'))
More information about the pypy-commit
mailing list