Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r84052:9f9c409ee27e Date: 2016-04-30 10:40 +0200 http://bitbucket.org/pypy/pypy/changeset/9f9c409ee27e/
Log: Test and fix for yet another very obscure misfeature of ctypes diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -1009,12 +1009,22 @@ container = _array_of_known_length(T.TO) container._storage = type(cobj)(cobj.contents) elif isinstance(T.TO, lltype.FuncType): + # cobj is a CFunctionType object. We naively think + # that it should be a function pointer. No no no. If + # it was read out of an array, say, then it is a *pointer* + # to a function pointer. In other words, the read doesn't + # read anything, it just takes the address of the function + # pointer inside the array. If later the array is modified + # or goes out of scope, then we crash. CTypes is fun. + # It works if we cast it now to an int and back. cobjkey = intmask(ctypes.cast(cobj, ctypes.c_void_p).value) if cobjkey in _int2obj: container = _int2obj[cobjkey] else: + name = getattr(cobj, '__name__', '?') + cobj = ctypes.cast(cobjkey, type(cobj)) _callable = get_ctypes_trampoline(T.TO, cobj) - return lltype.functionptr(T.TO, getattr(cobj, '__name__', '?'), + return lltype.functionptr(T.TO, name, _callable=_callable) elif isinstance(T.TO, lltype.OpaqueType): if T == llmemory.GCREF: diff --git a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py --- a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py @@ -1405,6 +1405,45 @@ a2 = ctypes2lltype(lltype.Ptr(A), lltype2ctypes(a)) assert a2._obj.getitem(0)._obj._parentstructure() is a2._obj + def test_array_of_function_pointers(self): + c_source = py.code.Source(r""" + #include "src/precommondefs.h" + #include <stdio.h> + + typedef int(*funcptr_t)(void); + static int forty_two(void) { return 42; } + static int forty_three(void) { return 43; } + static funcptr_t testarray[2]; + RPY_EXPORTED void runtest(void cb(funcptr_t *)) { + testarray[0] = &forty_two; + testarray[1] = &forty_three; + fprintf(stderr, "&forty_two = %p\n", testarray[0]); + fprintf(stderr, "&forty_three = %p\n", testarray[1]); + cb(testarray); + testarray[0] = 0; + testarray[1] = 0; + } + """) + eci = ExternalCompilationInfo(include_dirs=[cdir], + separate_module_sources=[c_source]) + + PtrF = lltype.Ptr(lltype.FuncType([], rffi.INT)) + ArrayPtrF = rffi.CArrayPtr(PtrF) + CALLBACK = rffi.CCallback([ArrayPtrF], lltype.Void) + + runtest = rffi.llexternal('runtest', [CALLBACK], lltype.Void, + compilation_info=eci) + seen = [] + + def callback(testarray): + seen.append(testarray[0]) # read a PtrF out of testarray + seen.append(testarray[1]) + + runtest(callback) + assert seen[0]() == 42 + assert seen[1]() == 43 + + class TestPlatform(object): def test_lib_on_libpaths(self): from rpython.translator.platform import platform _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit