[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