PageRenderTime 33ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/cppyy/executor.py

https://bitbucket.org/vanl/pypy
Python | 379 lines | 331 code | 36 blank | 12 comment | 4 complexity | 7c73a0257524bd6bb47aaa98541dba85 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, AGPL-3.0
  1. import sys
  2. from pypy.interpreter.error import OperationError
  3. from rpython.rtyper.lltypesystem import rffi, lltype
  4. from rpython.rlib import jit_libffi
  5. from pypy.module._rawffi.interp_rawffi import unpack_simple_shape
  6. from pypy.module._rawffi.array import W_Array, W_ArrayInstance
  7. from pypy.module.cppyy import helper, capi, ffitypes
  8. # Executor objects are used to dispatch C++ methods. They are defined by their
  9. # return type only: arguments are converted by Converter objects, and Executors
  10. # only deal with arrays of memory that are either passed to a stub or libffi.
  11. # No argument checking or conversions are done.
  12. #
  13. # If a libffi function is not implemented, FastCallNotPossible is raised. If a
  14. # stub function is missing (e.g. if no reflection info is available for the
  15. # return type), an app-level TypeError is raised.
  16. #
  17. # Executor instances are created by get_executor(<return type name>), see
  18. # below. The name given should be qualified in case there is a specialised,
  19. # exact match for the qualified type.
  20. NULL = lltype.nullptr(jit_libffi.FFI_TYPE_P.TO)
  21. class FunctionExecutor(object):
  22. _immutable_fields_ = ['libffitype']
  23. libffitype = NULL
  24. def __init__(self, space, extra):
  25. pass
  26. def execute(self, space, cppmethod, cppthis, num_args, args):
  27. raise OperationError(space.w_TypeError,
  28. space.wrap('return type not available or supported'))
  29. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  30. from pypy.module.cppyy.interp_cppyy import FastCallNotPossible
  31. raise FastCallNotPossible
  32. class PtrTypeExecutor(FunctionExecutor):
  33. _immutable_fields_ = ['libffitype', 'typecode']
  34. libffitype = jit_libffi.types.pointer
  35. typecode = 'P'
  36. def execute(self, space, cppmethod, cppthis, num_args, args):
  37. if hasattr(space, "fake"):
  38. raise NotImplementedError
  39. lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
  40. ptrval = rffi.cast(rffi.ULONG, lresult)
  41. arr = space.interp_w(W_Array, unpack_simple_shape(space, space.wrap(self.typecode)))
  42. if ptrval == 0:
  43. from pypy.module.cppyy import interp_cppyy
  44. return interp_cppyy.get_nullptr(space)
  45. return arr.fromaddress(space, ptrval, sys.maxint)
  46. class VoidExecutor(FunctionExecutor):
  47. _immutable_fields_ = ['libffitype']
  48. libffitype = jit_libffi.types.void
  49. def execute(self, space, cppmethod, cppthis, num_args, args):
  50. capi.c_call_v(space, cppmethod, cppthis, num_args, args)
  51. return space.w_None
  52. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  53. jit_libffi.jit_ffi_call(cif_descr, funcaddr, buffer)
  54. return space.w_None
  55. class NumericExecutorMixin(object):
  56. _mixin_ = True
  57. def _wrap_object(self, space, obj):
  58. return space.wrap(obj)
  59. def execute(self, space, cppmethod, cppthis, num_args, args):
  60. result = self.c_stubcall(space, cppmethod, cppthis, num_args, args)
  61. return self._wrap_object(space, rffi.cast(self.c_type, result))
  62. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  63. jit_libffi.jit_ffi_call(cif_descr, funcaddr, buffer)
  64. result = rffi.ptradd(buffer, cif_descr.exchange_result)
  65. return self._wrap_object(space, rffi.cast(self.c_ptrtype, result)[0])
  66. class NumericRefExecutorMixin(object):
  67. _mixin_ = True
  68. def __init__(self, space, extra):
  69. FunctionExecutor.__init__(self, space, extra)
  70. self.do_assign = False
  71. self.item = rffi.cast(self.c_type, 0)
  72. def set_item(self, space, w_item):
  73. self.item = self._unwrap_object(space, w_item)
  74. self.do_assign = True
  75. def _wrap_object(self, space, obj):
  76. return space.wrap(rffi.cast(self.c_type, obj))
  77. def _wrap_reference(self, space, rffiptr):
  78. if self.do_assign:
  79. rffiptr[0] = self.item
  80. self.do_assign = False
  81. return self._wrap_object(space, rffiptr[0]) # all paths, for rtyper
  82. def execute(self, space, cppmethod, cppthis, num_args, args):
  83. result = capi.c_call_r(space, cppmethod, cppthis, num_args, args)
  84. return self._wrap_reference(space, rffi.cast(self.c_ptrtype, result))
  85. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  86. jit_libffi.jit_ffi_call(cif_descr, funcaddr, buffer)
  87. result = rffi.ptradd(buffer, cif_descr.exchange_result)
  88. return self._wrap_reference(space,
  89. rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0]))
  90. class CStringExecutor(FunctionExecutor):
  91. def execute(self, space, cppmethod, cppthis, num_args, args):
  92. lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
  93. ccpresult = rffi.cast(rffi.CCHARP, lresult)
  94. if ccpresult == rffi.cast(rffi.CCHARP, 0):
  95. return space.wrap("")
  96. result = rffi.charp2str(ccpresult) # TODO: make it a choice to free
  97. return space.wrap(result)
  98. class ConstructorExecutor(FunctionExecutor):
  99. def execute(self, space, cppmethod, cpptype, num_args, args):
  100. from pypy.module.cppyy import interp_cppyy
  101. newthis = capi.c_constructor(space, cppmethod, cpptype, num_args, args)
  102. assert lltype.typeOf(newthis) == capi.C_OBJECT
  103. return space.wrap(rffi.cast(rffi.LONG, newthis)) # really want ptrdiff_t here
  104. class InstancePtrExecutor(FunctionExecutor):
  105. _immutable_fields_ = ['libffitype', 'cppclass']
  106. libffitype = jit_libffi.types.pointer
  107. def __init__(self, space, cppclass):
  108. FunctionExecutor.__init__(self, space, cppclass)
  109. self.cppclass = cppclass
  110. def execute(self, space, cppmethod, cppthis, num_args, args):
  111. from pypy.module.cppyy import interp_cppyy
  112. long_result = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
  113. ptr_result = rffi.cast(capi.C_OBJECT, long_result)
  114. pyres = interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass)
  115. return pyres
  116. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  117. jit_libffi.jit_ffi_call(cif_descr, funcaddr, buffer)
  118. result = rffi.ptradd(buffer, cif_descr.exchange_result)
  119. from pypy.module.cppyy import interp_cppyy
  120. ptr_result = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, result)[0])
  121. return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass)
  122. class InstancePtrPtrExecutor(InstancePtrExecutor):
  123. def execute(self, space, cppmethod, cppthis, num_args, args):
  124. from pypy.module.cppyy import interp_cppyy
  125. voidp_result = capi.c_call_r(space, cppmethod, cppthis, num_args, args)
  126. ref_address = rffi.cast(rffi.VOIDPP, voidp_result)
  127. ptr_result = rffi.cast(capi.C_OBJECT, ref_address[0])
  128. return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass)
  129. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  130. from pypy.module.cppyy.interp_cppyy import FastCallNotPossible
  131. raise FastCallNotPossible
  132. class InstanceExecutor(InstancePtrExecutor):
  133. def execute(self, space, cppmethod, cppthis, num_args, args):
  134. from pypy.module.cppyy import interp_cppyy
  135. long_result = capi.c_call_o(space, cppmethod, cppthis, num_args, args, self.cppclass)
  136. ptr_result = rffi.cast(capi.C_OBJECT, long_result)
  137. return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass,
  138. do_cast=False, python_owns=True, fresh=True)
  139. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  140. from pypy.module.cppyy.interp_cppyy import FastCallNotPossible
  141. raise FastCallNotPossible
  142. class StdStringExecutor(InstancePtrExecutor):
  143. def execute(self, space, cppmethod, cppthis, num_args, args):
  144. cstr_result = capi.c_call_s(space, cppmethod, cppthis, num_args, args)
  145. return space.wrap(capi.charp2str_free(space, cstr_result))
  146. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  147. from pypy.module.cppyy.interp_cppyy import FastCallNotPossible
  148. raise FastCallNotPossible
  149. class StdStringRefExecutor(InstancePtrExecutor):
  150. def __init__(self, space, cppclass):
  151. from pypy.module.cppyy import interp_cppyy
  152. cppclass = interp_cppyy.scope_byname(space, capi.std_string_name)
  153. InstancePtrExecutor.__init__(self, space, cppclass)
  154. class PyObjectExecutor(PtrTypeExecutor):
  155. def wrap_result(self, space, lresult):
  156. space.getbuiltinmodule("cpyext")
  157. from pypy.module.cpyext.pyobject import PyObject, from_ref, make_ref, Py_DecRef
  158. result = rffi.cast(PyObject, lresult)
  159. w_obj = from_ref(space, result)
  160. if result:
  161. Py_DecRef(space, result)
  162. return w_obj
  163. def execute(self, space, cppmethod, cppthis, num_args, args):
  164. if hasattr(space, "fake"):
  165. raise NotImplementedError
  166. lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
  167. return self.wrap_result(space, lresult)
  168. def execute_libffi(self, space, cif_descr, funcaddr, buffer):
  169. if hasattr(space, "fake"):
  170. raise NotImplementedError
  171. jit_libffi.jit_ffi_call(cif_descr, funcaddr, buffer)
  172. result = rffi.ptradd(buffer, cif_descr.exchange_result)
  173. return self.wrap_result(space, rffi.cast(rffi.LONGP, result)[0])
  174. _executors = {}
  175. def get_executor(space, name):
  176. # Matching of 'name' to an executor factory goes through up to four levels:
  177. # 1) full, qualified match
  178. # 2) drop '&': by-ref is pretty much the same as by-value, python-wise
  179. # 3) types/classes, either by ref/ptr or by value
  180. # 4) additional special cases
  181. #
  182. # If all fails, a default is used, which can be ignored at least until use.
  183. name = capi.c_resolve_name(space, name)
  184. # 1) full, qualified match
  185. try:
  186. return _executors[name](space, None)
  187. except KeyError:
  188. pass
  189. compound = helper.compound(name)
  190. clean_name = capi.c_resolve_name(space, helper.clean_type(name))
  191. # 1a) clean lookup
  192. try:
  193. return _executors[clean_name+compound](space, None)
  194. except KeyError:
  195. pass
  196. # 2) drop '&': by-ref is pretty much the same as by-value, python-wise
  197. if compound and compound[len(compound)-1] == '&':
  198. # TODO: this does not actually work with Reflex (?)
  199. try:
  200. return _executors[clean_name](space, None)
  201. except KeyError:
  202. pass
  203. # 3) types/classes, either by ref/ptr or by value
  204. from pypy.module.cppyy import interp_cppyy
  205. cppclass = interp_cppyy.scope_byname(space, clean_name)
  206. if cppclass:
  207. # type check for the benefit of the annotator
  208. from pypy.module.cppyy.interp_cppyy import W_CPPClass
  209. cppclass = space.interp_w(W_CPPClass, cppclass, can_be_None=False)
  210. if compound == '':
  211. return InstanceExecutor(space, cppclass)
  212. elif compound == '*' or compound == '&':
  213. return InstancePtrExecutor(space, cppclass)
  214. elif compound == '**' or compound == '*&':
  215. return InstancePtrPtrExecutor(space, cppclass)
  216. elif capi.c_is_enum(space, clean_name):
  217. return _executors['unsigned int'](space, None)
  218. # 4) additional special cases
  219. if compound == '*':
  220. return _executors['void*'](space, None) # allow at least passing of the pointer
  221. # currently used until proper lazy instantiation available in interp_cppyy
  222. return FunctionExecutor(space, None)
  223. _executors["void"] = VoidExecutor
  224. _executors["void*"] = PtrTypeExecutor
  225. _executors["const char*"] = CStringExecutor
  226. # special cases (note: 'string' aliases added below)
  227. _executors["constructor"] = ConstructorExecutor
  228. _executors["std::basic_string<char>"] = StdStringExecutor
  229. _executors["const std::basic_string<char>&"] = StdStringRefExecutor
  230. _executors["std::basic_string<char>&"] = StdStringRefExecutor
  231. _executors["PyObject*"] = PyObjectExecutor
  232. # add basic (builtin) executors
  233. def _build_basic_executors():
  234. "NOT_RPYTHON"
  235. type_info = (
  236. (bool, capi.c_call_b, ("bool",)),
  237. (rffi.CHAR, capi.c_call_c, ("char", "unsigned char")),
  238. (rffi.SHORT, capi.c_call_h, ("short", "short int", "unsigned short", "unsigned short int")),
  239. (rffi.INT, capi.c_call_i, ("int",)),
  240. (rffi.UINT, capi.c_call_l, ("unsigned", "unsigned int")),
  241. (rffi.LONG, capi.c_call_l, ("long", "long int")),
  242. (rffi.ULONG, capi.c_call_l, ("unsigned long", "unsigned long int")),
  243. (rffi.LONGLONG, capi.c_call_ll, ("long long", "long long int")),
  244. (rffi.ULONGLONG, capi.c_call_ll, ("unsigned long long", "unsigned long long int")),
  245. (rffi.FLOAT, capi.c_call_f, ("float",)),
  246. (rffi.DOUBLE, capi.c_call_d, ("double",)),
  247. )
  248. for c_type, stub, names in type_info:
  249. class BasicExecutor(ffitypes.typeid(c_type), NumericExecutorMixin, FunctionExecutor):
  250. _immutable_ = True
  251. c_stubcall = staticmethod(stub)
  252. class BasicRefExecutor(ffitypes.typeid(c_type), NumericRefExecutorMixin, FunctionExecutor):
  253. _immutable_fields_ = ['libffitype']
  254. libffitype = jit_libffi.types.pointer
  255. for name in names:
  256. _executors[name] = BasicExecutor
  257. _executors[name+'&'] = BasicRefExecutor
  258. _executors['const '+name+'&'] = BasicRefExecutor # no copy needed for builtins
  259. _build_basic_executors()
  260. # create the pointer executors; all real work is in the PtrTypeExecutor, since
  261. # all pointer types are of the same size
  262. def _build_ptr_executors():
  263. "NOT_RPYTHON"
  264. ptr_info = (
  265. ('b', ("bool",)), # really unsigned char, but this works ...
  266. ('h', ("short int", "short")),
  267. ('H', ("unsigned short int", "unsigned short")),
  268. ('i', ("int",)),
  269. ('I', ("unsigned int", "unsigned")),
  270. ('l', ("long int", "long")),
  271. ('L', ("unsigned long int", "unsigned long")),
  272. ('f', ("float",)),
  273. ('d', ("double",)),
  274. )
  275. for tcode, names in ptr_info:
  276. class PtrExecutor(PtrTypeExecutor):
  277. _immutable_fields_ = ['typecode']
  278. typecode = tcode
  279. for name in names:
  280. _executors[name+'*'] = PtrExecutor
  281. _build_ptr_executors()
  282. # add another set of aliased names
  283. def _add_aliased_executors():
  284. "NOT_RPYTHON"
  285. aliases = (
  286. ("const char*", "char*"),
  287. ("std::basic_string<char>", "string"),
  288. ("const std::basic_string<char>&", "const string&"),
  289. ("std::basic_string<char>&", "string&"),
  290. ("PyObject*", "_object*"),
  291. )
  292. for c_type, alias in aliases:
  293. _executors[alias] = _executors[c_type]
  294. _add_aliased_executors()