PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rlib/test/test_libffi.py

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