[pypy-commit] pypy cffi-1.0: in-progress

arigo noreply at buildbot.pypy.org
Sun May 17 19:30:59 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r77355:95596772fe0c
Date: 2015-05-17 19:31 +0200
http://bitbucket.org/pypy/pypy/changeset/95596772fe0c/

Log:	in-progress

diff --git a/pypy/module/_cffi_backend/cdlopen.py b/pypy/module/_cffi_backend/cdlopen.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/cdlopen.py
@@ -0,0 +1,93 @@
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rlib.objectmodel import specialize
+
+from pypy.module._cffi_backend.parse_c_type import (
+    _CFFI_OPCODE_T, GLOBAL_S, CDL_INTCONST_S,
+    ll_set_cdl_realize_global_int)
+from pypy.module._cffi_backend.realize_c_type import getop
+from pypy.module._cffi_backend import cffi_opcode
+
+
+class StringDecoder:
+    def __init__(self, ffi, string):
+        self.ffi = ffi
+        self.string = string
+        self.pos = 0
+
+    def next_4bytes(self):
+        pos = self.pos
+        src = ord(self.string[pos])
+        if src >= 0x80:
+            src -= 0x100
+        src = ((src << 24) |
+               (ord(self.string[pos + 1]) << 16) |
+               (ord(self.string[pos + 2]) << 8 ) |
+               (ord(self.string[pos + 3])      ))
+        self.pos = pos + 4
+        return src
+
+    def next_opcode(self):
+        return rffi.cast(_CFFI_OPCODE_T, self.next_4bytes())
+
+    def next_name(self):
+        frm = self.pos
+        i = self.string.find('\x00', frm)
+        if i < 0:
+            i = len(self.string)
+        pos = i + 1
+        p = rffi.str2charp(self.string[frm : i])
+        self.ffi._finalizer.free_mems.append(p)
+        return p
+
+
+def allocate(ffi, nbytes):
+    p = lltype.malloc(rffi.CCHARP.TO, nbytes, flavor='raw', zero=True)
+    ffi._finalizer.free_mems.append(p)
+    return p
+
+ at specialize.arg(1)
+def allocate_array(ffi, OF, nitems):
+    p = allocate(ffi, nitems * rffi.sizeof(OF))
+    return rffi.cast(rffi.CArrayPtr(OF), p)
+
+
+def ffiobj_init(ffi, module_name, version, types, w_globals,
+                w_struct_unions, w_enums, w_typenames, w_includes):
+    space = ffi.space
+
+    if types:
+        # unpack a string of 4-byte entries into an array of _cffi_opcode_t
+        n = len(types) // 4
+        ntypes = allocate_array(ffi, _CFFI_OPCODE_T, n)
+        decoder = StringDecoder(ffi, types)
+        for i in range(n):
+            ntypes[i] = decoder.next_opcode()
+        ffi.ctxobj.ctx.c_types = ntypes
+        rffi.setintfield(ffi.ctxobj.ctx, 'c_num_types', n)
+        ffi.cached_types = [None] * n
+
+    if w_globals is not None:
+        globals_w = space.fixedview(w_globals)
+        n = len(globals_w) // 2
+        size = n * rffi.sizeof(GLOBAL_S) + n * rffi.sizeof(CDL_INTCONST_S)
+        size = llmemory.raw_malloc_usage(size)
+        p = allocate(ffi, size)
+        nglobs = rffi.cast(rffi.CArrayPtr(GLOBAL_S), p)
+        p = rffi.ptradd(p, llmemory.raw_malloc_usage(n * rffi.sizeof(GLOBAL_S)))
+        nintconsts = rffi.cast(rffi.CArrayPtr(CDL_INTCONST_S), p)
+        for i in range(n):
+            decoder = StringDecoder(ffi, space.str_w(globals_w[i * 2]))
+            nglobs[i].c_type_op = decoder.next_opcode()
+            nglobs[i].c_name = decoder.next_name()
+            op = getop(nglobs[i].c_type_op)
+            if op == cffi_opcode.OP_CONSTANT_INT or op == cffi_opcode.OP_ENUM:
+                w_integer = globals_w[i * 2 + 1]
+                ll_set_cdl_realize_global_int(nglobs[i])
+                bigint = space.bigint_w(w_integer)
+                ullvalue = bigint.ulonglongmask()
+                rffi.setintfield(nintconsts[i], 'neg', int(bigint.sign <= 0))
+                rffi.setintfield(nintconsts[i], 'value', ullvalue)
+        ffi.ctxobj.ctx.c_globals = nglobs
+        rffi.setintfield(ffi.ctxobj.ctx, 'c_num_globals', n)
+
+    # ...
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -4,13 +4,14 @@
 from pypy.interpreter.typedef import TypeDef, GetSetProperty, ClassAttr
 from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
 from rpython.rlib import jit, rgc
-from rpython.rtyper.lltypesystem import rffi
+from rpython.rtyper.lltypesystem import lltype, rffi
 
 from pypy.module._cffi_backend import get_dict_rtld_constants
 from pypy.module._cffi_backend import parse_c_type, realize_c_type
 from pypy.module._cffi_backend import newtype, cerrno, ccallback, ctypearray
 from pypy.module._cffi_backend import ctypestruct, ctypeptr, handle
 from pypy.module._cffi_backend import cbuffer, func, cgc, structwrapper
+from pypy.module._cffi_backend import cffi_opcode
 from pypy.module._cffi_backend.ctypeobj import W_CType
 from pypy.module._cffi_backend.cdataobj import W_CData
 
@@ -30,9 +31,12 @@
 class FreeCtxObj(object):
     def __init__(self, ctxobj):
         self.ctxobj = ctxobj
+        self.free_mems = []       # filled from cdlopen.py
     @rgc.must_be_light_finalizer
     def __del__(self):
         parse_c_type.free_ctxobj(self.ctxobj)
+        for p in self.free_mems:
+            lltype.free(p, flavor='raw')
 
 
 class W_FFIObject(W_Root):
@@ -43,14 +47,36 @@
         self.space = space
         self.types_dict = {}
         self.ctxobj = parse_c_type.allocate_ctxobj(src_ctx)
+        self.is_static = bool(src_ctx)
+        self.is_nonempty = bool(src_ctx)
         self._finalizer = FreeCtxObj(self.ctxobj)
         if src_ctx:
             self.cached_types = [None] * parse_c_type.get_num_types(src_ctx)
         else:
             self.cached_types = None
         self.w_FFIError = get_ffi_error(space)
+        self.included_ffis = []        # list of W_FFIObject's included here
         self.included_libs = []        # list of W_LibObject's included here
 
+    def fetch_int_constant(self, name):
+        index = parse_c_type.search_in_globals(self.ctxobj.ctx, name)
+        if index >= 0:
+            g = self.ctxobj.ctx.c_globals[index]
+            op = realize_c_type.getop(g.c_type_op)
+            if (op == cffi_opcode.OP_CONSTANT_INT or
+                  op == cffi_opcode.OP_ENUM):
+                return realize_c_type.realize_global_int(self, g, index)
+            raise oefmt(self.w_FFIError,
+                        "function, global variable or non-integer constant "
+                        "'%s' must be fetched from its original 'lib' "
+                        "object", name)
+
+        for ffi1 in self.included_ffis:
+            w_result = ffi1.ffi_fetch_int_constant(name)
+            if w_result is not None:
+                return w_result
+        return None
+
     @jit.elidable_promote()
     def get_string_to_type(self, string, consider_fn_as_fnptr):
         x = self.types_dict[string]     # KeyError if not found
@@ -123,8 +149,21 @@
                     m1, s12, m2, s23, m3, w_x)
 
 
-    def descr_init(self):
-        pass       # if any argument is passed, gets a TypeError
+    @unwrap_spec(module_name=str, _version=int, _types=str)
+    def descr_init(self, module_name=None, _version=-1, _types=None,
+                   w__globals=None, w__struct_unions=None, w__enums=None,
+                   w__typenames=None, w__includes=None):
+        from pypy.module._cffi_backend import cdlopen
+        #
+        space = self.space
+        if self.is_nonempty:
+            raise oefmt(space.w_ValueError,
+                        "cannot call FFI.__init__() more than once")
+        self.is_nonempty = True
+        #
+        cdlopen.ffiobj_init(self, module_name, _version, _types,
+                            w__globals, w__struct_unions, w__enums,
+                            w__typenames, w__includes)
 
 
     doc_errno = "the value of 'errno' from/to the C calls"
@@ -316,6 +355,7 @@
             if add_paren:
                 result += ')'
             result += w_ctype.name[w_ctype.name_position:]
+        # Python 3: bytes -> unicode string
         return self.space.wrap(result)
 
 
@@ -437,6 +477,22 @@
         return self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CDATA)
 
 
+    @unwrap_spec(name=str)
+    def descr_integer_const(self, name):
+        """\
+Get the value of an integer constant.
+
+'ffi.integer_const(\"xxx\")' is equivalent to 'lib.xxx' if xxx names an
+integer constant.  The point of this function is limited to use cases
+where you have an 'ffi' object but not any associated 'lib' object."""
+        #
+        w_result = self.fetch_int_constant(name)
+        if w_result is None:
+            raise oefmt(self.space.w_AttributeError,
+                        "integer constant '%s' not found", name)
+        return w_result
+
+
 @jit.dont_look_inside
 def W_FFIObject___new__(space, w_subtype, __args__):
     r = space.allocate_instance(W_FFIObject, w_subtype)
@@ -487,6 +543,7 @@
         from_handle = interp2app(W_FFIObject.descr_from_handle),
         gc          = interp2app(W_FFIObject.descr_gc),
         getctype    = interp2app(W_FFIObject.descr_getctype),
+        integer_const = interp2app(W_FFIObject.descr_integer_const),
         new         = interp2app(W_FFIObject.descr_new),
         new_handle  = interp2app(W_FFIObject.descr_new_handle),
         offsetof    = interp2app(W_FFIObject.descr_offsetof),
diff --git a/pypy/module/_cffi_backend/lib_obj.py b/pypy/module/_cffi_backend/lib_obj.py
--- a/pypy/module/_cffi_backend/lib_obj.py
+++ b/pypy/module/_cffi_backend/lib_obj.py
@@ -120,6 +120,7 @@
                 fetch_funcptr = rffi.cast(
                     realize_c_type.FUNCPTR_FETCH_CHARP,
                     g.c_address)
+                xxxxxxxxxxxxxxxxxxxx
                 assert w_ct.size > 0
                 with lltype.scoped_alloc(rffi.CCHARP.TO, w_ct.size) as ptr:
                     fetch_funcptr(ptr)
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
@@ -24,6 +24,9 @@
                        ('address', rffi.VOIDP),
                        ('type_op', _CFFI_OPCODE_T),
                        ('size', rffi.SIZE_T))
+CDL_INTCONST_S = lltype.Struct('cdl_intconst_s',
+                       ('value', rffi.ULONGLONG),
+                       ('neg', rffi.INT))
 STRUCT_UNION_S = rffi.CStruct('_cffi_struct_union_s',
                        ('name', rffi.CCHARP),
                        ('type_index', rffi.INT),
@@ -68,6 +71,11 @@
                         ('error_location', rffi.SIZE_T),
                         ('error_message', rffi.CCHARP))
 
+GETCONST_S = rffi.CStruct('_cffi_getconst_s',
+                          ('value', rffi.ULONGLONG),
+                          ('ctx', PCTX),
+                          ('gindex', rffi.INT))
+
 ll_parse_c_type = llexternal('pypy_parse_c_type', [PINFO, rffi.CCHARP],
                              rffi.INT)
 ll_search_in_globals = llexternal('pypy_search_in_globals',
@@ -76,6 +84,9 @@
 ll_search_in_struct_unions = llexternal('pypy_search_in_struct_unions',
                                         [PCTX, rffi.CCHARP, rffi.SIZE_T],
                                         rffi.INT)
+ll_set_cdl_realize_global_int = llexternal('pypy_set_cdl_realize_global_int',
+                                           [lltype.Ptr(GLOBAL_S)],
+                                           lltype.Void)
 
 def parse_c_type(info, input):
     p_input = rffi.str2charp(input)
diff --git a/pypy/module/_cffi_backend/realize_c_type.py b/pypy/module/_cffi_backend/realize_c_type.py
--- a/pypy/module/_cffi_backend/realize_c_type.py
+++ b/pypy/module/_cffi_backend/realize_c_type.py
@@ -92,13 +92,16 @@
 
 
 FUNCPTR_FETCH_CHARP = lltype.Ptr(lltype.FuncType([rffi.CCHARP], lltype.Void))
-FUNCPTR_FETCH_LONGLONG = lltype.Ptr(lltype.FuncType([rffi.ULONGLONGP],
-                                                    rffi.INT))
-def realize_global_int(ffi, g):
+FUNCPTR_FETCH_LONGLONG = lltype.Ptr(lltype.FuncType(
+    [lltype.Ptr(parse_c_type.GETCONST_S)], rffi.INT))
+
+def realize_global_int(ffi, g, gindex):
     fetch_fnptr = rffi.cast(FUNCPTR_FETCH_LONGLONG, g.c_address)
-    with lltype.scoped_alloc(rffi.ULONGLONGP.TO, 1) as p_value:
+    with lltype.scoped_alloc(parse_c_type.GETCONST_S) as p_value:
+        p_value.c_ctx = ffi.ctxobj.ctx
+        rffi.setintfield(p_value, 'c_gindex', gindex)
         neg = fetch_fnptr(p_value)
-        value = p_value[0]
+        value = p_value.c_value
     neg = rffi.cast(lltype.Signed, neg)
 
     if neg == 0:     # positive
@@ -312,7 +315,7 @@
             assert getop(g.c_type_op) == cffi_opcode.OP_ENUM
             assert getarg(g.c_type_op) == -1
 
-            w_integer_value = realize_global_int(ffi, g)
+            w_integer_value = realize_global_int(ffi, g, gindex)
             enumvalues_w.append(w_integer_value)
 
             p = rffi.ptradd(p, j)
diff --git a/pypy/module/_cffi_backend/src/parse_c_type.c b/pypy/module/_cffi_backend/src/parse_c_type.c
--- a/pypy/module/_cffi_backend/src/parse_c_type.c
+++ b/pypy/module/_cffi_backend/src/parse_c_type.c
@@ -381,16 +381,22 @@
                     g = &tok->info->ctx->globals[gindex];
                     if (_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT_INT ||
                         _CFFI_GETOP(g->type_op) == _CFFI_OP_ENUM) {
-                        unsigned long long value;
-                        int neg = ((int(*)(unsigned long long*))g->address)
-                            (&value);
-                        if (!neg && value > MAX_SSIZE_T)
+                        int neg;
+                        struct _cffi_getconst_s gc;
+                        gc.ctx = tok->info->ctx;
+                        gc.gindex = gindex;
+                        neg = ((int(*)(struct _cffi_getconst_s*))g->address)
+                            (&gc);
+                        if (neg == 0 && gc.value > MAX_SSIZE_T)
                             return parse_error(tok,
                                                "integer constant too large");
-                        if (!neg || value == 0) {
-                            length = (size_t)value;
+                        if (neg == 0 || gc.value == 0) {
+                            length = (size_t)gc.value;
                             break;
                         }
+                        if (neg != 1)
+                            return parse_error(tok, "disagreement about"
+                                               " this constant's value");
                     }
                 }
                 /* fall-through to the default case */
@@ -763,3 +769,34 @@
         return parse_error(&token, "unexpected symbol");
     return result;
 }
+
+
+/************************************************************/
+/* extra from cdlopen.c                                     */
+
+typedef struct {
+    unsigned long long value;
+    int neg;
+} cdl_intconst_t;
+
+static int _cdl_realize_global_int(struct _cffi_getconst_s *gc)
+{
+    /* The 'address' field of 'struct _cffi_global_s' is set to point
+       to this function in case ffiobj_init() sees constant integers.
+       This fishes around after the 'ctx->globals' array, which is
+       initialized to contain another array, this time of
+       'cdl_intconst_t' structures.  We get the nth one and it tells
+       us what to return.
+    */
+    cdl_intconst_t *ic;
+    ic = (cdl_intconst_t *)(gc->ctx->globals + gc->ctx->num_globals);
+    ic += gc->gindex;
+    gc->value = ic->value;
+    return ic->neg;
+}
+
+RPY_EXTERN
+void pypy_set_cdl_realize_global_int(struct _cffi_global_s *target)
+{
+    target->address = (void *)_cdl_realize_global_int;
+}
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
@@ -161,4 +161,6 @@
 RPY_EXTERN int
 pypy_search_in_struct_unions(const struct _cffi_type_context_s *ctx,
                              const char *search, size_t search_len);
+RPY_EXTERN
+void pypy_set_cdl_realize_global_int(struct _cffi_global_s *target);
 #endif


More information about the pypy-commit mailing list