[pypy-commit] pypy ffi-backend: Pass strings to C functions.

arigo noreply at buildbot.pypy.org
Thu Jul 5 14:30:33 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: ffi-backend
Changeset: r55927:4a01dd5334b0
Date: 2012-07-05 14:30 +0200
http://bitbucket.org/pypy/pypy/changeset/4a01dd5334b0/

Log:	Pass strings to C functions.

diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -2,11 +2,11 @@
 Function pointers.
 """
 
-from __future__ import with_statement
 from pypy.interpreter.error import OperationError, operationerrfmt
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rlib import jit, clibffi
 from pypy.rlib.objectmodel import we_are_translated, instantiate
+from pypy.rlib.objectmodel import keepalive_until_here
 
 from pypy.module._cffi_backend.ctypeobj import W_CType
 from pypy.module._cffi_backend.ctypeptr import W_CTypePtrBase
@@ -97,13 +97,34 @@
             cif_descr = self.cif_descr
 
         size = cif_descr.exchange_size
-        with lltype.scoped_alloc(rffi.CCHARP.TO, size) as buffer:
+        mustfree_count_plus_1 = 0
+        buffer = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
+        try:
             buffer_array = rffi.cast(rffi.VOIDPP, buffer)
             for i in range(len(args_w)):
                 data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
                 buffer_array[i] = data
                 w_obj = args_w[i]
                 argtype = self.fargs[i]
+                #
+                # special-case for strings.  xxx should avoid copying
+                if argtype.is_char_ptr_or_array:
+                    try:
+                        s = space.str_w(w_obj)
+                    except OperationError, e:
+                        if not e.match(space, space.w_TypeError):
+                            raise
+                    else:
+                        raw_string = rffi.str2charp(s)
+                        rffi.cast(rffi.CCHARPP, data)[0] = raw_string
+                        # set the "must free" flag to 1
+                        set_mustfree_flag(data, 1)
+                        mustfree_count_plus_1 = i + 1
+                        continue   # skip the convert_from_object()
+
+                    # set the "must free" flag to 0
+                    set_mustfree_flag(data, 0)
+                #
                 argtype.convert_from_object(data, w_obj)
             resultdata = rffi.ptradd(buffer, cif_descr.exchange_result)
 
@@ -116,8 +137,23 @@
                 w_res = space.w_None
             else:
                 w_res = self.ctitem.convert_to_object(resultdata)
+        finally:
+            for i in range(mustfree_count_plus_1):
+                argtype = self.fargs[i]
+                if argtype.is_char_ptr_or_array:
+                    data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
+                    if get_mustfree_flag(data):
+                        raw_string = rffi.cast(rffi.CCHARPP, data)[0]
+                        lltype.free(raw_string, flavor='raw')
+            lltype.free(buffer, flavor='raw')
         return w_res
 
+def get_mustfree_flag(data):
+    return ord(rffi.ptradd(data, -1)[0])
+
+def set_mustfree_flag(data, flag):
+    rffi.ptradd(data, -1)[0] = chr(flag)
+
 # ____________________________________________________________
 
 # The "cif" is a block of raw memory describing how to do a call via libffi.
@@ -187,9 +223,9 @@
         if ctype.ffi_type:   # common case: the ffi_type was already computed
             return ctype.ffi_type
 
+        space = self.space
         size = ctype.size
         if size < 0:
-            space = self.space
             raise operationerrfmt(space.w_TypeError,
                                   "ctype '%s' has incomplete type",
                                   ctype.name)
@@ -258,7 +294,6 @@
             if   size == 4: return _settype(ctype, clibffi.ffi_type_float)
             elif size == 8: return _settype(ctype, clibffi.ffi_type_double)
 
-        space = self.space
         raise operationerrfmt(space.w_NotImplementedError,
                               "ctype '%s' (size %d) not supported as argument"
                               " or return value",
@@ -302,6 +337,8 @@
 
         # loop over args
         for i, farg in enumerate(self.fargs):
+            if farg.is_char_ptr_or_array:
+                exchange_offset += 1   # for the "must free" flag
             exchange_offset = self.align_arg(exchange_offset)
             cif_descr.exchange_args[i] = exchange_offset
             exchange_offset += rffi.getintfield(self.atypes[i], 'c_size')
@@ -312,7 +349,8 @@
 
     @jit.dont_look_inside
     def rawallocate(self, ctypefunc):
-        self.space = ctypefunc.space
+        space = ctypefunc.space
+        self.space = space
 
         # compute the total size needed in the CIF_DESCRIPTION buffer
         self.nb_bytes = 0
@@ -346,6 +384,5 @@
                                      len(self.fargs),
                                      self.rtype, self.atypes)
         if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK:
-            space = self.space
             raise OperationError(space.w_SystemError,
                 space.wrap("libffi failed to build this function type"))
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
--- a/pypy/module/_cffi_backend/ctypeobj.py
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -12,6 +12,7 @@
 class W_CType(Wrappable):
     #_immutable_ = True    XXX newtype.complete_struct_or_union()?
     cast_anything = False
+    is_char_ptr_or_array = False
 
     def __init__(self, space, size, name, name_position):
         self.space = space
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -23,6 +23,7 @@
         #  - for functions, it is the return type
         self.ctitem = ctitem
         self.can_cast_anything = could_cast_anything and ctitem.cast_anything
+        self.is_char_ptr_or_array = isinstance(ctitem, W_CTypePrimitiveChar)
 
 
 class W_CTypePtrBase(W_CTypePtrOrArray):


More information about the pypy-commit mailing list