Exemple #1
0
    def decorator(func):
        def wrapped(*args):
            if objectmodel.we_are_translated():
                val = func(*args)
            else:
                val = default
            return val

        wrapped.__name__ = func.__name__
        return jit.dont_look_inside(wrapped)
    def test_access_directly_stop_at_dont_look_inside(self):
        from rpython.rlib.jit import dont_look_inside

        class A:
            _virtualizable_ = ["x"]

        def h(a):
            g(a)

        h = dont_look_inside(h)

        def g(a):
            a.x = 2
            h(a)

        def f():
            a = A()
            a = hint(a, access_directly=True)
            a.x = 1
            g(a)

        t, typer, graph = self.gengraph(f, [])
        deref = typer.type_system.deref

        desc = typer.annotator.bookkeeper.getdesc(g)
        g_graphs = desc._cache.items()
        assert len(g_graphs) == 2
        g_graphs.sort()

        assert g_graphs[0][0] is None  # default
        g_graph = g_graphs[0][1]
        g_graph_directly = g_graphs[1][1]

        f_graph = t._graphof(f)
        h_graph = t._graphof(h)  # 1 graph!

        def get_direct_call_graph(graph):
            for block, op in graph.iterblockops():
                if op.opname == "direct_call":
                    return deref(op.args[0].value).graph
            return None

        assert get_direct_call_graph(f_graph) is g_graph_directly
        assert get_direct_call_graph(g_graph) is h_graph
        assert get_direct_call_graph(g_graph_directly) is h_graph
        assert get_direct_call_graph(h_graph) is g_graph
    def test_access_directly_stop_at_dont_look_inside(self):
        from rpython.rlib.jit import dont_look_inside

        class A:
            _virtualizable2_ = ['x']

        def h(a):
            g(a)
        h = dont_look_inside(h)        
        
        def g(a):
            a.x = 2
            h(a)

        def f():
            a = A()
            a = hint(a, access_directly=True)
            a.x = 1
            g(a)

        t, typer, graph = self.gengraph(f, [])
        deref = typer.type_system_deref
        
        desc = typer.annotator.bookkeeper.getdesc(g)
        g_graphs = desc._cache.items()
        assert len(g_graphs) == 2
        g_graphs.sort()

        assert g_graphs[0][0] is None # default
        g_graph = g_graphs[0][1]
        g_graph_directly = g_graphs[1][1]

        f_graph = t._graphof(f)
        h_graph = t._graphof(h) # 1 graph!

        def get_direct_call_graph(graph):
            for block, op in graph.iterblockops():
                if op.opname == 'direct_call':
                    return deref(op.args[0].value).graph
            return None

        assert get_direct_call_graph(f_graph) is g_graph_directly
        assert get_direct_call_graph(g_graph) is h_graph
        assert get_direct_call_graph(g_graph_directly) is h_graph
        assert get_direct_call_graph(h_graph) is g_graph
Exemple #4
0
def llexternal(name, args, result, _callable=None,
               compilation_info=ExternalCompilationInfo(),
               sandboxsafe=False, releasegil='auto',
               _nowrapper=False, calling_conv='c',
               elidable_function=False, macro=None,
               random_effects_on_gcobjs='auto'):
    """Build an external function that will invoke the C function 'name'
    with the given 'args' types and 'result' type.

    You get by default a wrapper that casts between number types as needed
    to match the arguments.  You can also pass an RPython string when a
    CCHARP argument is expected, and the C function receives a 'const char*'
    pointing to a read-only null-terminated character of arrays, as usual
    for C.

    The C function can have callbacks, but they must be specified explicitly
    as constant RPython functions.  We don't support yet C functions that
    invoke callbacks passed otherwise (e.g. set by a previous C call).

    releasegil: whether it's ok to release the GIL around the call.
                Default is yes, unless sandboxsafe is set, in which case
                we consider that the function is really short-running and
                don't bother releasing the GIL.  An explicit True or False
                overrides this logic.
    """
    if _callable is not None:
        assert callable(_callable)
    ext_type = lltype.FuncType(args, result)
    if _callable is None:
        if macro is not None:
            if macro is True:
                macro = name
            _callable = generate_macro_wrapper(
                name, macro, ext_type, compilation_info)
        else:
            _callable = ll2ctypes.LL2CtypesCallable(ext_type, calling_conv)
    if elidable_function:
        _callable._elidable_function_ = True
    kwds = {}

    has_callback = False
    for ARG in args:
        if _isfunctype(ARG):
            has_callback = True
    if has_callback:
        kwds['_callbacks'] = callbackholder = CallbackHolder()
    else:
        callbackholder = None

    if releasegil in (False, True):
        # invoke the around-handlers, which release the GIL, if and only if
        # the C function is thread-safe.
        invoke_around_handlers = releasegil
    else:
        # default case:
        # invoke the around-handlers only for "not too small" external calls;
        # sandboxsafe is a hint for "too-small-ness" (e.g. math functions).
        # Also, _nowrapper functions cannot release the GIL, by default.
        invoke_around_handlers = not sandboxsafe and not _nowrapper

    if random_effects_on_gcobjs not in (False, True):
        random_effects_on_gcobjs = (
            invoke_around_handlers or   # because it can release the GIL
            has_callback)               # because the callback can do it
    assert not (elidable_function and random_effects_on_gcobjs)

    funcptr = lltype.functionptr(ext_type, name, external='C',
                                 compilation_info=compilation_info,
                                 _callable=_callable,
                                 _safe_not_sandboxed=sandboxsafe,
                                 _debugexc=True, # on top of llinterp
                                 canraise=False,
                                 random_effects_on_gcobjs=
                                     random_effects_on_gcobjs,
                                 calling_conv=calling_conv,
                                 **kwds)
    if isinstance(_callable, ll2ctypes.LL2CtypesCallable):
        _callable.funcptr = funcptr

    if _nowrapper:
        return funcptr


    if invoke_around_handlers:
        # The around-handlers are releasing the GIL in a threaded pypy.
        # We need tons of care to ensure that no GC operation and no
        # exception checking occurs while the GIL is released.

        # The actual call is done by this small piece of non-inlinable
        # generated code in order to avoid seeing any GC pointer:
        # neither '*args' nor the GC objects originally passed in as
        # argument to wrapper(), if any (e.g. RPython strings).

        argnames = ', '.join(['a%d' % i for i in range(len(args))])
        source = py.code.Source("""
            def call_external_function(%(argnames)s):
                before = aroundstate.before
                if before: before()
                # NB. it is essential that no exception checking occurs here!
                res = funcptr(%(argnames)s)
                after = aroundstate.after
                if after: after()
                return res
        """ % locals())
        miniglobals = {'aroundstate': aroundstate,
                       'funcptr':     funcptr,
                       '__name__':    __name__, # for module name propagation
                       }
        exec source.compile() in miniglobals
        call_external_function = miniglobals['call_external_function']
        call_external_function._dont_inline_ = True
        call_external_function._annspecialcase_ = 'specialize:ll'
        call_external_function._gctransformer_hint_close_stack_ = True
        call_external_function._call_aroundstate_target_ = funcptr
        call_external_function = func_with_new_name(call_external_function,
                                                    'ccall_' + name)
        # don't inline, as a hack to guarantee that no GC pointer is alive
        # anywhere in call_external_function
    else:
        # if we don't have to invoke the aroundstate, we can just call
        # the low-level function pointer carelessly
        call_external_function = funcptr

    unrolling_arg_tps = unrolling_iterable(enumerate(args))
    def wrapper(*args):
        real_args = ()
        to_free = ()
        for i, TARGET in unrolling_arg_tps:
            arg = args[i]
            freeme = None
            if TARGET == CCHARP:
                if arg is None:
                    arg = lltype.nullptr(CCHARP.TO)   # None => (char*)NULL
                    freeme = arg
                elif isinstance(arg, str):
                    arg = str2charp(arg)
                    # XXX leaks if a str2charp() fails with MemoryError
                    # and was not the first in this function
                    freeme = arg
            elif TARGET == CWCHARP:
                if arg is None:
                    arg = lltype.nullptr(CWCHARP.TO)   # None => (wchar_t*)NULL
                    freeme = arg
                elif isinstance(arg, unicode):
                    arg = unicode2wcharp(arg)
                    # XXX leaks if a unicode2wcharp() fails with MemoryError
                    # and was not the first in this function
                    freeme = arg
            elif TARGET is VOIDP:
                if arg is None:
                    arg = lltype.nullptr(VOIDP.TO)
                elif isinstance(arg, str):
                    arg = str2charp(arg)
                    freeme = arg
                elif isinstance(arg, unicode):
                    arg = unicode2wcharp(arg)
                    freeme = arg
            elif _isfunctype(TARGET) and not _isllptr(arg):
                # XXX pass additional arguments
                if invoke_around_handlers:
                    arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
                                                             callbackholder,
                                                             aroundstate))
                else:
                    arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
                                                             callbackholder))
            else:
                SOURCE = lltype.typeOf(arg)
                if SOURCE != TARGET:
                    if TARGET is lltype.Float:
                        arg = float(arg)
                    elif ((isinstance(SOURCE, lltype.Number)
                           or SOURCE is lltype.Bool)
                      and (isinstance(TARGET, lltype.Number)
                           or TARGET is lltype.Bool)):
                        arg = cast(TARGET, arg)
            real_args = real_args + (arg,)
            to_free = to_free + (freeme,)
        res = call_external_function(*real_args)
        for i, TARGET in unrolling_arg_tps:
            if to_free[i]:
                lltype.free(to_free[i], flavor='raw')
        if rarithmetic.r_int is not r_int:
            if result is INT:
                return cast(lltype.Signed, res)
            elif result is UINT:
                return cast(lltype.Unsigned, res)
        return res
    wrapper._annspecialcase_ = 'specialize:ll'
    wrapper._always_inline_ = 'try'
    # for debugging, stick ll func ptr to that
    wrapper._ptr = funcptr
    wrapper = func_with_new_name(wrapper, name)

    if calling_conv != "c":
        wrapper = jit.dont_look_inside(wrapper)

    return wrapper
Exemple #5
0
            err = rwin32._GetLastError()
            # careful, setraw() overwrites GetLastError.
            # We must read it first, before the errno handling.
            rthread.tlfield_rpy_lasterror.setraw(err)
        elif save_err & rffi.RFFI_SAVE_WSALASTERROR:
            from rpython.rlib import rthread, _rsocket_rffi
            err = _rsocket_rffi._WSAGetLastError()
            rthread.tlfield_rpy_lasterror.setraw(err)
    if save_err & rffi.RFFI_SAVE_ERRNO:
        from rpython.rlib import rthread
        rthread.tlfield_rpy_errno.setraw(_get_errno())


if os.name == 'nt':
    is_valid_fd = jit.dont_look_inside(rffi.llexternal(
        "_PyVerify_fd", [rffi.INT], rffi.INT,
        compilation_info=errno_eci,
        ))
    def validate_fd(fd):
        if not is_valid_fd(fd):
            from errno import EBADF
            raise OSError(EBADF, 'Bad file descriptor')
else:
    def is_valid_fd(fd):
        return 1

    def validate_fd(fd):
        pass

def closerange(fd_low, fd_high):
    # this behaves like os.closerange() from Python 2.6.
    for fd in xrange(fd_low, fd_high):
Exemple #6
0
def alterMethods(cls):
    """
    Alter Monte methods on behalf of AutoHelp.

    Return the signatures of the altered methods.

    NOT_RPYTHON
    """

    atoms = {}
    imports = set()
    execNames = {"Refused": Refused}

    def nextName(nameIndex=[0]):
        name = "_%d" % nameIndex[0]
        nameIndex[0] += 1
        return name

    def namedLiteral(lit):
        name = nextName()
        execNames[name] = lit
        return name

    dispatchClauses = []
    methods = harvestMethods(cls)
    for attr, (f, verb, args, kwargs, rv) in methods.iteritems():
        assignments = []
        if isStarArgs(args):
            atomTest = "atom.verb == %r" % verb
            call = "self.%s(args)" % attr
        else:
            atom = getAtom(verb, len(args))
            atomName = namedLiteral(atom)
            ds = f.__doc__
            if ds is not None:
                ds = ds.decode("utf-8")
            atoms[atom] = ds
            atomTest = "atom is %s" % atomName
            argNames = []
            for i, arg in enumerate(args):
                argName = nextName()
                argNames.append(argName)
                assignments.append("%s = args[%d]" % (argName, i))
                if arg != "Any":
                    unwrapperModule = wrappers[arg]
                    pred = "is" + arg
                    imports.add("from %s import %s" % (unwrapperModule, pred))
                    atomTest += " and %s(args[%d])" % (pred, i)
                    unwrapper = "unwrap" + arg
                    imports.add("from %s import %s" % (unwrapperModule,
                        unwrapper))
                    assignments.append("%s = %s(%s)" % (argName, unwrapper,
                        argName))
                else:
                    imports.add("from typhon.objects.refs import resolution")
                    assignments.append("%s = resolution(%s)" % (argName,
                                                                argName))
            for k, v in kwargs.iteritems():
                # Look up the default value. We're going to pop this in and
                # use None as a sentinel value in .extractStringKey(). ~ C.
                default = getKwargDefault(f, k)
                defaultName = namedLiteral(default)
                kwargName = nextName()
                argNames.append("%s=%s" % (k, kwargName))
                assignments.append("%s = namedArgs.extractStringKey(%r, None)"
                        % (kwargName, k.decode("utf-8")))
                # If the kwarg is None, then it wasn't passed in; use the
                # default. Otherwise, invoke the unwrapper if one exists.
                assignments.append("if %s is None: %s = %s"
                        % (kwargName, kwargName, defaultName))
                if v != "Any":
                    unwrapperModule = wrappers[v]
                    unwrapper = "unwrap" + v
                    imports.add("from %s import %s"
                            % (unwrapperModule, unwrapper))
                    assignments.append("else: %s = %s(%s)"
                            % (kwargName, unwrapper, kwargName))
            call = "self.%s(%s)" % (attr, ",".join(argNames))
        retvals = []
        if rv == "Any":
            # No wrapping.
            retvals.append("return rv")
        elif rv == "Void":
            # Enforced correctness. Disobedience will not be tolerated.
            retvals.append("assert rv is None, 'habanero'")
            retvals.append("from typhon.objects.constants import NullObject")
            retvals.append("return NullObject")
        else:
            wrapperModule = wrappers[rv]
            wrapper = "wrap" + rv
            imports.add("from %s import %s" % (wrapperModule, wrapper))
            retvals.append("return %s(rv)" % wrapper)
        # We need to use newlines for the assignments since kwarg assignments
        # are conditional.
        dispatchClauses.append("""
 if %s:
  %s
  rv = %s
  %s
""" % (atomTest, "\n  ".join(assignments), call, ";".join(retvals)))
        setattr(cls, attr, f)
    # Temporary. Soon, all classes shall receive AutoHelp, and no class will
    # have a handwritten recv().
    if dispatchClauses:
        exec py.code.Source("""
def recvNamed(self, atom, args, namedArgs):
 %s
 %s
 rv = self.mirandaMethods(atom, args, namedArgs)
 if rv is None:
  raise Refused(self, atom, args)
 else:
  return rv
""" % (";".join(imports), "\n".join(dispatchClauses))).compile() in execNames
        recvNamed = execNames["recvNamed"]
        # This is the place for fixing the "too many constants" JIT
        # translation error. If there's too many dispatch clauses in
        # .recvNamed(), then the JIT transformation fails because too many
        # constant values are being mixed in. This number is determined
        # empirically; uncomment the next line to take measurements:
        # print "Made %d dispatch clauses for %r" % (len(dispatchClauses), cls)
        # Here's a worksheet. Please keep it up-to-date when altering this
        # section of code. ~ C.
        # * At last run, the longest working list of clauses had length 40
        # * And the shortest list which broke translation had length 46
        # * Assuming that each clause has a constant integral number of
        #   constants, the number of constants per clause is 6
        # * The maximum number of allowed constants is 255
        # * Therefore, the longest working list can have maximum length 42
        if len(dispatchClauses) >= 42:
            # Too many constants; forbid the JIT from recursing into us.
            recvNamed = dont_look_inside(recvNamed)
        cls.recvNamed = recvNamed

    return atoms
Exemple #7
0
def llexternal(name, args, result, _callable=None,
               compilation_info=ExternalCompilationInfo(),
               sandboxsafe=False, releasegil='auto',
               _nowrapper=False, calling_conv='c',
               elidable_function=False, macro=None,
               random_effects_on_gcobjs='auto',
               save_err=RFFI_ERR_NONE):
    """Build an external function that will invoke the C function 'name'
    with the given 'args' types and 'result' type.

    You get by default a wrapper that casts between number types as needed
    to match the arguments.  You can also pass an RPython string when a
    CCHARP argument is expected, and the C function receives a 'const char*'
    pointing to a read-only null-terminated character of arrays, as usual
    for C.

    The C function can have callbacks, but they must be specified explicitly
    as constant RPython functions.  We don't support yet C functions that
    invoke callbacks passed otherwise (e.g. set by a previous C call).

    releasegil: whether it's ok to release the GIL around the call.
                Default is yes, unless sandboxsafe is set, in which case
                we consider that the function is really short-running and
                don't bother releasing the GIL.  An explicit True or False
                overrides this logic.
    """
    if _callable is not None:
        assert callable(_callable)
    ext_type = lltype.FuncType(args, result)
    if _callable is None:
        if macro is not None:
            if macro is True:
                macro = name
            _callable = generate_macro_wrapper(
                name, macro, ext_type, compilation_info)
        else:
            _callable = ll2ctypes.LL2CtypesCallable(ext_type, calling_conv)
    else:
        assert macro is None, "'macro' is useless if you specify '_callable'"
    if elidable_function:
        _callable._elidable_function_ = True
    kwds = {}

    has_callback = False
    for ARG in args:
        if _isfunctype(ARG):
            has_callback = True
    if has_callback:
        kwds['_callbacks'] = callbackholder = CallbackHolder()
    else:
        callbackholder = None

    if releasegil in (False, True):
        # invoke the around-handlers, which release the GIL, if and only if
        # the C function is thread-safe.
        invoke_around_handlers = releasegil
    else:
        # default case:
        # invoke the around-handlers only for "not too small" external calls;
        # sandboxsafe is a hint for "too-small-ness" (e.g. math functions).
        # Also, _nowrapper functions cannot release the GIL, by default.
        invoke_around_handlers = not sandboxsafe and not _nowrapper

    if random_effects_on_gcobjs not in (False, True):
        random_effects_on_gcobjs = (
            invoke_around_handlers or   # because it can release the GIL
            has_callback)               # because the callback can do it
    assert not (elidable_function and random_effects_on_gcobjs)

    funcptr = lltype.functionptr(ext_type, name, external='C',
                                 compilation_info=compilation_info,
                                 _callable=_callable,
                                 _safe_not_sandboxed=sandboxsafe,
                                 _debugexc=True, # on top of llinterp
                                 canraise=False,
                                 random_effects_on_gcobjs=
                                     random_effects_on_gcobjs,
                                 calling_conv=calling_conv,
                                 **kwds)
    if isinstance(_callable, ll2ctypes.LL2CtypesCallable):
        _callable.funcptr = funcptr

    if _nowrapper:
        assert save_err == RFFI_ERR_NONE
        return funcptr

    if invoke_around_handlers:
        # The around-handlers are releasing the GIL in a threaded pypy.
        # We need tons of care to ensure that no GC operation and no
        # exception checking occurs while the GIL is released.

        # The actual call is done by this small piece of non-inlinable
        # generated code in order to avoid seeing any GC pointer:
        # neither '*args' nor the GC objects originally passed in as
        # argument to wrapper(), if any (e.g. RPython strings).

        argnames = ', '.join(['a%d' % i for i in range(len(args))])
        source = py.code.Source("""
            from rpython.rlib import rgil
            def call_external_function(%(argnames)s):
                rgil.release()
                # NB. it is essential that no exception checking occurs here!
                if %(save_err)d:
                    from rpython.rlib import rposix
                    rposix._errno_before(%(save_err)d)
                res = funcptr(%(argnames)s)
                if %(save_err)d:
                    from rpython.rlib import rposix
                    rposix._errno_after(%(save_err)d)
                rgil.acquire()
                return res
        """ % locals())
        miniglobals = {'funcptr':     funcptr,
                       '__name__':    __name__, # for module name propagation
                       }
        exec source.compile() in miniglobals
        call_external_function = miniglobals['call_external_function']
        call_external_function._dont_inline_ = True
        call_external_function._annspecialcase_ = 'specialize:ll'
        call_external_function._gctransformer_hint_close_stack_ = True
        #
        # '_call_aroundstate_target_' is used by the JIT to generate a
        # CALL_RELEASE_GIL directly to 'funcptr'.  This doesn't work if
        # 'funcptr' might be a C macro, though.
        if macro is None:
            call_external_function._call_aroundstate_target_ = funcptr, save_err
        #
        call_external_function = func_with_new_name(call_external_function,
                                                    'ccall_' + name)
        # don't inline, as a hack to guarantee that no GC pointer is alive
        # anywhere in call_external_function
    else:
        # if we don't have to invoke the GIL handling, we can just call
        # the low-level function pointer carelessly
        if macro is None and save_err == RFFI_ERR_NONE:
            call_external_function = funcptr
        else:
            # ...well, unless it's a macro, in which case we still have
            # to hide it from the JIT...
            argnames = ', '.join(['a%d' % i for i in range(len(args))])
            source = py.code.Source("""
                def call_external_function(%(argnames)s):
                    if %(save_err)d:
                        from rpython.rlib import rposix
                        rposix._errno_before(%(save_err)d)
                    res = funcptr(%(argnames)s)
                    if %(save_err)d:
                        from rpython.rlib import rposix
                        rposix._errno_after(%(save_err)d)
                    return res
            """ % locals())
            miniglobals = {'funcptr':     funcptr,
                           '__name__':    __name__,
                           }
            exec source.compile() in miniglobals
            call_external_function = miniglobals['call_external_function']
            call_external_function = func_with_new_name(call_external_function,
                                                        'ccall_' + name)
            call_external_function = jit.dont_look_inside(
                call_external_function)

    unrolling_arg_tps = unrolling_iterable(enumerate(args))
    def wrapper(*args):
        real_args = ()
        to_free = ()
        for i, TARGET in unrolling_arg_tps:
            arg = args[i]
            freeme = None
            if TARGET == CCHARP:
                if arg is None:
                    arg = lltype.nullptr(CCHARP.TO)   # None => (char*)NULL
                    freeme = arg
                elif isinstance(arg, str):
                    arg = str2charp(arg)
                    # XXX leaks if a str2charp() fails with MemoryError
                    # and was not the first in this function
                    freeme = arg
            elif TARGET == CWCHARP:
                if arg is None:
                    arg = lltype.nullptr(CWCHARP.TO)   # None => (wchar_t*)NULL
                    freeme = arg
                elif isinstance(arg, unicode):
                    arg = unicode2wcharp(arg)
                    # XXX leaks if a unicode2wcharp() fails with MemoryError
                    # and was not the first in this function
                    freeme = arg
            elif TARGET is VOIDP:
                if arg is None:
                    arg = lltype.nullptr(VOIDP.TO)
                elif isinstance(arg, str):
                    arg = str2charp(arg)
                    freeme = arg
                elif isinstance(arg, unicode):
                    arg = unicode2wcharp(arg)
                    freeme = arg
            elif _isfunctype(TARGET) and not _isllptr(arg):
                # XXX pass additional arguments
                use_gil = invoke_around_handlers
                arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
                                                         callbackholder,
                                                         use_gil))
            else:
                SOURCE = lltype.typeOf(arg)
                if SOURCE != TARGET:
                    if TARGET is lltype.Float:
                        arg = float(arg)
                    elif ((isinstance(SOURCE, lltype.Number)
                           or SOURCE is lltype.Bool)
                      and (isinstance(TARGET, lltype.Number)
                           or TARGET is lltype.Bool)):
                        arg = cast(TARGET, arg)
            real_args = real_args + (arg,)
            to_free = to_free + (freeme,)
        res = call_external_function(*real_args)
        for i, TARGET in unrolling_arg_tps:
            if to_free[i]:
                lltype.free(to_free[i], flavor='raw')
        if rarithmetic.r_int is not r_int:
            if result is INT:
                return cast(lltype.Signed, res)
            elif result is UINT:
                return cast(lltype.Unsigned, res)
        return res
    wrapper._annspecialcase_ = 'specialize:ll'
    wrapper._always_inline_ = 'try'
    # for debugging, stick ll func ptr to that
    wrapper._ptr = funcptr
    wrapper = func_with_new_name(wrapper, name)

    if calling_conv != "c":
        wrapper = jit.dont_look_inside(wrapper)

    return wrapper
Exemple #8
0
def alterMethods(cls):
    """
    Alter Monte methods on behalf of AutoHelp.

    Return the signatures of the altered methods.

    NOT_RPYTHON
    """

    atoms = []
    imports = set()
    execNames = {"Refused": Refused}

    def nextName(nameIndex=[0]):
        name = "_%d" % nameIndex[0]
        nameIndex[0] += 1
        return name

    def namedLiteral(lit):
        name = nextName()
        execNames[name] = lit
        return name

    dispatchClauses = []
    methods = harvestMethods(cls)
    for attr, (f, verb, args, kwargs, rv) in methods.iteritems():
        assignments = []
        if isStarArgs(args):
            atomTest = "atom.verb == %r" % verb
            call = "self.%s(args)" % attr
        else:
            atom = getAtom(verb, len(args))
            atomName = namedLiteral(atom)
            atoms.append(atom)
            atomTest = "atom is %s" % atomName
            argNames = []
            for i, arg in enumerate(args):
                argName = nextName()
                argNames.append(argName)
                assignments.append("%s = args[%d]" % (argName, i))
                if arg != "Any":
                    unwrapperModule = wrappers[arg]
                    pred = "is" + arg
                    imports.add("from %s import %s" % (unwrapperModule, pred))
                    atomTest += " and %s(args[%d])" % (pred, i)
                    unwrapper = "unwrap" + arg
                    imports.add("from %s import %s" % (unwrapperModule,
                        unwrapper))
                    assignments.append("%s = %s(%s)" % (argName, unwrapper,
                        argName))
                else:
                    imports.add("from typhon.objects.refs import resolution")
                    assignments.append("%s = resolution(%s)" % (argName,
                                                                argName))
            for k, v in kwargs.iteritems():
                # Look up the default value. We're going to pop this in and
                # use None as a sentinel value in .extractStringKey(). ~ C.
                default = getKwargDefault(f, k)
                defaultName = namedLiteral(default)
                kwargName = nextName()
                argNames.append("%s=%s" % (k, kwargName))
                assignments.append("%s = namedArgs.extractStringKey(%r, None)"
                        % (kwargName, k.decode("utf-8")))
                # If the kwarg is None, then it wasn't passed in; use the
                # default. Otherwise, invoke the unwrapper if one exists.
                assignments.append("if %s is None: %s = %s"
                        % (kwargName, kwargName, defaultName))
                if v != "Any":
                    unwrapperModule = wrappers[v]
                    unwrapper = "unwrap" + v
                    imports.add("from %s import %s"
                            % (unwrapperModule, unwrapper))
                    assignments.append("else: %s = %s(%s)"
                            % (kwargName, unwrapper, kwargName))
            call = "self.%s(%s)" % (attr, ",".join(argNames))
        retvals = []
        if rv == "Any":
            # No wrapping.
            retvals.append("return rv")
        elif rv == "Void":
            # Enforced correctness. Disobedience will not be tolerated.
            retvals.append("assert rv is None, 'habanero'")
            retvals.append("from typhon.objects.constants import NullObject")
            retvals.append("return NullObject")
        else:
            wrapperModule = wrappers[rv]
            wrapper = "wrap" + rv
            imports.add("from %s import %s" % (wrapperModule, wrapper))
            retvals.append("return %s(rv)" % wrapper)
        # We need to use newlines for the assignments since kwarg assignments
        # are conditional.
        dispatchClauses.append("""
 if %s:
  %s
  rv = %s
  %s
""" % (atomTest, "\n  ".join(assignments), call, ";".join(retvals)))
        setattr(cls, attr, f)
    # Temporary. Soon, all classes shall receive AutoHelp, and no class will
    # have a handwritten recv().
    if dispatchClauses:
        exec py.code.Source("""
def recvNamed(self, atom, args, namedArgs):
 %s
 %s
 rv = self.mirandaMethods(atom, args, namedArgs)
 if rv is None:
  raise Refused(self, atom, args)
 else:
  return rv
""" % (";".join(imports), "\n".join(dispatchClauses))).compile() in execNames
        recvNamed = execNames["recvNamed"]
        # This is the place for fixing the "too many constants" JIT
        # translation error. If there's too many dispatch clauses in
        # .recvNamed(), then the JIT transformation fails because too many
        # constant values are being mixed in. This number is determined
        # empirically; uncomment the next line to take measurements:
        # print "Made %d dispatch clauses for %r" % (len(dispatchClauses), cls)
        # Here's a worksheet. Please keep it up-to-date when altering this
        # section of code. ~ C.
        # * At last run, the longest working list of clauses had length 40
        # * And the shortest list which broke translation had length 46
        # * Assuming that each clause has a constant integral number of
        #   constants, the number of constants per clause is 6
        # * The maximum number of allowed constants is 255
        # * Therefore, the longest working list can have maximum length 42
        if len(dispatchClauses) >= 42:
            # Too many constants; forbid the JIT from recursing into us.
            recvNamed = dont_look_inside(recvNamed)
        cls.recvNamed = recvNamed

    return atoms
Exemple #9
0
                rthread.tlfield_alt_lasterror.setraw(err)
            else:
                rthread.tlfield_rpy_lasterror.setraw(err)
    if save_err & rffi.RFFI_SAVE_ERRNO:
        from rpython.rlib import rthread
        if save_err & rffi.RFFI_ALT_ERRNO:
            rthread.tlfield_alt_errno.setraw(_get_errno())
        else:
            rthread.tlfield_rpy_errno.setraw(_get_errno())


if os.name == 'nt':
    is_valid_fd = jit.dont_look_inside(
        rffi.llexternal(
            "_PyVerify_fd",
            [rffi.INT],
            rffi.INT,
            compilation_info=errno_eci,
        ))

    def validate_fd(fd):
        if not is_valid_fd(fd):
            from errno import EBADF
            raise OSError(EBADF, 'Bad file descriptor')
else:

    def is_valid_fd(fd):
        return 1

    def validate_fd(fd):
        pass
Exemple #10
0
def llexternal(name, args, result, _callable=None,
               compilation_info=ExternalCompilationInfo(),
               sandboxsafe=False, releasegil='auto',
               _nowrapper=False, calling_conv=None,
               elidable_function=False, macro=None,
               random_effects_on_gcobjs='auto',
               save_err=RFFI_ERR_NONE):
    """Build an external function that will invoke the C function 'name'
    with the given 'args' types and 'result' type.

    You get by default a wrapper that casts between number types as needed
    to match the arguments.  You can also pass an RPython string when a
    CCHARP argument is expected, and the C function receives a 'const char*'
    pointing to a read-only null-terminated character of arrays, as usual
    for C.

    The C function can have callbacks, but they must be specified explicitly
    as constant RPython functions.  We don't support yet C functions that
    invoke callbacks passed otherwise (e.g. set by a previous C call).

    releasegil: whether it's ok to release the GIL around the call.
                Default is yes, unless sandboxsafe is set, in which case
                we consider that the function is really short-running and
                don't bother releasing the GIL.  An explicit True or False
                overrides this logic.

    calling_conv: if 'unknown' or 'win', the C function is not directly seen
                  by the JIT.  If 'c', it can be seen (depending on
                  releasegil=False).  For tests only, or if _nowrapper,
                  it defaults to 'c'.
    """
    if calling_conv is None:
        if sys.platform == 'win32' and not _nowrapper:
            calling_conv = 'unknown'
        else:
            calling_conv = 'c'
    if _callable is not None:
        assert callable(_callable)
    ext_type = lltype.FuncType(args, result)
    if _callable is None:
        if macro is not None:
            if macro is True:
                macro = name
            _callable = generate_macro_wrapper(
                name, macro, ext_type, compilation_info)
        else:
            _callable = ll2ctypes.LL2CtypesCallable(ext_type,
                'c' if calling_conv == 'unknown' else calling_conv)
    else:
        assert macro is None, "'macro' is useless if you specify '_callable'"
    if elidable_function:
        _callable._elidable_function_ = True
    kwds = {}

    has_callback = False
    for ARG in args:
        if _isfunctype(ARG):
            has_callback = True
    if has_callback:
        kwds['_callbacks'] = callbackholder = CallbackHolder()
    else:
        callbackholder = None

    if releasegil in (False, True):
        # invoke the around-handlers, which release the GIL, if and only if
        # the C function is thread-safe.
        invoke_around_handlers = releasegil
    else:
        # default case:
        # invoke the around-handlers only for "not too small" external calls;
        # sandboxsafe is a hint for "too-small-ness" (e.g. math functions).
        # Also, _nowrapper functions cannot release the GIL, by default.
        invoke_around_handlers = not sandboxsafe and not _nowrapper

    if random_effects_on_gcobjs not in (False, True):
        random_effects_on_gcobjs = (
            invoke_around_handlers or   # because it can release the GIL
            has_callback)               # because the callback can do it
    assert not (elidable_function and random_effects_on_gcobjs)

    funcptr = lltype.functionptr(ext_type, name, external='C',
                                 compilation_info=compilation_info,
                                 _callable=_callable,
                                 _safe_not_sandboxed=sandboxsafe,
                                 _debugexc=True, # on top of llinterp
                                 canraise=False,
                                 random_effects_on_gcobjs=
                                     random_effects_on_gcobjs,
                                 calling_conv=calling_conv,
                                 **kwds)
    if isinstance(_callable, ll2ctypes.LL2CtypesCallable):
        _callable.funcptr = funcptr

    if _nowrapper:
        assert save_err == RFFI_ERR_NONE
        return funcptr

    if invoke_around_handlers:
        # The around-handlers are releasing the GIL in a threaded pypy.
        # We need tons of care to ensure that no GC operation and no
        # exception checking occurs while the GIL is released.

        # The actual call is done by this small piece of non-inlinable
        # generated code in order to avoid seeing any GC pointer:
        # neither '*args' nor the GC objects originally passed in as
        # argument to wrapper(), if any (e.g. RPython strings).

        argnames = ', '.join(['a%d' % i for i in range(len(args))])
        source = py.code.Source("""
            from rpython.rlib import rgil
            def call_external_function(%(argnames)s):
                rgil.release()
                # NB. it is essential that no exception checking occurs here!
                if %(save_err)d:
                    from rpython.rlib import rposix
                    rposix._errno_before(%(save_err)d)
                res = funcptr(%(argnames)s)
                if %(save_err)d:
                    from rpython.rlib import rposix
                    rposix._errno_after(%(save_err)d)
                rgil.acquire()
                return res
        """ % locals())
        miniglobals = {'funcptr':     funcptr,
                       '__name__':    __name__, # for module name propagation
                       }
        exec source.compile() in miniglobals
        call_external_function = miniglobals['call_external_function']
        call_external_function._dont_inline_ = True
        call_external_function._annspecialcase_ = 'specialize:ll'
        call_external_function._gctransformer_hint_close_stack_ = True
        #
        # '_call_aroundstate_target_' is used by the JIT to generate a
        # CALL_RELEASE_GIL directly to 'funcptr'.  This doesn't work if
        # 'funcptr' might be a C macro, though.
        if macro is None:
            call_external_function._call_aroundstate_target_ = funcptr, save_err
        #
        call_external_function = func_with_new_name(call_external_function,
                                                    'ccall_' + name)
        # don't inline, as a hack to guarantee that no GC pointer is alive
        # anywhere in call_external_function
    else:
        # if we don't have to invoke the GIL handling, we can just call
        # the low-level function pointer carelessly
        # ...well, unless it's a macro, in which case we still have
        # to hide it from the JIT...
        need_wrapper = (macro is not None or save_err != RFFI_ERR_NONE)
        # ...and unless we're on Windows and the calling convention is
        # 'win' or 'unknown'
        if calling_conv != 'c':
            need_wrapper = True
        #
        if not need_wrapper:
            call_external_function = funcptr
        else:
            argnames = ', '.join(['a%d' % i for i in range(len(args))])
            source = py.code.Source("""
                def call_external_function(%(argnames)s):
                    if %(save_err)d:
                        from rpython.rlib import rposix
                        rposix._errno_before(%(save_err)d)
                    res = funcptr(%(argnames)s)
                    if %(save_err)d:
                        from rpython.rlib import rposix
                        rposix._errno_after(%(save_err)d)
                    return res
            """ % locals())
            miniglobals = {'funcptr':     funcptr,
                           '__name__':    __name__,
                           }
            exec source.compile() in miniglobals
            call_external_function = miniglobals['call_external_function']
            call_external_function = func_with_new_name(call_external_function,
                                                        'ccall_' + name)
            call_external_function = jit.dont_look_inside(
                call_external_function)

    def _oops():
        raise AssertionError("can't pass (any more) a unicode string"
                             " directly to a VOIDP argument")
    _oops._annspecialcase_ = 'specialize:memo'

    nb_args = len(args)
    unrolling_arg_tps = unrolling_iterable(enumerate(args))
    def wrapper(*args):
        assert len(args) == nb_args
        real_args = ()
        # XXX 'to_free' leaks if an allocation fails with MemoryError
        # and was not the first in this function
        to_free = ()
        for i, TARGET in unrolling_arg_tps:
            arg = args[i]
            if TARGET == CCHARP or TARGET is VOIDP:
                if arg is None:
                    arg = lltype.nullptr(CCHARP.TO)   # None => (char*)NULL
                    to_free = to_free + (arg, '\x04')
                elif isinstance(arg, str):
                    tup = get_nonmovingbuffer_final_null(arg)
                    to_free = to_free + tup
                    arg = tup[0]
                elif isinstance(arg, unicode):
                    _oops()
            elif TARGET == CWCHARP:
                if arg is None:
                    arg = lltype.nullptr(CWCHARP.TO)   # None => (wchar_t*)NULL
                    to_free = to_free + (arg,)
                elif isinstance(arg, unicode):
                    arg = unicode2wcharp(arg)
                    to_free = to_free + (arg,)
            elif _isfunctype(TARGET) and not _isllptr(arg):
                # XXX pass additional arguments
                use_gil = invoke_around_handlers
                arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
                                                         callbackholder,
                                                         use_gil))
            else:
                SOURCE = lltype.typeOf(arg)
                if SOURCE != TARGET:
                    if TARGET is lltype.Float:
                        arg = float(arg)
                    elif ((isinstance(SOURCE, lltype.Number)
                           or SOURCE is lltype.Bool)
                      and (isinstance(TARGET, lltype.Number)
                           or TARGET is lltype.Bool)):
                        arg = cast(TARGET, arg)
            real_args = real_args + (arg,)
        res = call_external_function(*real_args)
        for i, TARGET in unrolling_arg_tps:
            arg = args[i]
            if TARGET == CCHARP or TARGET is VOIDP:
                if arg is None:
                    to_free = to_free[2:]
                elif isinstance(arg, str):
                    free_nonmovingbuffer(arg, to_free[0], to_free[1])
                    to_free = to_free[2:]
            elif TARGET == CWCHARP:
                if arg is None:
                    to_free = to_free[1:]
                elif isinstance(arg, unicode):
                    free_wcharp(to_free[0])
                    to_free = to_free[1:]
        assert len(to_free) == 0
        if rarithmetic.r_int is not r_int:
            if result is INT:
                return cast(lltype.Signed, res)
            elif result is UINT:
                return cast(lltype.Unsigned, res)
        return res
    wrapper._annspecialcase_ = 'specialize:ll'
    wrapper._always_inline_ = 'try'
    # for debugging, stick ll func ptr to that
    wrapper._ptr = funcptr
    wrapper = func_with_new_name(wrapper, name)
    return wrapper