PageRenderTime 77ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/rlib/test/test_libffi.py

http://github.com/pypy/pypy
Python | 539 lines | 489 code | 16 blank | 34 comment | 2 complexity | c2c94a55e91d8754cfb04229537745a5 MD5 | raw file
  1. import sys
  2. import py
  3. from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
  4. from pypy.rlib.test.test_clibffi import BaseFfiTest, get_libm_name, make_struct_ffitype_e
  5. from pypy.rpython.lltypesystem import rffi, lltype
  6. from pypy.rpython.lltypesystem.ll2ctypes import ALLOCATED
  7. from pypy.rlib.libffi import (CDLL, Func, get_libc_name, ArgChain, types,
  8. IS_32_BIT, array_getitem, array_setitem)
  9. from pypy.rlib.libffi import (struct_getfield_int, struct_setfield_int,
  10. struct_getfield_longlong, struct_setfield_longlong,
  11. struct_getfield_float, struct_setfield_float,
  12. struct_getfield_singlefloat, struct_setfield_singlefloat)
  13. class TestLibffiMisc(BaseFfiTest):
  14. CDLL = CDLL
  15. def test_argchain(self):
  16. chain = ArgChain()
  17. assert chain.numargs == 0
  18. chain2 = chain.arg(42)
  19. assert chain2 is chain
  20. assert chain.numargs == 1
  21. intarg = chain.first
  22. assert chain.last is intarg
  23. assert intarg.intval == 42
  24. chain.arg(123.45)
  25. assert chain.numargs == 2
  26. assert chain.first is intarg
  27. assert intarg.next is chain.last
  28. floatarg = intarg.next
  29. assert floatarg.floatval == 123.45
  30. def test_wrong_args(self):
  31. # so far the test passes but for the wrong reason :-), i.e. because
  32. # .arg() only supports integers and floats
  33. chain = ArgChain()
  34. x = lltype.malloc(lltype.GcStruct('xxx'))
  35. y = lltype.malloc(lltype.GcArray(rffi.SIGNED), 3)
  36. z = lltype.malloc(lltype.Array(rffi.SIGNED), 4, flavor='raw')
  37. py.test.raises(TypeError, "chain.arg(x)")
  38. py.test.raises(TypeError, "chain.arg(y)")
  39. py.test.raises(TypeError, "chain.arg(z)")
  40. lltype.free(z, flavor='raw')
  41. def test_library_open(self):
  42. lib = self.get_libc()
  43. del lib
  44. assert not ALLOCATED
  45. def test_library_get_func(self):
  46. lib = self.get_libc()
  47. ptr = lib.getpointer('fopen', [], types.void)
  48. py.test.raises(KeyError, lib.getpointer, 'xxxxxxxxxxxxxxx', [], types.void)
  49. del ptr
  50. del lib
  51. assert not ALLOCATED
  52. def test_struct_fields(self):
  53. longsize = 4 if IS_32_BIT else 8
  54. POINT = lltype.Struct('POINT',
  55. ('x', rffi.LONG),
  56. ('y', rffi.SHORT),
  57. ('z', rffi.VOIDP),
  58. )
  59. y_ofs = longsize
  60. z_ofs = longsize*2
  61. p = lltype.malloc(POINT, flavor='raw')
  62. p.x = 42
  63. p.y = rffi.cast(rffi.SHORT, -1)
  64. p.z = rffi.cast(rffi.VOIDP, 0x1234)
  65. addr = rffi.cast(rffi.VOIDP, p)
  66. assert struct_getfield_int(types.slong, addr, 0) == 42
  67. assert struct_getfield_int(types.sshort, addr, y_ofs) == -1
  68. assert struct_getfield_int(types.pointer, addr, z_ofs) == 0x1234
  69. #
  70. struct_setfield_int(types.slong, addr, 0, 43)
  71. struct_setfield_int(types.sshort, addr, y_ofs, 0x1234FFFE) # 0x1234 is masked out
  72. struct_setfield_int(types.pointer, addr, z_ofs, 0x4321)
  73. assert p.x == 43
  74. assert p.y == -2
  75. assert rffi.cast(rffi.LONG, p.z) == 0x4321
  76. #
  77. lltype.free(p, flavor='raw')
  78. def test_array_fields(self):
  79. POINT = lltype.Struct("POINT",
  80. ("x", lltype.Float),
  81. ("y", lltype.Float),
  82. )
  83. points = lltype.malloc(rffi.CArray(POINT), 2, flavor="raw")
  84. points[0].x = 1.0
  85. points[0].y = 2.0
  86. points[1].x = 3.0
  87. points[1].y = 4.0
  88. points = rffi.cast(rffi.CArrayPtr(lltype.Char), points)
  89. assert array_getitem(types.double, 16, points, 0, 0) == 1.0
  90. assert array_getitem(types.double, 16, points, 0, 8) == 2.0
  91. assert array_getitem(types.double, 16, points, 1, 0) == 3.0
  92. assert array_getitem(types.double, 16, points, 1, 8) == 4.0
  93. #
  94. array_setitem(types.double, 16, points, 0, 0, 10.0)
  95. array_setitem(types.double, 16, points, 0, 8, 20.0)
  96. array_setitem(types.double, 16, points, 1, 0, 30.0)
  97. array_setitem(types.double, 16, points, 1, 8, 40.0)
  98. #
  99. assert array_getitem(types.double, 16, points, 0, 0) == 10.0
  100. assert array_getitem(types.double, 16, points, 0, 8) == 20.0
  101. assert array_getitem(types.double, 16, points, 1, 0) == 30.0
  102. assert array_getitem(types.double, 16, points, 1, 8) == 40.0
  103. #
  104. lltype.free(points, flavor="raw")
  105. def test_struct_fields_longlong(self):
  106. POINT = lltype.Struct('POINT',
  107. ('x', rffi.LONGLONG),
  108. ('y', rffi.ULONGLONG)
  109. )
  110. y_ofs = 8
  111. p = lltype.malloc(POINT, flavor='raw')
  112. p.x = r_longlong(123)
  113. p.y = r_ulonglong(456)
  114. addr = rffi.cast(rffi.VOIDP, p)
  115. assert struct_getfield_longlong(types.slonglong, addr, 0) == 123
  116. assert struct_getfield_longlong(types.ulonglong, addr, y_ofs) == 456
  117. #
  118. v = rffi.cast(lltype.SignedLongLong, r_ulonglong(9223372036854775808))
  119. struct_setfield_longlong(types.slonglong, addr, 0, v)
  120. struct_setfield_longlong(types.ulonglong, addr, y_ofs, r_longlong(-1))
  121. assert p.x == -9223372036854775808
  122. assert rffi.cast(lltype.UnsignedLongLong, p.y) == 18446744073709551615
  123. #
  124. lltype.free(p, flavor='raw')
  125. def test_struct_fields_float(self):
  126. POINT = lltype.Struct('POINT',
  127. ('x', rffi.DOUBLE),
  128. ('y', rffi.DOUBLE)
  129. )
  130. y_ofs = 8
  131. p = lltype.malloc(POINT, flavor='raw')
  132. p.x = 123.4
  133. p.y = 567.8
  134. addr = rffi.cast(rffi.VOIDP, p)
  135. assert struct_getfield_float(types.double, addr, 0) == 123.4
  136. assert struct_getfield_float(types.double, addr, y_ofs) == 567.8
  137. #
  138. struct_setfield_float(types.double, addr, 0, 321.0)
  139. struct_setfield_float(types.double, addr, y_ofs, 876.5)
  140. assert p.x == 321.0
  141. assert p.y == 876.5
  142. #
  143. lltype.free(p, flavor='raw')
  144. def test_struct_fields_singlefloat(self):
  145. POINT = lltype.Struct('POINT',
  146. ('x', rffi.FLOAT),
  147. ('y', rffi.FLOAT)
  148. )
  149. y_ofs = 4
  150. p = lltype.malloc(POINT, flavor='raw')
  151. p.x = r_singlefloat(123.4)
  152. p.y = r_singlefloat(567.8)
  153. addr = rffi.cast(rffi.VOIDP, p)
  154. assert struct_getfield_singlefloat(types.double, addr, 0) == r_singlefloat(123.4)
  155. assert struct_getfield_singlefloat(types.double, addr, y_ofs) == r_singlefloat(567.8)
  156. #
  157. struct_setfield_singlefloat(types.double, addr, 0, r_singlefloat(321.0))
  158. struct_setfield_singlefloat(types.double, addr, y_ofs, r_singlefloat(876.5))
  159. assert p.x == r_singlefloat(321.0)
  160. assert p.y == r_singlefloat(876.5)
  161. #
  162. lltype.free(p, flavor='raw')
  163. class TestLibffiCall(BaseFfiTest):
  164. """
  165. Test various kind of calls through libffi.
  166. The peculiarity of these tests is that they are run both directly (going
  167. really through libffi) and by jit/metainterp/test/test_fficall.py, which
  168. tests the call when JITted.
  169. If you need to test a behaviour than it's not affected by JITing (e.g.,
  170. typechecking), you should put your test in TestLibffiMisc.
  171. """
  172. CDLL = CDLL
  173. @classmethod
  174. def setup_class(cls):
  175. from pypy.tool.udir import udir
  176. from pypy.translator.tool.cbuild import ExternalCompilationInfo
  177. from pypy.translator.tool.cbuild import STANDARD_DEFINES
  178. from pypy.translator.platform import platform
  179. BaseFfiTest.setup_class()
  180. # prepare C code as an example, so we can load it and call
  181. # it via rlib.libffi
  182. c_file = udir.ensure("test_libffi", dir=1).join("foolib.c")
  183. # automatically collect the C source from the docstrings of the tests
  184. snippets = []
  185. exports = []
  186. for name in dir(cls):
  187. if name.startswith('test_'):
  188. meth = getattr(cls, name)
  189. # the heuristic to determine it it's really C code could be
  190. # improved: so far we just check that there is a '{' :-)
  191. if meth.__doc__ is not None and '{' in meth.__doc__:
  192. snippets.append(meth.__doc__)
  193. import re
  194. for match in re.finditer(" ([a-z_]+)\(", meth.__doc__):
  195. exports.append(match.group(1))
  196. #
  197. c_file.write(STANDARD_DEFINES + str(py.code.Source('\n'.join(snippets))))
  198. eci = ExternalCompilationInfo(export_symbols=exports)
  199. cls.libfoo_name = str(platform.compile([c_file], eci, 'x',
  200. standalone=False))
  201. def get_libfoo(self):
  202. return self.CDLL(self.libfoo_name)
  203. def call(self, funcspec, args, RESULT, is_struct=False, jitif=[]):
  204. """
  205. Call the specified function after constructing and ArgChain with the
  206. arguments in ``args``.
  207. The function is specified with ``funcspec``, which is a tuple of the
  208. form (lib, name, argtypes, restype).
  209. This method is overridden by metainterp/test/test_fficall.py in
  210. order to do the call in a loop and JIT it. The optional arguments are
  211. used only by that overridden method.
  212. """
  213. lib, name, argtypes, restype = funcspec
  214. func = lib.getpointer(name, argtypes, restype)
  215. chain = ArgChain()
  216. for arg in args:
  217. if isinstance(arg, tuple):
  218. methname, arg = arg
  219. meth = getattr(chain, methname)
  220. meth(arg)
  221. else:
  222. chain.arg(arg)
  223. return func.call(chain, RESULT, is_struct=is_struct)
  224. # ------------------------------------------------------------------------
  225. def test_very_simple(self):
  226. """
  227. int diff_xy(int x, Signed y)
  228. {
  229. return x - y;
  230. }
  231. """
  232. libfoo = self.get_libfoo()
  233. func = (libfoo, 'diff_xy', [types.sint, types.signed], types.sint)
  234. res = self.call(func, [50, 8], lltype.Signed)
  235. assert res == 42
  236. def test_simple(self):
  237. """
  238. int sum_xy(int x, double y)
  239. {
  240. return (x + (int)y);
  241. }
  242. """
  243. libfoo = self.get_libfoo()
  244. func = (libfoo, 'sum_xy', [types.sint, types.double], types.sint)
  245. res = self.call(func, [38, 4.2], lltype.Signed, jitif=["floats"])
  246. assert res == 42
  247. def test_float_result(self):
  248. libm = self.get_libm()
  249. func = (libm, 'pow', [types.double, types.double], types.double)
  250. res = self.call(func, [2.0, 3.0], rffi.DOUBLE, jitif=["floats"])
  251. assert res == 8.0
  252. def test_cast_result(self):
  253. """
  254. unsigned char cast_to_uchar_and_ovf(int x)
  255. {
  256. return 200+(unsigned char)x;
  257. }
  258. """
  259. libfoo = self.get_libfoo()
  260. func = (libfoo, 'cast_to_uchar_and_ovf', [types.sint], types.uchar)
  261. res = self.call(func, [0], rffi.UCHAR)
  262. assert res == 200
  263. def test_cast_argument(self):
  264. """
  265. int many_args(char a, int b)
  266. {
  267. return a+b;
  268. }
  269. """
  270. libfoo = self.get_libfoo()
  271. func = (libfoo, 'many_args', [types.uchar, types.sint], types.sint)
  272. res = self.call(func, [chr(20), 22], rffi.SIGNED)
  273. assert res == 42
  274. def test_char_args(self):
  275. """
  276. char sum_args(char a, char b) {
  277. return a + b;
  278. }
  279. """
  280. libfoo = self.get_libfoo()
  281. func = (libfoo, 'sum_args', [types.schar, types.schar], types.schar)
  282. res = self.call(func, [123, 43], rffi.CHAR)
  283. assert res == chr(166)
  284. def test_unsigned_short_args(self):
  285. """
  286. unsigned short sum_xy_us(unsigned short x, unsigned short y)
  287. {
  288. return x+y;
  289. }
  290. """
  291. libfoo = self.get_libfoo()
  292. func = (libfoo, 'sum_xy_us', [types.ushort, types.ushort], types.ushort)
  293. res = self.call(func, [32000, 8000], rffi.USHORT)
  294. assert res == 40000
  295. def test_pointer_as_argument(self):
  296. """#include <stdlib.h>
  297. Signed inc(Signed* x)
  298. {
  299. Signed oldval;
  300. if (x == NULL)
  301. return -1;
  302. oldval = *x;
  303. *x = oldval+1;
  304. return oldval;
  305. }
  306. """
  307. libfoo = self.get_libfoo()
  308. func = (libfoo, 'inc', [types.pointer], types.signed)
  309. null = lltype.nullptr(rffi.SIGNEDP.TO)
  310. res = self.call(func, [null], rffi.SIGNED)
  311. assert res == -1
  312. #
  313. ptr_result = lltype.malloc(rffi.SIGNEDP.TO, 1, flavor='raw')
  314. ptr_result[0] = 41
  315. res = self.call(func, [ptr_result], rffi.SIGNED)
  316. if self.__class__ is TestLibffiCall:
  317. # the function was called only once
  318. assert res == 41
  319. assert ptr_result[0] == 42
  320. lltype.free(ptr_result, flavor='raw')
  321. # the test does not make sense when run with the JIT through
  322. # meta_interp, because the __del__ are not properly called (hence
  323. # we "leak" memory)
  324. del libfoo
  325. assert not ALLOCATED
  326. else:
  327. # the function as been called 9 times
  328. assert res == 50
  329. assert ptr_result[0] == 51
  330. lltype.free(ptr_result, flavor='raw')
  331. def test_return_pointer(self):
  332. """
  333. struct pair {
  334. Signed a;
  335. Signed b;
  336. };
  337. struct pair my_static_pair = {10, 20};
  338. Signed* get_pointer_to_b()
  339. {
  340. return &my_static_pair.b;
  341. }
  342. """
  343. libfoo = self.get_libfoo()
  344. func = (libfoo, 'get_pointer_to_b', [], types.pointer)
  345. res = self.call(func, [], rffi.SIGNEDP)
  346. assert res[0] == 20
  347. def test_void_result(self):
  348. """
  349. int dummy;
  350. void set_dummy(int val) { dummy = val; }
  351. int get_dummy() { return dummy; }
  352. """
  353. libfoo = self.get_libfoo()
  354. set_dummy = (libfoo, 'set_dummy', [types.sint], types.void)
  355. get_dummy = (libfoo, 'get_dummy', [], types.sint)
  356. #
  357. initval = self.call(get_dummy, [], rffi.SIGNED)
  358. #
  359. res = self.call(set_dummy, [initval+1], lltype.Void)
  360. assert res is None
  361. #
  362. res = self.call(get_dummy, [], rffi.SIGNED)
  363. assert res == initval+1
  364. def test_single_float_args(self):
  365. """
  366. float sum_xy_float(float x, float y)
  367. {
  368. return x+y;
  369. }
  370. """
  371. from ctypes import c_float # this is used only to compute the expected result
  372. libfoo = self.get_libfoo()
  373. func = (libfoo, 'sum_xy_float', [types.float, types.float], types.float)
  374. x = r_singlefloat(12.34)
  375. y = r_singlefloat(56.78)
  376. res = self.call(func, [x, y], rffi.FLOAT, jitif=["singlefloats"])
  377. expected = c_float(c_float(12.34).value + c_float(56.78).value).value
  378. assert float(res) == expected
  379. def test_slonglong_args(self):
  380. """
  381. long long sum_xy_longlong(long long x, long long y)
  382. {
  383. return x+y;
  384. }
  385. """
  386. maxint32 = 2147483647 # we cannot really go above maxint on 64 bits
  387. # (and we would not test anything, as there long
  388. # is the same as long long)
  389. libfoo = self.get_libfoo()
  390. func = (libfoo, 'sum_xy_longlong', [types.slonglong, types.slonglong],
  391. types.slonglong)
  392. if IS_32_BIT:
  393. x = r_longlong(maxint32+1)
  394. y = r_longlong(maxint32+2)
  395. else:
  396. x = maxint32+1
  397. y = maxint32+2
  398. res = self.call(func, [x, y], rffi.LONGLONG, jitif=["longlong"])
  399. expected = maxint32*2 + 3
  400. assert res == expected
  401. def test_ulonglong_args(self):
  402. """
  403. unsigned long long sum_xy_ulonglong(unsigned long long x,
  404. unsigned long long y)
  405. {
  406. return x+y;
  407. }
  408. """
  409. maxint64 = 9223372036854775807 # maxint64+1 does not fit into a
  410. # longlong, but it does into a
  411. # ulonglong
  412. libfoo = self.get_libfoo()
  413. func = (libfoo, 'sum_xy_ulonglong', [types.ulonglong, types.ulonglong],
  414. types.ulonglong)
  415. x = r_ulonglong(maxint64+1)
  416. y = r_ulonglong(2)
  417. res = self.call(func, [x, y], rffi.ULONGLONG, jitif=["longlong"])
  418. expected = maxint64 + 3
  419. assert res == expected
  420. def test_wrong_number_of_arguments(self):
  421. from pypy.rpython.llinterp import LLException
  422. libfoo = self.get_libfoo()
  423. func = (libfoo, 'sum_xy', [types.sint, types.double], types.sint)
  424. glob = globals()
  425. loc = locals()
  426. def my_raises(s):
  427. try:
  428. exec s in glob, loc
  429. except TypeError:
  430. pass
  431. except LLException, e:
  432. if str(e) != "<LLException 'TypeError'>":
  433. raise
  434. else:
  435. assert False, 'Did not raise'
  436. my_raises("self.call(func, [38], rffi.SIGNED)") # one less
  437. my_raises("self.call(func, [38, 12.3, 42], rffi.SIGNED)") # one more
  438. def test_byval_argument(self):
  439. """
  440. struct Point {
  441. Signed x;
  442. Signed y;
  443. };
  444. Signed sum_point(struct Point p) {
  445. return p.x + p.y;
  446. }
  447. """
  448. libfoo = CDLL(self.libfoo_name)
  449. ffi_point_struct = make_struct_ffitype_e(0, 0, [types.signed, types.signed])
  450. ffi_point = ffi_point_struct.ffistruct
  451. sum_point = (libfoo, 'sum_point', [ffi_point], types.signed)
  452. #
  453. ARRAY = rffi.CArray(rffi.SIGNED)
  454. buf = lltype.malloc(ARRAY, 2, flavor='raw')
  455. buf[0] = 30
  456. buf[1] = 12
  457. adr = rffi.cast(rffi.VOIDP, buf)
  458. res = self.call(sum_point, [('arg_raw', adr)], rffi.SIGNED,
  459. jitif=["byval"])
  460. assert res == 42
  461. # check that we still have the ownership on the buffer
  462. assert buf[0] == 30
  463. assert buf[1] == 12
  464. lltype.free(buf, flavor='raw')
  465. lltype.free(ffi_point_struct, flavor='raw')
  466. def test_byval_result(self):
  467. """
  468. struct Point make_point(Signed x, Signed y) {
  469. struct Point p;
  470. p.x = x;
  471. p.y = y;
  472. return p;
  473. }
  474. """
  475. libfoo = CDLL(self.libfoo_name)
  476. ffi_point_struct = make_struct_ffitype_e(0, 0, [types.signed, types.signed])
  477. ffi_point = ffi_point_struct.ffistruct
  478. libfoo = CDLL(self.libfoo_name)
  479. make_point = (libfoo, 'make_point', [types.signed, types.signed], ffi_point)
  480. #
  481. PTR = lltype.Ptr(rffi.CArray(rffi.SIGNED))
  482. p = self.call(make_point, [12, 34], PTR, is_struct=True,
  483. jitif=["byval"])
  484. assert p[0] == 12
  485. assert p[1] == 34
  486. lltype.free(p, flavor='raw')
  487. lltype.free(ffi_point_struct, flavor='raw')