Author: Armin Rigo <[email protected]>
Branch: cpyext-avoid-roundtrip
Changeset: r92655:6d880cc2b4d6
Date: 2017-10-08 18:52 +0200
http://bitbucket.org/pypy/pypy/changeset/6d880cc2b4d6/

Log:    (antocuni, arigo)

        Hack around: add the @c_only decorator that makes it possible to
        write a function in RPython that is callable (only) from C, with no
        magic.

        Use it for free(): we want it to occur in RPython because of
        lltype.malloc(), and the tests are not happy because ll2ctypes never
        sees the free.

diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -255,14 +255,69 @@
 
 cpyext_namespace = NameManager('cpyext_')
 
-class ApiFunction(object):
-    def __init__(self, argtypes, restype, callable, error=CANNOT_FAIL,
-                 c_name=None, cdecl=None, gil=None,
-                 result_borrowed=False, result_is_ll=False):
+class BaseApiFunction(object):
+    def __init__(self, argtypes, restype, callable):
         self.argtypes = argtypes
         self.restype = restype
         self.functype = lltype.Ptr(lltype.FuncType(argtypes, restype))
         self.callable = callable
+        self.cdecl = None    # default
+
+    def get_api_decl(self, name, c_writer):
+        restype = self.get_c_restype(c_writer)
+        args = self.get_c_args(c_writer)
+        res = self.API_VISIBILITY % (restype,)
+        return "{res} {name}({args});".format(**locals())
+
+    def get_c_restype(self, c_writer):
+        if self.cdecl:
+            return self.cdecl.tp.result.get_c_name()
+        return c_writer.gettype(self.restype).replace('@', '').strip()
+
+    def get_c_args(self, c_writer):
+        if self.cdecl:
+            args = [tp.get_c_name('arg%d' % i) for i, tp in
+                enumerate(self.cdecl.tp.args)]
+            return ', '.join(args) or "void"
+        args = []
+        for i, argtype in enumerate(self.argtypes):
+            if argtype is CONST_STRING:
+                arg = 'const char *@'
+            elif argtype is CONST_STRINGP:
+                arg = 'const char **@'
+            elif argtype is CONST_WSTRING:
+                arg = 'const wchar_t *@'
+            else:
+                arg = c_writer.gettype(argtype)
+            arg = arg.replace('@', 'arg%d' % (i,)).strip()
+            args.append(arg)
+        args = ', '.join(args) or "void"
+        return args
+
+    def get_ptr_decl(self, name, c_writer):
+        restype = self.get_c_restype(c_writer)
+        args = self.get_c_args(c_writer)
+        return "{restype} (*{name})({args});".format(**locals())
+
+    def get_ctypes_impl(self, name, c_writer):
+        restype = self.get_c_restype(c_writer)
+        args = self.get_c_args(c_writer)
+        callargs = ', '.join('arg%d' % (i,)
+                            for i in range(len(self.argtypes)))
+        if self.restype is lltype.Void:
+            body = "{ _pypyAPI.%s(%s); }" % (name, callargs)
+        else:
+            body = "{ return _pypyAPI.%s(%s); }" % (name, callargs)
+        return '%s %s(%s)\n%s' % (restype, name, args, body)
+
+
+class ApiFunction(BaseApiFunction):
+    API_VISIBILITY = "PyAPI_FUNC(%s)"
+
+    def __init__(self, argtypes, restype, callable, error=CANNOT_FAIL,
+                 c_name=None, cdecl=None, gil=None,
+                 result_borrowed=False, result_is_ll=False):
+        BaseApiFunction.__init__(self, argtypes, restype, callable)
         self.error_value = error
         self.c_name = c_name
         self.cdecl = cdecl
@@ -383,52 +438,6 @@
                 keepalive_until_here(*keepalives)
         return unwrapper
 
-    def get_c_restype(self, c_writer):
-        if self.cdecl:
-            return self.cdecl.tp.result.get_c_name()
-        return c_writer.gettype(self.restype).replace('@', '').strip()
-
-    def get_c_args(self, c_writer):
-        if self.cdecl:
-            args = [tp.get_c_name('arg%d' % i) for i, tp in
-                enumerate(self.cdecl.tp.args)]
-            return ', '.join(args) or "void"
-        args = []
-        for i, argtype in enumerate(self.argtypes):
-            if argtype is CONST_STRING:
-                arg = 'const char *@'
-            elif argtype is CONST_STRINGP:
-                arg = 'const char **@'
-            elif argtype is CONST_WSTRING:
-                arg = 'const wchar_t *@'
-            else:
-                arg = c_writer.gettype(argtype)
-            arg = arg.replace('@', 'arg%d' % (i,)).strip()
-            args.append(arg)
-        args = ', '.join(args) or "void"
-        return args
-
-    def get_api_decl(self, name, c_writer):
-        restype = self.get_c_restype(c_writer)
-        args = self.get_c_args(c_writer)
-        return "PyAPI_FUNC({restype}) {name}({args});".format(**locals())
-
-    def get_ptr_decl(self, name, c_writer):
-        restype = self.get_c_restype(c_writer)
-        args = self.get_c_args(c_writer)
-        return "{restype} (*{name})({args});".format(**locals())
-
-    def get_ctypes_impl(self, name, c_writer):
-        restype = self.get_c_restype(c_writer)
-        args = self.get_c_args(c_writer)
-        callargs = ', '.join('arg%d' % (i,)
-                            for i in range(len(self.argtypes)))
-        if self.restype is lltype.Void:
-            body = "{ _pypyAPI.%s(%s); }" % (name, callargs)
-        else:
-            body = "{ return _pypyAPI.%s(%s); }" % (name, callargs)
-        return '%s %s(%s)\n%s' % (restype, name, args, body)
-
 
 DEFAULT_HEADER = 'pypy_decl.h'
 def cpython_api(argtypes, restype, error=_NOT_SPECIFIED, header=DEFAULT_HEADER,
@@ -466,6 +475,30 @@
         return unwrapper
     return decorate
 
+class COnlyApiFunction(BaseApiFunction):
+    API_VISIBILITY = "extern %s"
+
+    def __init__(self, *args, **kwds):
+        BaseApiFunction.__init__(self, *args, **kwds)
+        #
+        def get_llhelper(space):
+            return llhelper(self.functype, self.callable)
+        self.get_llhelper = get_llhelper
+
+    def __call__(self, *args):
+        raise TypeError("the function %s should not be directly "
+                        "called from RPython, but only from C" % (self.func,))
+
+def c_only(argtypes, restype):
+    def decorate(func):
+        header = DEFAULT_HEADER
+        if func.__name__ in FUNCTIONS_BY_HEADER[header]:
+            raise ValueError("%s already registered" % func.__name__)
+        api_function = COnlyApiFunction(argtypes, restype, func)
+        FUNCTIONS_BY_HEADER[header][func.__name__] = api_function
+        return api_function
+    return decorate
+
 def api_func_from_cdef(func, cdef, cts,
         error=_NOT_SPECIFIED, header=DEFAULT_HEADER,
         result_is_ll=False):
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -3,7 +3,7 @@
     cpython_api, generic_cpy_call, CANNOT_FAIL, Py_ssize_t, Py_ssize_tP,
     PyVarObject, size_t, slot_function,
     Py_TPFLAGS_HEAPTYPE, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT,
-    Py_GE, CONST_STRING, FILEP, fwrite)
+    Py_GE, CONST_STRING, FILEP, fwrite, c_only)
 from pypy.module.cpyext.pyobject import (
     PyObject, PyObjectP, from_ref, Py_IncRef, Py_DecRef,
     get_typedescr)
@@ -32,6 +32,10 @@
     # XXX FIXME
     return realloc(ptr, size)
 
+@c_only([rffi.VOIDP], lltype.Void)
+def _PyPy_Free(ptr):
+    lltype.free(ptr, flavor='raw')
+
 @cpython_api([PyTypeObjectPtr], PyObject, result_is_ll=True)
 def _PyObject_New(space, type):
     return _PyObject_NewVar(space, type, 0)
diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c
--- a/pypy/module/cpyext/src/object.c
+++ b/pypy/module/cpyext/src/object.c
@@ -36,16 +36,18 @@
         Py_DECREF(pto);
 }
 
+extern void _PyPy_Free(void *ptr);
+
 void
 PyObject_Free(void *obj)
 {
-    free(obj);
+    _PyPy_Free(obj);
 }
 
 void
 PyObject_GC_Del(void *obj)
 {
-    free(obj);
+    _PyPy_Free(obj);
 }
 
 PyObject *
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to