PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/rpython/extfunc.py

https://bitbucket.org/quangquach/pypy
Python | 292 lines | 250 code | 22 blank | 20 comment | 36 complexity | 6b0b9169a64bcfbce390681b25c09db1 MD5 | raw file
  1. from pypy.rpython import extregistry
  2. from pypy.rpython.extregistry import ExtRegistryEntry
  3. from pypy.rpython.lltypesystem.lltype import typeOf
  4. from pypy.objspace.flow.model import Constant
  5. from pypy.annotation import model as annmodel
  6. from pypy.annotation.signature import annotation
  7. import py, sys
  8. class extdef(object):
  9. def __init__(self, *args, **kwds):
  10. self.def_args = args
  11. self.def_kwds = kwds
  12. def lazy_register(func_or_list, register_func):
  13. """ Lazily register external function. Will create a function,
  14. which explodes when llinterpd/translated, but does not explode
  15. earlier
  16. """
  17. if isinstance(func_or_list, list):
  18. funcs = func_or_list
  19. else:
  20. funcs = [func_or_list]
  21. try:
  22. val = register_func()
  23. if isinstance(val, extdef):
  24. assert len(funcs) == 1
  25. register_external(funcs[0], *val.def_args, **val.def_kwds)
  26. return
  27. return val
  28. except (SystemExit, MemoryError, KeyboardInterrupt), e:
  29. raise
  30. except:
  31. if 0:
  32. import traceback
  33. print >> sys.stderr, 'WARNING: cannot register', func_or_list, ':'
  34. traceback.print_exc()
  35. import pdb; pdb.set_trace()
  36. exc, exc_inst, tb = sys.exc_info()
  37. for func in funcs:
  38. # if the function has already been registered and we got
  39. # an exception afterwards, the ExtRaisingEntry would create
  40. # a double-registration and crash in an AssertionError that
  41. # masks the original problem. In this case, just re-raise now.
  42. if extregistry.is_registered(func):
  43. raise exc, exc_inst, tb
  44. class ExtRaisingEntry(ExtRegistryEntry):
  45. _about_ = func
  46. def __getattr__(self, attr):
  47. if attr == '_about_' or attr == '__dict__':
  48. return super(ExtRegistryEntry, self).__getattr__(attr)
  49. raise exc, exc_inst, tb
  50. def registering(func, condition=True):
  51. if not condition:
  52. return lambda method: None
  53. def decorator(method):
  54. method._registering_func = func
  55. return method
  56. return decorator
  57. def registering_if(ns, name, condition=True):
  58. try:
  59. func = getattr(ns, name)
  60. except AttributeError:
  61. condition = False
  62. func = None
  63. return registering(func, condition=condition)
  64. class LazyRegisteringMeta(type):
  65. def __new__(self, _name, _type, _vars):
  66. RegisteringClass = type.__new__(self, _name, _type, _vars)
  67. allfuncs = []
  68. for varname in _vars:
  69. attr = getattr(RegisteringClass, varname)
  70. f = getattr(attr, '_registering_func', None)
  71. if f:
  72. allfuncs.append(f)
  73. registering_inst = lazy_register(allfuncs, RegisteringClass)
  74. if registering_inst is not None:
  75. for varname in _vars:
  76. attr = getattr(registering_inst, varname)
  77. f = getattr(attr, '_registering_func', None)
  78. if f:
  79. lazy_register(f, attr)
  80. RegisteringClass.instance = registering_inst
  81. # override __init__ to avoid confusion
  82. def raising(self):
  83. raise TypeError("Cannot call __init__ directly, use cls.instance to access singleton")
  84. RegisteringClass.__init__ = raising
  85. return RegisteringClass
  86. class BaseLazyRegistering(object):
  87. __metaclass__ = LazyRegisteringMeta
  88. compilation_info = None
  89. def configure(self, CConfig):
  90. classes_seen = self.__dict__.setdefault('__classes_seen', {})
  91. if CConfig in classes_seen:
  92. return
  93. from pypy.rpython.tool import rffi_platform as platform
  94. # copy some stuff
  95. if self.compilation_info is None:
  96. self.compilation_info = CConfig._compilation_info_
  97. else:
  98. self.compilation_info = self.compilation_info.merge(
  99. CConfig._compilation_info_)
  100. self.__dict__.update(platform.configure(CConfig))
  101. classes_seen[CConfig] = True
  102. def llexternal(self, *args, **kwds):
  103. kwds = kwds.copy()
  104. from pypy.rpython.lltypesystem import rffi
  105. if 'compilation_info' in kwds:
  106. kwds['compilation_info'] = self.compilation_info.merge(
  107. kwds['compilation_info'])
  108. else:
  109. kwds['compilation_info'] = self.compilation_info
  110. return rffi.llexternal(*args, **kwds)
  111. def _freeze_(self):
  112. return True
  113. class genericcallable(object):
  114. """ A way to specify the callable annotation, but deferred until
  115. we have bookkeeper
  116. """
  117. def __init__(self, args, result=None):
  118. self.args = args
  119. self.result = result
  120. class _ext_callable(ExtRegistryEntry):
  121. _type_ = genericcallable
  122. # we defer a bit annotation here
  123. def compute_result_annotation(self):
  124. return annmodel.SomeGenericCallable([annotation(i, self.bookkeeper)
  125. for i in self.instance.args],
  126. annotation(self.instance.result, self.bookkeeper))
  127. class ExtFuncEntry(ExtRegistryEntry):
  128. safe_not_sandboxed = False
  129. # common case: args is a list of annotation or types
  130. def normalize_args(self, *args_s):
  131. args = self.signature_args
  132. signature_args = [annotation(arg, None) for arg in args]
  133. assert len(args_s) == len(signature_args),\
  134. "Argument number mismatch"
  135. for i, expected in enumerate(signature_args):
  136. arg = annmodel.unionof(args_s[i], expected)
  137. if not expected.contains(arg):
  138. name = getattr(self, 'name', None)
  139. if not name:
  140. try:
  141. name = self.instance.__name__
  142. except AttributeError:
  143. name = '?'
  144. raise Exception("In call to external function %r:\n"
  145. "arg %d must be %s,\n"
  146. " got %s" % (
  147. name, i+1, expected, args_s[i]))
  148. return signature_args
  149. def compute_result_annotation(self, *args_s):
  150. self.normalize_args(*args_s) # check arguments
  151. return self.signature_result
  152. def specialize_call(self, hop):
  153. rtyper = hop.rtyper
  154. signature_args = self.normalize_args(*hop.args_s)
  155. args_r = [rtyper.getrepr(s_arg) for s_arg in signature_args]
  156. args_ll = [r_arg.lowleveltype for r_arg in args_r]
  157. s_result = hop.s_result
  158. r_result = rtyper.getrepr(s_result)
  159. ll_result = r_result.lowleveltype
  160. name = getattr(self, 'name', None) or self.instance.__name__
  161. method_name = rtyper.type_system.name[:2] + 'typeimpl'
  162. fake_method_name = rtyper.type_system.name[:2] + 'typefakeimpl'
  163. impl = getattr(self, method_name, None)
  164. fakeimpl = getattr(self, fake_method_name, self.instance)
  165. if impl:
  166. if hasattr(self, fake_method_name):
  167. # If we have both an {ll,oo}impl and a {ll,oo}fakeimpl,
  168. # we need a wrapper that selects the proper one and calls it
  169. from pypy.tool.sourcetools import func_with_new_name
  170. # Using '*args' is delicate because this wrapper is also
  171. # created for init-time functions like llarena.arena_malloc
  172. # which are called before the GC is fully initialized
  173. args = ', '.join(['arg%d' % i for i in range(len(args_ll))])
  174. d = {'original_impl': impl,
  175. 's_result': s_result,
  176. 'fakeimpl': fakeimpl,
  177. '__name__': __name__,
  178. }
  179. exec py.code.compile("""
  180. from pypy.rlib.objectmodel import running_on_llinterp
  181. from pypy.rlib.debug import llinterpcall
  182. from pypy.rlib.jit import dont_look_inside
  183. # note: we say 'dont_look_inside' mostly because the
  184. # JIT does not support 'running_on_llinterp', but in
  185. # theory it is probably right to stop jitting anyway.
  186. @dont_look_inside
  187. def ll_wrapper(%s):
  188. if running_on_llinterp:
  189. return llinterpcall(s_result, fakeimpl, %s)
  190. else:
  191. return original_impl(%s)
  192. """ % (args, args, args)) in d
  193. impl = func_with_new_name(d['ll_wrapper'], name + '_wrapper')
  194. if rtyper.annotator.translator.config.translation.sandbox:
  195. impl._dont_inline_ = True
  196. # store some attributes to the 'impl' function, where
  197. # the eventual call to rtyper.getcallable() will find them
  198. # and transfer them to the final lltype.functionptr().
  199. impl._llfnobjattrs_ = {
  200. '_name': self.name,
  201. '_safe_not_sandboxed': self.safe_not_sandboxed,
  202. }
  203. obj = rtyper.getannmixlevel().delayedfunction(
  204. impl, signature_args, hop.s_result)
  205. else:
  206. #if not self.safe_not_sandboxed:
  207. # print '>>>>>>>>>>>>>-----------------------------------'
  208. # print name, self.name
  209. # print '<<<<<<<<<<<<<-----------------------------------'
  210. obj = rtyper.type_system.getexternalcallable(args_ll, ll_result,
  211. name, _external_name=self.name, _callable=fakeimpl,
  212. _safe_not_sandboxed=self.safe_not_sandboxed)
  213. vlist = [hop.inputconst(typeOf(obj), obj)] + hop.inputargs(*args_r)
  214. hop.exception_is_here()
  215. return hop.genop('direct_call', vlist, r_result)
  216. def register_external(function, args, result=None, export_name=None,
  217. llimpl=None, ooimpl=None,
  218. llfakeimpl=None, oofakeimpl=None,
  219. sandboxsafe=False):
  220. """
  221. function: the RPython function that will be rendered as an external function (e.g.: math.floor)
  222. args: a list containing the annotation of the arguments
  223. result: surprisingly enough, the annotation of the result
  224. export_name: the name of the function as it will be seen by the backends
  225. llimpl, ooimpl: optional; if provided, these RPython functions are called instead of the target function
  226. llfakeimpl, oofakeimpl: optional; if provided, they are called by the llinterpreter
  227. sandboxsafe: use True if the function performs no I/O (safe for --sandbox)
  228. """
  229. if export_name is None:
  230. export_name = function.__name__
  231. class FunEntry(ExtFuncEntry):
  232. _about_ = function
  233. safe_not_sandboxed = sandboxsafe
  234. if args is None:
  235. def normalize_args(self, *args_s):
  236. return args_s # accept any argument unmodified
  237. elif callable(args):
  238. # custom annotation normalizer (see e.g. os.utime())
  239. normalize_args = staticmethod(args)
  240. else: # use common case behavior
  241. signature_args = args
  242. signature_result = annotation(result, None)
  243. name=export_name
  244. if llimpl:
  245. lltypeimpl = staticmethod(llimpl)
  246. if ooimpl:
  247. ootypeimpl = staticmethod(ooimpl)
  248. if llfakeimpl:
  249. lltypefakeimpl = staticmethod(llfakeimpl)
  250. if oofakeimpl:
  251. ootypefakeimpl = staticmethod(oofakeimpl)
  252. if export_name:
  253. FunEntry.__name__ = export_name
  254. else:
  255. FunEntry.__name__ = function.func_name
  256. BaseLazyRegistering.register = staticmethod(register_external)
  257. def is_external(func):
  258. if hasattr(func, 'value'):
  259. func = func.value
  260. if hasattr(func, '_external_name'):
  261. return True
  262. return False