PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rlib/test/test_clibffi.py

https://bitbucket.org/pypy/pypy/
Python | 443 lines | 393 code | 37 blank | 13 comment | 15 complexity | 514dc09d0f26b4a619eb00b8bfb1291b MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """ Tests of libffi wrapper
  2. """
  3. from rpython.translator.c.test.test_genc import compile
  4. from rpython.translator import cdir
  5. from rpython.rlib.clibffi import *
  6. from rpython.rlib.objectmodel import keepalive_until_here
  7. from rpython.rtyper.lltypesystem.ll2ctypes import ALLOCATED
  8. from rpython.rtyper.lltypesystem import rffi, lltype
  9. import py
  10. import sys
  11. import time
  12. def get_libm_name(platform):
  13. if platform == 'win32':
  14. return 'msvcrt.dll'
  15. elif platform == "darwin":
  16. return 'libm.dylib'
  17. else:
  18. return 'libm.so'
  19. class BaseFfiTest(object):
  20. CDLL = None # overridden by subclasses
  21. @classmethod
  22. def setup_class(cls):
  23. for name in type_names:
  24. # XXX force this to be seen by ll2ctypes
  25. # so that ALLOCATED.clear() clears it
  26. ffistruct = globals()[name]
  27. rffi.cast(rffi.VOIDP, ffistruct)
  28. def setup_method(self, meth):
  29. ALLOCATED.clear()
  30. def get_libc(self):
  31. return self.CDLL(get_libc_name())
  32. def get_libm(self):
  33. return self.CDLL(get_libm_name(sys.platform))
  34. class TestCLibffi(BaseFfiTest):
  35. CDLL = CDLL
  36. def test_library_open(self):
  37. lib = self.get_libc()
  38. del lib
  39. assert not ALLOCATED
  40. def test_library_get_func(self):
  41. lib = self.get_libc()
  42. ptr = lib.getpointer('fopen', [], ffi_type_void)
  43. py.test.raises(KeyError, lib.getpointer, 'xxxxxxxxxxxxxxx', [], ffi_type_void)
  44. del ptr
  45. del lib
  46. assert not ALLOCATED
  47. def test_library_func_call(self):
  48. lib = self.get_libc()
  49. ptr = lib.getpointer('rand', [], ffi_type_sint)
  50. zeroes = 0
  51. first = ptr.call(rffi.INT)
  52. for i in range(100):
  53. res = ptr.call(rffi.INT)
  54. if res == first:
  55. zeroes += 1
  56. assert zeroes < 90
  57. # not very hard check, but something :]
  58. del ptr
  59. del lib
  60. assert not ALLOCATED
  61. def test_call_args(self):
  62. libm = self.get_libm()
  63. pow = libm.getpointer('pow', [ffi_type_double, ffi_type_double],
  64. ffi_type_double)
  65. pow.push_arg(2.0)
  66. pow.push_arg(2.0)
  67. res = pow.call(rffi.DOUBLE)
  68. assert res == 4.0
  69. pow.push_arg(3.0)
  70. pow.push_arg(3.0)
  71. res = pow.call(rffi.DOUBLE)
  72. assert res == 27.0
  73. del pow
  74. del libm
  75. assert not ALLOCATED
  76. def test_wrong_args(self):
  77. libc = self.get_libc()
  78. # XXX assume time_t is long
  79. ulong = cast_type_to_ffitype(rffi.ULONG)
  80. ctime = libc.getpointer('fopen', [ffi_type_pointer], ulong)
  81. x = lltype.malloc(lltype.GcStruct('xxx'))
  82. y = lltype.malloc(lltype.GcArray(rffi.LONG), 3)
  83. z = lltype.malloc(lltype.Array(rffi.LONG), 4, flavor='raw')
  84. py.test.raises(ValueError, "ctime.push_arg(x)")
  85. py.test.raises(ValueError, "ctime.push_arg(y)")
  86. py.test.raises(ValueError, "ctime.push_arg(z)")
  87. del ctime
  88. del libc
  89. lltype.free(z, flavor='raw')
  90. # allocation check makes no sense, since we've got GcStructs around
  91. def test_unichar(self):
  92. from rpython.rlib.runicode import MAXUNICODE
  93. wchar = cast_type_to_ffitype(lltype.UniChar)
  94. if MAXUNICODE > 65535:
  95. assert wchar is ffi_type_uint32
  96. else:
  97. assert wchar is ffi_type_uint16
  98. def test_call_time(self):
  99. libc = self.get_libc()
  100. # XXX assume time_t is long
  101. ulong = cast_type_to_ffitype(rffi.ULONG)
  102. try:
  103. ctime = libc.getpointer('time', [ffi_type_pointer], ulong)
  104. except KeyError:
  105. # This function is named differently since msvcr80
  106. ctime = libc.getpointer('_time32', [ffi_type_pointer], ulong)
  107. ctime.push_arg(lltype.nullptr(rffi.CArray(rffi.LONG)))
  108. t0 = ctime.call(rffi.LONG)
  109. time.sleep(2)
  110. ctime.push_arg(lltype.nullptr(rffi.CArray(rffi.LONG)))
  111. t1 = ctime.call(rffi.LONG)
  112. assert t1 > t0
  113. l_t = lltype.malloc(rffi.CArray(rffi.LONG), 1, flavor='raw')
  114. ctime.push_arg(l_t)
  115. t1 = ctime.call(rffi.LONG)
  116. assert l_t[0] == t1
  117. lltype.free(l_t, flavor='raw')
  118. del ctime
  119. del libc
  120. assert not ALLOCATED
  121. def test_closure_heap(self):
  122. ch = ClosureHeap()
  123. assert not ch.free_list
  124. a = ch.alloc()
  125. assert ch.free_list
  126. b = ch.alloc()
  127. chunks = [a, b]
  128. p = ch.free_list
  129. while p:
  130. chunks.append(p)
  131. p = rffi.cast(rffi.VOIDPP, p)[0]
  132. closure_size = rffi.sizeof(FFI_CLOSUREP.TO)
  133. assert len(chunks) == CHUNK//closure_size
  134. for i in range(len(chunks) -1 ):
  135. s = rffi.cast(rffi.UINT, chunks[i+1])
  136. e = rffi.cast(rffi.UINT, chunks[i])
  137. assert (e-s) >= rffi.sizeof(FFI_CLOSUREP.TO)
  138. ch.free(a)
  139. assert ch.free_list == rffi.cast(rffi.VOIDP, a)
  140. snd = rffi.cast(rffi.VOIDPP, a)[0]
  141. assert snd == chunks[2]
  142. ch.free(b)
  143. assert ch.free_list == rffi.cast(rffi.VOIDP, b)
  144. snd = rffi.cast(rffi.VOIDPP, b)[0]
  145. assert snd == rffi.cast(rffi.VOIDP, a)
  146. def test_callback(self):
  147. slong = cast_type_to_ffitype(rffi.LONG)
  148. libc = self.get_libc()
  149. qsort = libc.getpointer('qsort', [ffi_type_pointer, slong,
  150. slong, ffi_type_pointer],
  151. ffi_type_void)
  152. def callback(ll_args, ll_res, stuff):
  153. p_a1 = rffi.cast(rffi.VOIDPP, ll_args[0])[0]
  154. p_a2 = rffi.cast(rffi.VOIDPP, ll_args[1])[0]
  155. a1 = rffi.cast(rffi.INTP, p_a1)[0]
  156. a2 = rffi.cast(rffi.INTP, p_a2)[0]
  157. res = rffi.cast(rffi.SIGNEDP, ll_res)
  158. # must store a full ffi arg!
  159. if a1 > a2:
  160. res[0] = 1
  161. else:
  162. res[0] = -1
  163. ptr = CallbackFuncPtr([ffi_type_pointer, ffi_type_pointer],
  164. ffi_type_sint, callback)
  165. TP = rffi.CArray(rffi.INT)
  166. to_sort = lltype.malloc(TP, 4, flavor='raw')
  167. to_sort[0] = rffi.cast(rffi.INT, 4)
  168. to_sort[1] = rffi.cast(rffi.INT, 3)
  169. to_sort[2] = rffi.cast(rffi.INT, 1)
  170. to_sort[3] = rffi.cast(rffi.INT, 2)
  171. qsort.push_arg(rffi.cast(rffi.VOIDP, to_sort))
  172. qsort.push_arg(rffi.sizeof(rffi.INT))
  173. qsort.push_arg(4)
  174. qsort.push_arg(ptr.ll_closure)
  175. qsort.call(lltype.Void)
  176. assert ([rffi.cast(lltype.Signed, to_sort[i]) for i in range(4)] ==
  177. [1,2,3,4])
  178. lltype.free(to_sort, flavor='raw')
  179. keepalive_until_here(ptr) # <= this test is not translated, but don't
  180. # forget this in code that is meant to be
  181. def test_compile(self):
  182. import py
  183. py.test.skip("Segfaulting test, skip")
  184. # XXX cannot run it on top of llinterp, some problems
  185. # with pointer casts
  186. def f(x, y):
  187. libm = self.get_libm()
  188. c_pow = libm.getpointer('pow', [ffi_type_double, ffi_type_double], ffi_type_double)
  189. c_pow.push_arg(x)
  190. c_pow.push_arg(y)
  191. res = c_pow.call(rffi.DOUBLE)
  192. return res
  193. fn = compile(f, [float, float])
  194. res = fn(2.0, 4.0)
  195. assert res == 16.0
  196. def test_rawfuncptr(self):
  197. libm = self.get_libm()
  198. pow = libm.getrawpointer('pow', [ffi_type_double, ffi_type_double],
  199. ffi_type_double)
  200. buffer = lltype.malloc(rffi.DOUBLEP.TO, 3, flavor='raw')
  201. buffer[0] = 2.0
  202. buffer[1] = 3.0
  203. buffer[2] = 43.5
  204. pow.call([rffi.cast(rffi.VOIDP, buffer),
  205. rffi.cast(rffi.VOIDP, rffi.ptradd(buffer, 1))],
  206. rffi.cast(rffi.VOIDP, rffi.ptradd(buffer, 2)))
  207. assert buffer[2] == 8.0
  208. lltype.free(buffer, flavor='raw')
  209. del pow
  210. del libm
  211. assert not ALLOCATED
  212. def test_make_struct_ffitype_e(self):
  213. tpe = make_struct_ffitype_e(16, 4, [ffi_type_pointer, ffi_type_uchar])
  214. assert tpe.ffistruct.c_type == FFI_TYPE_STRUCT
  215. assert tpe.ffistruct.c_size == 16
  216. assert tpe.ffistruct.c_alignment == 4
  217. assert tpe.ffistruct.c_elements[0] == ffi_type_pointer
  218. assert tpe.ffistruct.c_elements[1] == ffi_type_uchar
  219. assert not tpe.ffistruct.c_elements[2]
  220. lltype.free(tpe, flavor='raw')
  221. def test_nested_struct_elements(self):
  222. tpe2 = make_struct_ffitype_e(16, 4, [ffi_type_pointer, ffi_type_uchar])
  223. tp2 = tpe2.ffistruct
  224. tpe = make_struct_ffitype_e(32, 4, [tp2, ffi_type_schar])
  225. assert tpe.ffistruct.c_elements[0] == tp2
  226. assert tpe.ffistruct.c_elements[1] == ffi_type_schar
  227. assert not tpe.ffistruct.c_elements[2]
  228. lltype.free(tpe, flavor='raw')
  229. lltype.free(tpe2, flavor='raw')
  230. def test_struct_by_val(self):
  231. from rpython.translator.tool.cbuild import ExternalCompilationInfo
  232. from rpython.translator.platform import platform
  233. from rpython.tool.udir import udir
  234. c_file = udir.ensure("test_libffi", dir=1).join("xlib.c")
  235. c_file.write(py.code.Source('''
  236. #include "src/precommondefs.h"
  237. #include <stdlib.h>
  238. #include <stdio.h>
  239. struct x_y {
  240. long x;
  241. long y;
  242. };
  243. RPY_EXPORTED
  244. long sum_x_y(struct x_y s) {
  245. return s.x + s.y;
  246. }
  247. long sum_x_y_p(struct x_y *p) {
  248. return p->x + p->y;
  249. }
  250. '''))
  251. eci = ExternalCompilationInfo(include_dirs=[cdir])
  252. lib_name = str(platform.compile([c_file], eci, 'x', standalone=False))
  253. lib = CDLL(lib_name)
  254. slong = cast_type_to_ffitype(rffi.LONG)
  255. size = slong.c_size*2
  256. alignment = slong.c_alignment
  257. tpe = make_struct_ffitype_e(size, alignment, [slong, slong])
  258. sum_x_y = lib.getrawpointer('sum_x_y', [tpe.ffistruct], slong)
  259. buffer = lltype.malloc(rffi.LONGP.TO, 3, flavor='raw')
  260. buffer[0] = 200
  261. buffer[1] = 220
  262. buffer[2] = 666
  263. sum_x_y.call([rffi.cast(rffi.VOIDP, buffer)],
  264. rffi.cast(rffi.VOIDP, rffi.ptradd(buffer, 2)))
  265. assert buffer[2] == 420
  266. lltype.free(buffer, flavor='raw')
  267. del sum_x_y
  268. lltype.free(tpe, flavor='raw')
  269. del lib
  270. assert not ALLOCATED
  271. def test_ret_struct_val(self):
  272. from rpython.translator.tool.cbuild import ExternalCompilationInfo
  273. from rpython.translator.platform import platform
  274. from rpython.tool.udir import udir
  275. c_file = udir.ensure("test_libffi", dir=1).join("xlib.c")
  276. c_file.write(py.code.Source('''
  277. #include "src/precommondefs.h"
  278. #include <stdlib.h>
  279. #include <stdio.h>
  280. struct s2h {
  281. short x;
  282. short y;
  283. };
  284. RPY_EXPORTED
  285. struct s2h give(short x, short y) {
  286. struct s2h out;
  287. out.x = x;
  288. out.y = y;
  289. return out;
  290. }
  291. RPY_EXPORTED
  292. struct s2h perturb(struct s2h inp) {
  293. inp.x *= 2;
  294. inp.y *= 3;
  295. return inp;
  296. }
  297. '''))
  298. eci = ExternalCompilationInfo(include_dirs=[cdir])
  299. lib_name = str(platform.compile([c_file], eci, 'x', standalone=False))
  300. lib = CDLL(lib_name)
  301. size = ffi_type_sshort.c_size*2
  302. alignment = ffi_type_sshort.c_alignment
  303. tpe = make_struct_ffitype_e(size, alignment, [ffi_type_sshort]*2)
  304. give = lib.getrawpointer('give', [ffi_type_sshort, ffi_type_sshort],
  305. tpe.ffistruct)
  306. inbuffer = lltype.malloc(rffi.SHORTP.TO, 2, flavor='raw')
  307. inbuffer[0] = rffi.cast(rffi.SHORT, 40)
  308. inbuffer[1] = rffi.cast(rffi.SHORT, 72)
  309. outbuffer = lltype.malloc(rffi.SHORTP.TO, 2, flavor='raw')
  310. give.call([rffi.cast(rffi.VOIDP, inbuffer),
  311. rffi.cast(rffi.VOIDP, rffi.ptradd(inbuffer, 1))],
  312. rffi.cast(rffi.VOIDP, outbuffer))
  313. assert outbuffer[0] == 40
  314. assert outbuffer[1] == 72
  315. perturb = lib.getrawpointer('perturb', [tpe.ffistruct], tpe.ffistruct)
  316. inbuffer[0] = rffi.cast(rffi.SHORT, 7)
  317. inbuffer[1] = rffi.cast(rffi.SHORT, 11)
  318. perturb.call([rffi.cast(rffi.VOIDP, inbuffer)],
  319. rffi.cast(rffi.VOIDP, outbuffer))
  320. assert inbuffer[0] == 7
  321. assert inbuffer[1] == 11
  322. assert outbuffer[0] == 14
  323. assert outbuffer[1] == 33
  324. lltype.free(outbuffer, flavor='raw')
  325. lltype.free(inbuffer, flavor='raw')
  326. del give
  327. del perturb
  328. lltype.free(tpe, flavor='raw')
  329. del lib
  330. assert not ALLOCATED
  331. def test_cdll_life_time(self):
  332. from rpython.translator.tool.cbuild import ExternalCompilationInfo
  333. from rpython.translator.platform import platform
  334. from rpython.tool.udir import udir
  335. c_file = udir.ensure("test_libffi", dir=1).join("xlib.c")
  336. c_file.write(py.code.Source('''
  337. #include "src/precommondefs.h"
  338. RPY_EXPORTED
  339. long fun(long i) {
  340. return i + 42;
  341. }
  342. '''))
  343. eci = ExternalCompilationInfo(include_dirs=[cdir])
  344. lib_name = str(platform.compile([c_file], eci, 'x', standalone=False))
  345. lib = CDLL(lib_name)
  346. slong = cast_type_to_ffitype(rffi.LONG)
  347. fun = lib.getrawpointer('fun', [slong], slong)
  348. del lib # already delete here
  349. buffer = lltype.malloc(rffi.LONGP.TO, 2, flavor='raw')
  350. buffer[0] = 200
  351. buffer[1] = -1
  352. fun.call([rffi.cast(rffi.VOIDP, buffer)],
  353. rffi.cast(rffi.VOIDP, rffi.ptradd(buffer, 1)))
  354. assert buffer[1] == 242
  355. lltype.free(buffer, flavor='raw')
  356. del fun
  357. assert not ALLOCATED
  358. class TestWin32Handles(BaseFfiTest):
  359. def setup_class(cls):
  360. if sys.platform != 'win32':
  361. py.test.skip("Handle to libc library, Win-only test")
  362. BaseFfiTest.setup_class()
  363. def test_get_libc_handle(self):
  364. handle = get_libc_handle()
  365. print get_libc_name()
  366. print dir(handle)
  367. addr = rffi.cast(rffi.INT, handle)
  368. assert addr != 0
  369. assert addr % 0x1000 == 0