File: extfunc.py

package info (click to toggle)
pypy 5.6.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 97,040 kB
  • ctags: 185,069
  • sloc: python: 1,147,862; ansic: 49,642; cpp: 5,245; asm: 5,169; makefile: 529; sh: 481; xml: 232; lisp: 45
file content (137 lines) | stat: -rw-r--r-- 5,413 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from rpython.annotator.model import unionof, SomeObject
from rpython.annotator.signature import annotation, SignatureError
from rpython.rtyper.extregistry import ExtRegistryEntry, lookup
from rpython.rtyper.lltypesystem.lltype import (
    typeOf, FuncType, functionptr, _ptr, Void)
from rpython.rtyper.error import TyperError
from rpython.rtyper.rmodel import Repr

class SomeExternalFunction(SomeObject):
    def __init__(self, name, args_s, s_result):
        self.name = name
        self.args_s = args_s
        self.s_result = s_result

    def check_args(self, callspec):
        params_s = self.args_s
        args_s, kwargs = callspec.unpack()
        if kwargs:
            raise SignatureError(
                "External functions cannot be called with keyword arguments")
        if len(args_s) != len(params_s):
            raise SignatureError("Argument number mismatch")
        for i, s_param in enumerate(params_s):
            arg = unionof(args_s[i], s_param)
            if not s_param.contains(arg):
                raise SignatureError(
                    "In call to external function %r:\n"
                    "arg %d must be %s,\n"
                    "          got %s" % (
                        self.name, i + 1, s_param, args_s[i]))

    def call(self, callspec):
        self.check_args(callspec)
        return self.s_result

    def rtyper_makerepr(self, rtyper):
        if not self.is_constant():
            raise TyperError("Non-constant external function!")
        entry = lookup(self.const)
        impl = getattr(entry, 'lltypeimpl', None)
        fakeimpl = getattr(entry, 'lltypefakeimpl', None)
        return ExternalFunctionRepr(self, impl, fakeimpl)

    def rtyper_makekey(self):
        return self.__class__, self

class ExternalFunctionRepr(Repr):
    lowleveltype = Void

    def __init__(self, s_func, impl, fakeimpl):
        self.s_func = s_func
        self.impl = impl
        self.fakeimpl = fakeimpl

    def rtype_simple_call(self, hop):
        rtyper = hop.rtyper
        args_r = [rtyper.getrepr(s_arg) for s_arg in self.s_func.args_s]
        r_result = rtyper.getrepr(self.s_func.s_result)
        obj = self.get_funcptr(rtyper, args_r, r_result)
        hop2 = hop.copy()
        hop2.r_s_popfirstarg()
        vlist = [hop2.inputconst(typeOf(obj), obj)] + hop2.inputargs(*args_r)
        hop2.exception_is_here()
        return hop2.genop('direct_call', vlist, r_result)

    def get_funcptr(self, rtyper, args_r, r_result):
        from rpython.rtyper.rtyper import llinterp_backend
        args_ll = [r_arg.lowleveltype for r_arg in args_r]
        ll_result = r_result.lowleveltype
        name = self.s_func.name
        if self.fakeimpl and rtyper.backend is llinterp_backend:
            FT = FuncType(args_ll, ll_result)
            return functionptr(
                FT, name, _external_name=name, _callable=self.fakeimpl)
        elif self.impl:
            if isinstance(self.impl, _ptr):
                return self.impl
            else:
                # store some attributes to the 'impl' function, where
                # the eventual call to rtyper.getcallable() will find them
                # and transfer them to the final lltype.functionptr().
                self.impl._llfnobjattrs_ = {'_name': name}
                return rtyper.getannmixlevel().delayedfunction(
                    self.impl, self.s_func.args_s, self.s_func.s_result)
        else:
            fakeimpl = self.fakeimpl or self.s_func.const
            FT = FuncType(args_ll, ll_result)
            return functionptr(
                FT, name, _external_name=name, _callable=fakeimpl)


class ExtFuncEntry(ExtRegistryEntry):
    safe_not_sandboxed = False

    def compute_annotation(self):
        s_result = SomeExternalFunction(
            self.name, self.signature_args, self.signature_result)
        if (self.bookkeeper.annotator.translator.config.translation.sandbox
                and not self.safe_not_sandboxed):
            s_result.needs_sandboxing = True
        return s_result


def register_external(function, args, result=None, export_name=None,
                       llimpl=None, llfakeimpl=None, sandboxsafe=False):
    """
    function: the RPython function that will be rendered as an external function (e.g.: math.floor)
    args: a list containing the annotation of the arguments
    result: surprisingly enough, the annotation of the result
    export_name: the name of the function as it will be seen by the backends
    llimpl: optional; if provided, this RPython function is called instead of the target function
    llfakeimpl: optional; if provided, called by the llinterpreter
    sandboxsafe: use True if the function performs no I/O (safe for --sandbox)
    """

    if export_name is None:
        export_name = function.__name__
    params_s = [annotation(arg) for arg in args]
    s_result = annotation(result)

    class FunEntry(ExtFuncEntry):
        _about_ = function
        safe_not_sandboxed = sandboxsafe
        signature_args = params_s
        signature_result = s_result
        name = export_name
        if llimpl:
            lltypeimpl = staticmethod(llimpl)
        if llfakeimpl:
            lltypefakeimpl = staticmethod(llfakeimpl)

def is_external(func):
    if hasattr(func, 'value'):
        func = func.value
    if hasattr(func, '_external_name'):
        return True
    return False