[pypy-commit] pypy default: Merged in Dodan/pypy_ctypes/pypy_ctypes_nosegfault_nofastpath (pull request #547)

cfbolz pypy.commits at gmail.com
Fri May 19 08:53:10 EDT 2017


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: 
Changeset: r91344:8b4ebb4d87c8
Date: 2017-05-19 12:52 +0000
http://bitbucket.org/pypy/pypy/changeset/8b4ebb4d87c8/

Log:	Merged in Dodan/pypy_ctypes/pypy_ctypes_nosegfault_nofastpath (pull
	request #547)

	fixed ctypes segfault by removing fastpath.

	Approved-by: Carl Friedrich Bolz <cfbolz at gmx.de> Approved-by:
	Antonio Cuni <anto.cuni+2 at gmail.com>

diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py
--- a/lib_pypy/_ctypes/function.py
+++ b/lib_pypy/_ctypes/function.py
@@ -1,4 +1,3 @@
-
 from _ctypes.basics import _CData, _CDataMeta, cdata_from_address
 from _ctypes.primitive import SimpleType, _SimpleCData
 from _ctypes.basics import ArgumentError, keepalive_key
@@ -9,13 +8,16 @@
 import sys
 import traceback
 
-try: from __pypy__ import builtinify
-except ImportError: builtinify = lambda f: f
+
+try:
+    from __pypy__ import builtinify
+except ImportError:
+    builtinify = lambda f: f
 
 # XXX this file needs huge refactoring I fear
 
-PARAMFLAG_FIN   = 0x1
-PARAMFLAG_FOUT  = 0x2
+PARAMFLAG_FIN = 0x1
+PARAMFLAG_FOUT = 0x2
 PARAMFLAG_FLCID = 0x4
 PARAMFLAG_COMBINED = PARAMFLAG_FIN | PARAMFLAG_FOUT | PARAMFLAG_FLCID
 
@@ -24,9 +26,9 @@
     PARAMFLAG_FIN,
     PARAMFLAG_FIN | PARAMFLAG_FOUT,
     PARAMFLAG_FIN | PARAMFLAG_FLCID
-    )
+)
 
-WIN64 = sys.platform == 'win32' and sys.maxint == 2**63 - 1
+WIN64 = sys.platform == 'win32' and sys.maxint == 2 ** 63 - 1
 
 
 def get_com_error(errcode, riid, pIunk):
@@ -35,6 +37,7 @@
     from _ctypes import COMError
     return COMError(errcode, None, None)
 
+
 @builtinify
 def call_function(func, args):
     "Only for debugging so far: So that we can call CFunction instances"
@@ -94,14 +97,9 @@
                         "item %d in _argtypes_ has no from_param method" % (
                             i + 1,))
             self._argtypes_ = list(argtypes)
-            self._check_argtypes_for_fastpath()
+
     argtypes = property(_getargtypes, _setargtypes)
 
-    def _check_argtypes_for_fastpath(self):
-        if all([hasattr(argtype, '_ffiargshape_') for argtype in self._argtypes_]):
-            fastpath_cls = make_fastpath_subclass(self.__class__)
-            fastpath_cls.enable_fastpath_maybe(self)
-
     def _getparamflags(self):
         return self._paramflags
 
@@ -126,27 +124,26 @@
                 raise TypeError(
                     "paramflags must be a sequence of (int [,string [,value]]) "
                     "tuples"
-                    )
+                )
             if not isinstance(flag, int):
                 raise TypeError(
                     "paramflags must be a sequence of (int [,string [,value]]) "
                     "tuples"
-                    )
+                )
             _flag = flag & PARAMFLAG_COMBINED
             if _flag == PARAMFLAG_FOUT:
                 typ = self._argtypes_[idx]
                 if getattr(typ, '_ffiargshape_', None) not in ('P', 'z', 'Z'):
                     raise TypeError(
                         "'out' parameter %d must be a pointer type, not %s"
-                        % (idx+1, type(typ).__name__)
-                        )
+                        % (idx + 1, type(typ).__name__)
+                    )
             elif _flag not in VALID_PARAMFLAGS:
                 raise TypeError("paramflag value %d not supported" % flag)
         self._paramflags = paramflags
 
     paramflags = property(_getparamflags, _setparamflags)
 
-
     def _getrestype(self):
         return self._restype_
 
@@ -156,7 +153,7 @@
             from ctypes import c_int
             restype = c_int
         if not (isinstance(restype, _CDataMeta) or restype is None or
-                callable(restype)):
+                    callable(restype)):
             raise TypeError("restype must be a type, a callable, or None")
         self._restype_ = restype
 
@@ -168,15 +165,18 @@
 
     def _geterrcheck(self):
         return getattr(self, '_errcheck_', None)
+
     def _seterrcheck(self, errcheck):
         if not callable(errcheck):
             raise TypeError("The errcheck attribute must be callable")
         self._errcheck_ = errcheck
+
     def _delerrcheck(self):
         try:
             del self._errcheck_
         except AttributeError:
             pass
+
     errcheck = property(_geterrcheck, _seterrcheck, _delerrcheck)
 
     def _ffishapes(self, args, restype):
@@ -188,7 +188,7 @@
                 raise TypeError("invalid result type for callback function")
             restype = restype._ffiargshape_
         else:
-            restype = 'O' # void
+            restype = 'O'  # void
         return argtypes, restype
 
     def _set_address(self, address):
@@ -201,7 +201,7 @@
 
     def __init__(self, *args):
         self.name = None
-        self._objects = {keepalive_key(0):self}
+        self._objects = {keepalive_key(0): self}
         self._needs_free = True
 
         # Empty function object -- this is needed for casts
@@ -222,10 +222,8 @@
             if self._argtypes_ is None:
                 self._argtypes_ = []
             self._ptr = self._getfuncptr_fromaddress(self._argtypes_, restype)
-            self._check_argtypes_for_fastpath()
             return
 
-
         # A callback into python
         if callable(argument) and not argsl:
             self.callable = argument
@@ -259,7 +257,7 @@
         if (sys.platform == 'win32' and isinstance(argument, (int, long))
             and argsl):
             ffiargs, ffires = self._ffishapes(self._argtypes_, self._restype_)
-            self._com_index =  argument + 0x1000
+            self._com_index = argument + 0x1000
             self.name = argsl.pop(0)
             if argsl:
                 self.paramflags = argsl.pop(0)
@@ -281,6 +279,7 @@
             except SystemExit as e:
                 handle_system_exit(e)
                 raise
+
         return f
 
     def __call__(self, *args, **kwargs):
@@ -317,7 +316,7 @@
             except:
                 exc_info = sys.exc_info()
                 traceback.print_tb(exc_info[2], file=sys.stderr)
-                print >>sys.stderr, "%s: %s" % (exc_info[0].__name__, exc_info[1])
+                print >> sys.stderr, "%s: %s" % (exc_info[0].__name__, exc_info[1])
                 return 0
             if self._restype_ is not None:
                 return res
@@ -328,7 +327,7 @@
             # really slow".  Now we don't worry that much about slowness
             # of ctypes, and it's strange to get warnings for perfectly-
             # legal code.
-            #warnings.warn('C function without declared arguments called',
+            # warnings.warn('C function without declared arguments called',
             #              RuntimeWarning, stacklevel=2)
             argtypes = []
 
@@ -337,7 +336,7 @@
             if not args:
                 raise ValueError(
                     "native COM method call without 'this' parameter"
-                    )
+                )
             thisvalue = args[0]
             thisarg = cast(thisvalue, POINTER(POINTER(c_void_p)))
             keepalives, newargs, argtypes, outargs, errcheckargs = (
@@ -366,7 +365,6 @@
         return tuple(outargs)
 
     def _call_funcptr(self, funcptr, *newargs):
-
         if self._flags_ & _rawffi.FUNCFLAG_USE_ERRNO:
             tmp = _rawffi.get_errno()
             _rawffi.set_errno(get_errno())
@@ -431,7 +429,7 @@
             ffiargs = [argtype.get_ffi_argtype() for argtype in argtypes]
             ffires = restype.get_ffi_argtype()
             return _ffi.FuncPtr.fromaddr(ptr, '', ffiargs, ffires, self._flags_)
-        
+
         cdll = self.dll._handle
         try:
             ffi_argtypes = [argtype.get_ffi_argtype() for argtype in argtypes]
@@ -450,7 +448,7 @@
             # funcname -> _funcname@<n>
             # where n is 0, 4, 8, 12, ..., 128
             for i in range(33):
-                mangled_name = "_%s@%d" % (self.name, i*4)
+                mangled_name = "_%s@%d" % (self.name, i * 4)
                 try:
                     return cdll.getfunc(mangled_name,
                                         ffi_argtypes, ffi_restype,
@@ -492,7 +490,7 @@
         for argtype, arg in zip(argtypes, args):
             param = argtype.from_param(arg)
             _type_ = getattr(argtype, '_type_', None)
-            if _type_ == 'P': # special-case for c_void_p
+            if _type_ == 'P':  # special-case for c_void_p
                 param = param._get_buffer_value()
             elif self._is_primitive(argtype):
                 param = param.value
@@ -668,69 +666,11 @@
             self._needs_free = False
 
 
-def make_fastpath_subclass(CFuncPtr):
-    if CFuncPtr._is_fastpath:
-        return CFuncPtr
-    #
-    try:
-        return make_fastpath_subclass.memo[CFuncPtr]
-    except KeyError:
-        pass
-
-    class CFuncPtrFast(CFuncPtr):
-
-        _is_fastpath = True
-        _slowpath_allowed = True # set to False by tests
-
-        @classmethod
-        def enable_fastpath_maybe(cls, obj):
-            if (obj.callable is None and
-                obj._com_index is None):
-                obj.__class__ = cls
-
-        def __rollback(self):
-            assert self._slowpath_allowed
-            self.__class__ = CFuncPtr
-
-        # disable the fast path if we reset argtypes
-        def _setargtypes(self, argtypes):
-            self.__rollback()
-            self._setargtypes(argtypes)
-        argtypes = property(CFuncPtr._getargtypes, _setargtypes)
-
-        def _setcallable(self, func):
-            self.__rollback()
-            self.callable = func
-        callable = property(lambda x: None, _setcallable)
-
-        def _setcom_index(self, idx):
-            self.__rollback()
-            self._com_index = idx
-        _com_index = property(lambda x: None, _setcom_index)
-
-        def __call__(self, *args):
-            thisarg = None
-            argtypes = self._argtypes_
-            restype = self._restype_
-            funcptr = self._getfuncptr(argtypes, restype, thisarg)
-            try:
-                result = self._call_funcptr(funcptr, *args)
-                result, _ = self._do_errcheck(result, args)
-            except (TypeError, ArgumentError, UnicodeDecodeError):
-                assert self._slowpath_allowed
-                return CFuncPtr.__call__(self, *args)
-            return result
-
-    make_fastpath_subclass.memo[CFuncPtr] = CFuncPtrFast
-    return CFuncPtrFast
-make_fastpath_subclass.memo = {}
-
-
 def handle_system_exit(e):
     # issue #1194: if we get SystemExit here, then exit the interpreter.
     # Highly obscure imho but some people seem to depend on it.
     if sys.flags.inspect:
-        return   # Don't exit if -i flag was given.
+        return  # Don't exit if -i flag was given.
     else:
         code = e.code
         if isinstance(code, int):
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/README b/pypy/module/test_lib_pypy/ctypes_tests/README
new file mode 100644
--- /dev/null
+++ b/pypy/module/test_lib_pypy/ctypes_tests/README
@@ -0,0 +1,8 @@
+-------Ctypes tests------
+
+Unlike the other tests in the PyPy sources, these tests are assumed to run after the translation is complete.
+Therefore, using the resulting binary, you can then call, for example:
+
+/path/to/your_modified_pypy_binary pytest.py pypy/module/test_lib_pypy/ctypes_tests/test_libc.py
+
+This also applies to any other test in ctypes_tests.
\ No newline at end of file
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_fastpath.py b/pypy/module/test_lib_pypy/ctypes_tests/test_fastpath.py
deleted file mode 100644
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_fastpath.py
+++ /dev/null
@@ -1,128 +0,0 @@
-from ctypes import CDLL, POINTER, pointer, c_byte, c_int, c_char_p, CFUNCTYPE, c_void_p, c_size_t
-import sys
-import py
-from support import BaseCTypesTestChecker
-
-class MyCDLL(CDLL):
-    def __getattr__(self, attr):
-        fn = self[attr] # this way it's not cached as an attribute
-        fn._slowpath_allowed = False
-        return fn
-
-def setup_module(mod):
-    import conftest
-    _ctypes_test = str(conftest.sofile)
-    mod.dll = MyCDLL(_ctypes_test)  # slowpath not allowed
-    mod.dll2 = CDLL(_ctypes_test)   # slowpath allowed
-
-
-class TestFastpath(BaseCTypesTestChecker):
-
-    def test_fastpath_forbidden(self):
-        def myfunc():
-            pass
-        #
-        tf_b = dll.tf_b
-        tf_b.restype = c_byte
-        #
-        # so far, it's still using the slowpath
-        assert not tf_b._is_fastpath
-        tf_b.callable = myfunc
-        tf_b.argtypes = (c_byte,)
-        # errcheck prevented the fastpath to kick in
-        assert not tf_b._is_fastpath
-        #
-        del tf_b.callable
-        tf_b.argtypes = (c_byte,) # try to re-enable the fastpath
-        assert tf_b._is_fastpath
-        #
-        assert not tf_b._slowpath_allowed
-        py.test.raises(AssertionError, "tf_b.callable = myfunc")
-        py.test.raises(AssertionError, "tf_b('aaa')") # force a TypeError
-
-    def test_simple_args(self):
-        tf_b = dll.tf_b
-        tf_b.restype = c_byte
-        tf_b.argtypes = (c_byte,)
-        assert tf_b(-126) == -42
-
-    def test_from_cfunctype(self):
-        from _ctypes import _memmove_addr
-        functype = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)
-        my_memmove = functype(_memmove_addr)
-        assert my_memmove._is_fastpath
-
-    def test_undeclared_restype(self):
-        # make sure we get a fresh function
-        try:
-            del dll.tf_i
-        except AttributeError:
-            pass
-        tf_i = dll.tf_i
-        assert not tf_i._is_fastpath
-        tf_i.argtypes = (c_int,)
-        assert tf_i._is_fastpath
-        assert tf_i(12) == 4
-
-    def test_pointer_args(self):
-        f = dll._testfunc_p_p
-        f.restype = POINTER(c_int)
-        f.argtypes = [POINTER(c_int)]
-        v = c_int(42)
-        result = f(pointer(v))
-        assert type(result) == POINTER(c_int)
-        assert result.contents.value == 42
-
-    def test_simple_pointer_args(self):
-        f = dll.my_strchr
-        f.argtypes = [c_char_p, c_int]
-        f.restype = c_char_p
-        mystr = c_char_p("abcd")
-        result = f(mystr, ord("b"))
-        assert result == "bcd"
-
-    def test_strings(self):
-        f = dll.my_strchr
-        f.argtypes = [c_char_p, c_int]
-        f.restype = c_char_p
-        result = f("abcd", ord("b"))
-        assert result == "bcd"
-
-    def test_errcheck(self):
-        def errcheck(result, func, args):
-            return 'hello'
-        tf_b = dll.tf_b
-        tf_b.restype = c_byte
-        tf_b.argtypes = (c_byte,)
-        tf_b.errcheck = errcheck
-        assert tf_b(-126) == 'hello'
-
-    def test_array_to_ptr(self):
-        ARRAY = c_int * 8
-        func = dll._testfunc_ai8
-        func.restype = POINTER(c_int)
-        func.argtypes = [ARRAY]
-        array = ARRAY(1, 2, 3, 4, 5, 6, 7, 8)
-        ptr = func(array)
-        assert ptr[0] == 1
-        assert ptr[7] == 8
-
-
-class TestFallbackToSlowpath(BaseCTypesTestChecker):
-
-    def test_argtypes_is_None(self):
-        tf_b = dll2.tf_b
-        tf_b.restype = c_byte
-        tf_b.argtypes = (c_char_p,)  # this is intentionally wrong
-        tf_b.argtypes = None # kill the fast path
-        assert not tf_b._is_fastpath
-        assert tf_b(-126) == -42
-
-    def test_callable_is_None(self):
-        tf_b = dll2.tf_b
-        tf_b.restype = c_byte
-        tf_b.argtypes = (c_byte,)
-        tf_b.callable = lambda x: x+1
-        assert not tf_b._is_fastpath
-        assert tf_b(-126) == -125
-        tf_b.callable = None
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
@@ -478,7 +478,7 @@
         raises(ArgumentError, lambda: callback((1, 2, 3, 4), POINT()))
 
     def test_argument_conversion_and_checks(self):
-        py.test.skip("XXX currently broken on PyPy, sorry")
+        #This test is designed to check for segfaults if the wrong type of argument is passed as parameter
         strlen = dll.my_strchr
         strlen.argtypes = [c_char_p, c_int]
         strlen.restype = c_char_p


More information about the pypy-commit mailing list