PageRenderTime 38ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rtyper/extfunc.py

https://bitbucket.org/pypy/pypy/
Python | 137 lines | 108 code | 17 blank | 12 comment | 22 complexity | 8cbc1bad0188f02b157f2140a1177348 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from rpython.annotator.model import unionof, SomeObject
  2. from rpython.annotator.signature import annotation, SignatureError
  3. from rpython.rtyper.extregistry import ExtRegistryEntry, lookup
  4. from rpython.rtyper.lltypesystem.lltype import (
  5. typeOf, FuncType, functionptr, _ptr, Void)
  6. from rpython.rtyper.error import TyperError
  7. from rpython.rtyper.rmodel import Repr
  8. class SomeExternalFunction(SomeObject):
  9. def __init__(self, name, args_s, s_result):
  10. self.name = name
  11. self.args_s = args_s
  12. self.s_result = s_result
  13. def check_args(self, callspec):
  14. params_s = self.args_s
  15. args_s, kwargs = callspec.unpack()
  16. if kwargs:
  17. raise SignatureError(
  18. "External functions cannot be called with keyword arguments")
  19. if len(args_s) != len(params_s):
  20. raise SignatureError("Argument number mismatch")
  21. for i, s_param in enumerate(params_s):
  22. arg = unionof(args_s[i], s_param)
  23. if not s_param.contains(arg):
  24. raise SignatureError(
  25. "In call to external function %r:\n"
  26. "arg %d must be %s,\n"
  27. " got %s" % (
  28. self.name, i + 1, s_param, args_s[i]))
  29. def call(self, callspec):
  30. self.check_args(callspec)
  31. return self.s_result
  32. def rtyper_makerepr(self, rtyper):
  33. if not self.is_constant():
  34. raise TyperError("Non-constant external function!")
  35. entry = lookup(self.const)
  36. impl = getattr(entry, 'lltypeimpl', None)
  37. fakeimpl = getattr(entry, 'lltypefakeimpl', None)
  38. return ExternalFunctionRepr(self, impl, fakeimpl)
  39. def rtyper_makekey(self):
  40. return self.__class__, self
  41. class ExternalFunctionRepr(Repr):
  42. lowleveltype = Void
  43. def __init__(self, s_func, impl, fakeimpl):
  44. self.s_func = s_func
  45. self.impl = impl
  46. self.fakeimpl = fakeimpl
  47. def rtype_simple_call(self, hop):
  48. rtyper = hop.rtyper
  49. args_r = [rtyper.getrepr(s_arg) for s_arg in self.s_func.args_s]
  50. r_result = rtyper.getrepr(self.s_func.s_result)
  51. obj = self.get_funcptr(rtyper, args_r, r_result)
  52. hop2 = hop.copy()
  53. hop2.r_s_popfirstarg()
  54. vlist = [hop2.inputconst(typeOf(obj), obj)] + hop2.inputargs(*args_r)
  55. hop2.exception_is_here()
  56. return hop2.genop('direct_call', vlist, r_result)
  57. def get_funcptr(self, rtyper, args_r, r_result):
  58. from rpython.rtyper.rtyper import llinterp_backend
  59. args_ll = [r_arg.lowleveltype for r_arg in args_r]
  60. ll_result = r_result.lowleveltype
  61. name = self.s_func.name
  62. if self.fakeimpl and rtyper.backend is llinterp_backend:
  63. FT = FuncType(args_ll, ll_result)
  64. return functionptr(
  65. FT, name, _external_name=name, _callable=self.fakeimpl)
  66. elif self.impl:
  67. if isinstance(self.impl, _ptr):
  68. return self.impl
  69. else:
  70. # store some attributes to the 'impl' function, where
  71. # the eventual call to rtyper.getcallable() will find them
  72. # and transfer them to the final lltype.functionptr().
  73. self.impl._llfnobjattrs_ = {'_name': name}
  74. return rtyper.getannmixlevel().delayedfunction(
  75. self.impl, self.s_func.args_s, self.s_func.s_result)
  76. else:
  77. fakeimpl = self.fakeimpl or self.s_func.const
  78. FT = FuncType(args_ll, ll_result)
  79. return functionptr(
  80. FT, name, _external_name=name, _callable=fakeimpl)
  81. class ExtFuncEntry(ExtRegistryEntry):
  82. safe_not_sandboxed = False
  83. def compute_annotation(self):
  84. s_result = SomeExternalFunction(
  85. self.name, self.signature_args, self.signature_result)
  86. if (self.bookkeeper.annotator.translator.config.translation.sandbox
  87. and not self.safe_not_sandboxed):
  88. s_result.needs_sandboxing = True
  89. return s_result
  90. def register_external(function, args, result=None, export_name=None,
  91. llimpl=None, llfakeimpl=None, sandboxsafe=False):
  92. """
  93. function: the RPython function that will be rendered as an external function (e.g.: math.floor)
  94. args: a list containing the annotation of the arguments
  95. result: surprisingly enough, the annotation of the result
  96. export_name: the name of the function as it will be seen by the backends
  97. llimpl: optional; if provided, this RPython function is called instead of the target function
  98. llfakeimpl: optional; if provided, called by the llinterpreter
  99. sandboxsafe: use True if the function performs no I/O (safe for --sandbox)
  100. """
  101. if export_name is None:
  102. export_name = function.__name__
  103. params_s = [annotation(arg) for arg in args]
  104. s_result = annotation(result)
  105. class FunEntry(ExtFuncEntry):
  106. _about_ = function
  107. safe_not_sandboxed = sandboxsafe
  108. signature_args = params_s
  109. signature_result = s_result
  110. name = export_name
  111. if llimpl:
  112. lltypeimpl = staticmethod(llimpl)
  113. if llfakeimpl:
  114. lltypefakeimpl = staticmethod(llfakeimpl)
  115. def is_external(func):
  116. if hasattr(func, 'value'):
  117. func = func.value
  118. if hasattr(func, '_external_name'):
  119. return True
  120. return False