PageRenderTime 48ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rlib/test/test_libffi.py

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