PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/pypy/rlib/test/test_libffi.py

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