Exemple #1
0
def _make_ll_dictnext(kind):
    # make three versions of the following function: keys, values, items
    @jit.look_inside_iff(lambda RETURNTYPE, iter: jit.isvirtual(iter)
                         and (iter.dict is None or
                              jit.isvirtual(iter.dict)))
    @jit.oopspec("dictiter.next%s(iter)" % kind)
    def ll_dictnext(RETURNTYPE, iter):
        # note that RETURNTYPE is None for keys and values
        dict = iter.dict
        if dict:
            entries = dict.entries
            index = iter.index
            assert index >= 0
            entries_len = len(entries)
            while index < entries_len:
                entry = entries[index]
                is_valid = entries.valid(index)
                index = index + 1
                if is_valid:
                    iter.index = index
                    if RETURNTYPE is lltype.Void:
                        return None
                    elif kind == 'items':
                        r = lltype.malloc(RETURNTYPE.TO)
                        r.item0 = recast(RETURNTYPE.TO.item0, entry.key)
                        r.item1 = recast(RETURNTYPE.TO.item1, entry.value)
                        return r
                    elif kind == 'keys':
                        return entry.key
                    elif kind == 'values':
                        return entry.value
            # clear the reference to the dict and prevent restarts
            iter.dict = lltype.nullptr(lltype.typeOf(iter).TO.dict.TO)
        raise StopIteration
    return ll_dictnext
Exemple #2
0
def scan_continuation(curr, prompt_tag, look_for=None):
    """
    Segment a continuation based on a given continuation-prompt-tag.
    The head of the continuation, up to and including the desired continuation
    prompt is reversed (in place), and the tail is returned un-altered.

    The hint value |look_for| is used to determine when the continuation being
    installed is a prefix of the extant continuation.
    In this case, installing the continuation is much simpler, as the expensive
    merge operation needed to find common substructure is the two continuation is
    not needed.
    """
    handlers = False
    xs = []
    while isinstance(curr, Cont):
        if curr is look_for:
            return None, handlers
        handlers |= isinstance(curr, DynamicWindValueCont)
        xs.append(curr)
        if isinstance(curr, Prompt) and curr.tag is prompt_tag:
            break
        curr = curr.prev
        if not jit.isvirtual(curr):
            return _scan_continuation(curr, prompt_tag, look_for, xs, handlers)
    return xs, handlers
Exemple #3
0
def w_dict_unrolling_heuristic(w_dct):
    """In which cases iterating over dict items can be unrolled.
    Note that w_dct is an instance of W_DictMultiObject, not necesarilly
    an actual dict
    """
    return jit.isvirtual(w_dct) or (jit.isconstant(w_dct) and
                                    w_dct.length() <= UNROLL_CUTOFF)
Exemple #4
0
def scan_continuation(curr, prompt_tag, look_for=None, escape=False):
    """
    Segment a continuation based on a given continuation-prompt-tag.
    The head of the continuation, up to and including the desired continuation
    prompt is reversed (in place), and the tail is returned un-altered.

    The hint value |look_for| is used to determine when the continuation being
    installed is a prefix of the extant continuation.
    In this case, installing the continuation is much simpler, as the expensive
    merge operation needed to find common substructure is the two continuation is
    not needed.
    """
    handlers = False
    xs = []
    while isinstance(curr, Cont):
        if curr is look_for:
            return None, handlers
        handlers |= isinstance(curr, DynamicWindValueCont)
        xs.append(curr)
        if isinstance(curr, Prompt) and curr.tag is prompt_tag:
            break
        curr = curr.prev
        if not escape and not jit.isvirtual(curr):
            return _scan_continuation(curr, prompt_tag, look_for, xs, handlers)
    return xs, handlers
Exemple #5
0
def listeq_unroll_case(l1, l2, eqfn):
    if jit.isvirtual(l1) and l1.ll_length() < 10:
        return True
    if jit.isvirtual(l2) and l2.ll_length() < 10:
        return True
    return False
Exemple #6
0
def ll_pop(func, l, index):
    length = l.ll_length()
    if index < 0:
        index += length
    if func is dum_checkidx:
        if index < 0 or index >= length:
            raise IndexError
    else:
        ll_assert(index >= 0, "negative list pop index out of bound")
        ll_assert(index < length, "list pop index out of bound")
    res = l.ll_getitem_fast(index)
    ll_delitem_nonneg(dum_nocheck, l, index)
    return res


@jit.look_inside_iff(lambda l: jit.isvirtual(l))
def ll_reverse(l):
    length = l.ll_length()
    i = 0
    length_1_i = length - 1 - i
    while i < length_1_i:
        tmp = l.ll_getitem_fast(i)
        l.ll_setitem_fast(i, l.ll_getitem_fast(length_1_i))
        l.ll_setitem_fast(length_1_i, tmp)
        i += 1
        length_1_i -= 1


def ll_getitem_nonneg(func, basegetitem, l, index):
    ll_assert(index >= 0, "unexpectedly negative list getitem index")
    if func is dum_checkidx:
Exemple #7
0
class PyFrame(W_Root):
    """Represents a frame for a regular Python function
    that needs to be interpreted.

    Public fields:
     * 'space' is the object space this frame is running in
     * 'code' is the PyCode object this frame runs
     * 'w_locals' is the locals dictionary to use, if needed, stored on a
       debug object
     * 'w_globals' is the attached globals dictionary
     * 'builtin' is the attached built-in module
     * 'valuestack_w', 'blockstack', control the interpretation

    Cell Vars:
        my local variables that are exposed to my inner functions
    Free Vars:
        variables coming from a parent function in which i'm nested
    'closure' is a list of Cell instances: the received free vars.
    """

    __metaclass__ = extendabletype

    frame_finished_execution = False
    f_generator_wref = rweakref.dead_ref  # for generators/coroutines
    f_generator_nowref = None  # (only one of the two attrs)
    w_yielding_from = None
    last_instr = -1
    f_backref = jit.vref_None

    escaped = False  # see mark_as_escaped()
    debugdata = None

    pycode = None  # code object executed by that frame
    locals_cells_stack_w = None  # the list of all locals, cells and the valuestack
    valuestackdepth = 0  # number of items on valuestack
    lastblock = None

    # other fields:

    # builtin - builtin cache, only if honor__builtins__ is True
    # defaults to False

    # there is also self.space which is removed by the annotator

    # additionally JIT uses vable_token field that is representing
    # frame current virtualizable state as seen by the JIT

    def __init__(self, space, code, w_globals, outer_func):
        if not we_are_translated():
            assert type(self) == space.FrameClass, (
                "use space.FrameClass(), not directly PyFrame()")
        self = hint(self, access_directly=True, fresh_virtualizable=True)
        assert isinstance(code, pycode.PyCode)
        self.space = space
        self.pycode = code
        if code.frame_stores_global(w_globals):
            self.getorcreatedebug().w_globals = w_globals
        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        size = code.co_nlocals + ncellvars + nfreevars + code.co_stacksize
        # the layout of this list is as follows:
        # | local vars | cells | stack |
        self.locals_cells_stack_w = [None] * size
        self.valuestackdepth = code.co_nlocals + ncellvars + nfreevars
        make_sure_not_resized(self.locals_cells_stack_w)
        check_nonneg(self.valuestackdepth)
        #
        if space.config.objspace.honor__builtins__:
            self.builtin = space.builtin.pick_builtin(w_globals)
        # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
        # class bodies only have CO_NEWLOCALS.
        self.initialize_frame_scopes(outer_func, code)

    def getdebug(self):
        return self.debugdata

    def getorcreatedebug(self):
        if self.debugdata is None:
            self.debugdata = FrameDebugData(self.pycode)
        return self.debugdata

    def get_w_globals(self):
        debugdata = self.getdebug()
        if debugdata is not None:
            return debugdata.w_globals
        return jit.promote(self.pycode).w_globals

    def get_w_f_trace(self):
        d = self.getdebug()
        if d is None:
            return None
        return d.w_f_trace

    def get_is_being_profiled(self):
        d = self.getdebug()
        if d is None:
            return False
        return d.is_being_profiled

    def get_w_locals(self):
        d = self.getdebug()
        if d is None:
            return None
        return d.w_locals

    def get_f_trace_lines(self):
        d = self.getdebug()
        if d is None:
            return True
        return d.f_trace_lines

    def get_f_trace_opcodes(self):
        d = self.getdebug()
        if d is None:
            return False
        return d.f_trace_opcodes

    @not_rpython
    def __repr__(self):
        # useful in tracebacks
        return "<%s.%s executing %s at line %s" % (
            self.__class__.__module__, self.__class__.__name__, self.pycode,
            self.get_last_lineno())

    def _getcell(self, varindex):
        cell = self.locals_cells_stack_w[varindex + self.pycode.co_nlocals]
        assert isinstance(cell, Cell)
        return cell

    def mark_as_escaped(self):
        """
        Must be called on frames that are exposed to applevel, e.g. by
        sys._getframe().  This ensures that the virtualref holding the frame
        is properly forced by ec.leave(), and thus the frame will be still
        accessible even after the corresponding C stack died.
        """
        self.escaped = True

    def append_block(self, block):
        assert block.previous is self.lastblock
        self.lastblock = block

    def pop_block(self):
        block = self.lastblock
        self.lastblock = block.previous
        return block

    def blockstack_non_empty(self):
        return self.lastblock is not None

    def get_blocklist(self):
        """Returns a list containing all the blocks in the frame"""
        lst = []
        block = self.lastblock
        while block is not None:
            lst.append(block)
            block = block.previous
        return lst

    def set_blocklist(self, lst):
        self.lastblock = None
        i = len(lst) - 1
        while i >= 0:
            block = lst[i]
            i -= 1
            block.previous = self.lastblock
            self.lastblock = block

    def get_builtin(self):
        if self.space.config.objspace.honor__builtins__:
            return self.builtin
        else:
            return self.space.builtin

    @jit.unroll_safe
    def initialize_frame_scopes(self, outer_func, code):
        # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
        # class bodies only have CO_NEWLOCALS.
        # CO_NEWLOCALS: make a locals dict unless optimized is also set
        # CO_OPTIMIZED: no locals dict needed at all
        flags = code.co_flags
        if not (flags & pycode.CO_OPTIMIZED):
            if flags & pycode.CO_NEWLOCALS:
                self.getorcreatedebug().w_locals = self.space.newdict(
                    module=True)
            else:
                w_globals = self.get_w_globals()
                assert w_globals is not None
                self.getorcreatedebug().w_locals = w_globals

        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        if not nfreevars:
            if not ncellvars:
                return  # no cells needed - fast path
        elif outer_func is None:
            space = self.space
            raise oefmt(
                space.w_TypeError,
                "directly executed code object may not contain free "
                "variables")
        if outer_func and outer_func.closure:
            closure_size = len(outer_func.closure)
        else:
            closure_size = 0
        if closure_size != nfreevars:
            raise ValueError("code object received a closure with "
                             "an unexpected number of free variables")
        index = code.co_nlocals
        for i in range(ncellvars):
            self.locals_cells_stack_w[index] = Cell(
                None, self.pycode.cell_families[i])
            index += 1
        for i in range(nfreevars):
            self.locals_cells_stack_w[index] = outer_func.closure[i]
            index += 1

    def _is_generator_or_coroutine(self):
        return (self.getcode().co_flags &
                (pycode.CO_COROUTINE | pycode.CO_GENERATOR
                 | pycode.CO_ASYNC_GENERATOR)) != 0

    def run(self, name=None, qualname=None):
        """Start this frame's execution."""
        if self._is_generator_or_coroutine():
            return self.initialize_as_generator(name, qualname)
        else:
            return self.execute_frame()

    run._always_inline_ = True

    def initialize_as_generator(self, name, qualname):
        space = self.space
        flags = self.getcode().co_flags
        if flags & pycode.CO_COROUTINE:
            from pypy.interpreter.generator import Coroutine
            gen = Coroutine(self, name, qualname)
            ec = space.getexecutioncontext()
            w_wrapper = ec.w_coroutine_wrapper_fn
            gen.capture_origin(ec)
        elif flags & pycode.CO_ASYNC_GENERATOR:
            from pypy.interpreter.generator import AsyncGenerator
            gen = AsyncGenerator(self, name, qualname)
            ec = None
            w_wrapper = None
        elif flags & pycode.CO_GENERATOR:
            from pypy.interpreter.generator import GeneratorIterator
            gen = GeneratorIterator(self, name, qualname)
            ec = None
            w_wrapper = None
        else:
            raise AssertionError("bad co_flags")

        if space.config.translation.rweakref:
            self.f_generator_wref = rweakref.ref(gen)
        else:
            self.f_generator_nowref = gen
        w_gen = gen

        if w_wrapper is not None:
            if ec.in_coroutine_wrapper:
                raise oefmt(
                    space.w_RuntimeError, "coroutine wrapper %R attempted "
                    "to recursively wrap %R", w_wrapper, self.getcode())
            ec.in_coroutine_wrapper = True
            try:
                w_gen = space.call_function(w_wrapper, w_gen)
            finally:
                ec.in_coroutine_wrapper = False
        return w_gen

    def resume_execute_frame(self, w_arg_or_err):
        # Called from execute_frame() just before resuming the bytecode
        # interpretation.
        from pypy.interpreter.pyopcode import SApplicationException
        space = self.space
        w_yf = self.w_yielding_from
        if w_yf is not None:
            self.w_yielding_from = None
            try:
                self.next_yield_from(w_yf, w_arg_or_err)
            except OperationError as operr:
                operr.record_context(space, space.getexecutioncontext())
                return self.handle_generator_error(operr)
            # Normal case: the call above raises Yield.
            # We reach this point if the iterable is exhausted.
            last_instr = jit.promote(self.last_instr)
            assert last_instr & 1 == 0
            assert last_instr >= 0
            return r_uint(last_instr + 2)

        if isinstance(w_arg_or_err, SApplicationException):
            return self.handle_generator_error(w_arg_or_err.operr)

        last_instr = jit.promote(self.last_instr)
        if last_instr != -1:
            assert last_instr & 1 == 0
            self.pushvalue(w_arg_or_err)
            return r_uint(last_instr + 2)
        else:
            return r_uint(0)

    def execute_frame(self, w_arg_or_err=None):
        """Execute this frame.  Main entry point to the interpreter.
        'w_arg_or_err' is non-None iff we are starting or resuming
        a generator or coroutine frame; in that case, w_arg_or_err
        is the input argument -or- an SApplicationException instance.
        """
        from pypy.interpreter import pyopcode
        # the following 'assert' is an annotation hint: it hides from
        # the annotator all methods that are defined in PyFrame but
        # overridden in the {,Host}FrameClass subclasses of PyFrame.
        assert (isinstance(self, self.space.FrameClass)
                or not self.space.config.translating)
        executioncontext = self.space.getexecutioncontext()
        executioncontext.enter(self)
        got_exception = True
        w_exitvalue = self.space.w_None
        try:
            executioncontext.call_trace(self)
            #
            # Execution starts just after the last_instr.  Initially,
            # last_instr is -1.  After a generator suspends it points to
            # the YIELD_VALUE/YIELD_FROM instruction.
            try:
                try:
                    if w_arg_or_err is None:
                        assert self.last_instr == -1
                        next_instr = r_uint(0)
                    else:
                        next_instr = self.resume_execute_frame(w_arg_or_err)
                except pyopcode.Yield:
                    w_exitvalue = self.popvalue()
                else:
                    w_exitvalue = self.dispatch(self.pycode, next_instr,
                                                executioncontext)
            except OperationError:
                raise
            except Exception as e:  # general fall-back
                raise self._convert_unexpected_exception(e)
            finally:
                executioncontext.return_trace(self, w_exitvalue)
            got_exception = False
        finally:
            executioncontext.leave(self, w_exitvalue, got_exception)
        return w_exitvalue

    execute_frame.insert_stack_check_here = True

    # stack manipulation helpers
    def pushvalue(self, w_object):
        depth = self.valuestackdepth
        self.locals_cells_stack_w[depth] = ll_assert_not_none(w_object)
        self.valuestackdepth = depth + 1

    def pushvalue_none(self):
        depth = self.valuestackdepth
        # the entry is already None, and remains None
        assert self.locals_cells_stack_w[depth] is None
        self.valuestackdepth = depth + 1

    def assert_stack_index(self, index):
        if we_are_translated():
            return
        assert self._check_stack_index(index)

    def _check_stack_index(self, index):
        code = self.pycode
        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        stackstart = code.co_nlocals + ncellvars + nfreevars
        return index >= stackstart

    def popvalue(self):
        return ll_assert_not_none(self.popvalue_maybe_none())

    def popvalue_maybe_none(self):
        depth = self.valuestackdepth - 1
        self.assert_stack_index(depth)
        assert depth >= 0
        w_object = self.locals_cells_stack_w[depth]
        self.locals_cells_stack_w[depth] = None
        self.valuestackdepth = depth
        return w_object

    # we need two popvalues that return different data types:
    # one in case we want list another in case of tuple
    def _new_popvalues():
        @jit.unroll_safe
        def popvalues(self, n):
            values_w = [None] * n
            while True:
                n -= 1
                if n < 0:
                    break
                values_w[n] = self.popvalue()
            return values_w

        return popvalues

    popvalues = _new_popvalues()
    popvalues_mutable = _new_popvalues()
    del _new_popvalues

    @jit.unroll_safe
    def peekvalues(self, n):
        values_w = [None] * n
        base = self.valuestackdepth - n
        self.assert_stack_index(base)
        assert base >= 0
        while True:
            n -= 1
            if n < 0:
                break
            values_w[n] = self.locals_cells_stack_w[base + n]
        return values_w

    @jit.unroll_safe
    def dropvalues(self, n):
        n = hint(n, promote=True)
        finaldepth = self.valuestackdepth - n
        self.assert_stack_index(finaldepth)
        assert finaldepth >= 0
        while True:
            n -= 1
            if n < 0:
                break
            self.locals_cells_stack_w[finaldepth + n] = None
        self.valuestackdepth = finaldepth

    @jit.unroll_safe
    def pushrevvalues(self, n, values_w):  # n should be len(values_w)
        make_sure_not_resized(values_w)
        while True:
            n -= 1
            if n < 0:
                break
            self.pushvalue(values_w[n])

    @jit.unroll_safe
    def dupvalues(self, n):
        delta = n - 1
        while True:
            n -= 1
            if n < 0:
                break
            w_value = self.peekvalue(delta)
            self.pushvalue(w_value)

    def peekvalue(self, index_from_top=0):
        # NOTE: top of the stack is peekvalue(0).
        # Contrast this with CPython where it's PEEK(-1).
        return ll_assert_not_none(self.peekvalue_maybe_none(index_from_top))

    def peekvalue_maybe_none(self, index_from_top=0):
        index_from_top = hint(index_from_top, promote=True)
        index = self.valuestackdepth + ~index_from_top
        self.assert_stack_index(index)
        assert index >= 0
        return self.locals_cells_stack_w[index]

    def settopvalue(self, w_object, index_from_top=0):
        index_from_top = hint(index_from_top, promote=True)
        index = self.valuestackdepth + ~index_from_top
        self.assert_stack_index(index)
        assert index >= 0
        self.locals_cells_stack_w[index] = ll_assert_not_none(w_object)

    @jit.unroll_safe
    def dropvaluesuntil(self, finaldepth):
        depth = self.valuestackdepth - 1
        finaldepth = hint(finaldepth, promote=True)
        assert finaldepth >= 0
        while depth >= finaldepth:
            self.locals_cells_stack_w[depth] = None
            depth -= 1
        self.valuestackdepth = finaldepth

    def _guess_function_name_parens(self, fnname=None, w_function=None):
        """ Returns 'funcname()' from either a function name fnname or a
        wrapped callable w_function. If it's not a function or a method, returns
        'Classname object'"""
        # XXX this is super annoying to compute every time we do a function call!
        # CPython has a similar function, PyEval_GetFuncName
        from pypy.interpreter.function import Function, Method
        if fnname is not None:
            return fnname + '()'
        if w_function is None:
            return None
        if isinstance(w_function, Function):
            return w_function.name + '()'
        if isinstance(w_function, Method):
            return self._guess_function_name_parens(None,
                                                    w_function.w_function)
        return self.space.type(w_function).getname(self.space) + ' object'

    def make_arguments(self,
                       nargs,
                       methodcall=False,
                       w_function=None,
                       fnname=None):
        fnname_parens = self._guess_function_name_parens(fnname, w_function)
        return Arguments(self.space,
                         self.peekvalues(nargs),
                         methodcall=methodcall,
                         fnname_parens=fnname_parens)

    def argument_factory(self,
                         arguments,
                         keywords,
                         keywords_w,
                         w_star,
                         w_starstar,
                         methodcall=False,
                         w_function=None,
                         fnname=None):
        fnname_parens = self._guess_function_name_parens(fnname, w_function)
        return Arguments(self.space,
                         arguments,
                         keywords,
                         keywords_w,
                         w_star,
                         w_starstar,
                         methodcall=methodcall,
                         fnname_parens=fnname_parens)

    def hide(self):
        return self.pycode.hidden_applevel

    def getcode(self):
        return hint(self.pycode, promote=True)

    @jit.look_inside_iff(lambda self, scope_w: jit.isvirtual(scope_w))
    def setfastscope(self, scope_w):
        """Initialize the fast locals from a list of values,
        where the order is according to self.pycode.signature()."""
        scope_len = len(scope_w)
        if scope_len > self.pycode.co_nlocals:
            raise ValueError("new fastscope is longer than the allocated area")
        # don't assign directly to 'locals_cells_stack_w[:scope_len]' to be
        # virtualizable-friendly
        for i in range(scope_len):
            self.locals_cells_stack_w[i] = scope_w[i]
        self.init_cells()

    def getdictscope(self):
        """
        Get the locals as a dictionary
        """
        self.fast2locals()
        return self.debugdata.w_locals

    def setdictscope(self, w_locals):
        """
        Initialize the locals from a dictionary.
        """
        self.getorcreatedebug().w_locals = w_locals
        self.locals2fast()

    @jit.unroll_safe
    def fast2locals(self):
        # Copy values from the fastlocals to self.w_locals
        d = self.getorcreatedebug()
        if d.w_locals is None:
            d.w_locals = self.space.newdict(module=True)
        varnames = self.getcode().getvarnames()
        for i in range(min(len(varnames), self.getcode().co_nlocals)):
            name = varnames[i]
            w_value = self.locals_cells_stack_w[i]
            if w_value is not None:
                self.space.setitem_str(d.w_locals, name, w_value)
            else:
                w_name = self.space.newtext(name)
                try:
                    self.space.delitem(d.w_locals, w_name)
                except OperationError as e:
                    if not e.match(self.space, self.space.w_KeyError):
                        raise

        # cellvars are values exported to inner scopes
        # freevars are values coming from outer scopes
        # (see locals2fast for why CO_OPTIMIZED)
        freevarnames = self.pycode.co_cellvars
        if self.pycode.co_flags & consts.CO_OPTIMIZED:
            freevarnames = freevarnames + self.pycode.co_freevars
        for i in range(len(freevarnames)):
            name = freevarnames[i]
            cell = self._getcell(i)
            try:
                w_value = cell.get()
            except ValueError:
                pass
            else:
                self.space.setitem_str(d.w_locals, name, w_value)

    @jit.unroll_safe
    def locals2fast(self):
        # Copy values from self.w_locals to the fastlocals
        w_locals = self.getorcreatedebug().w_locals
        assert w_locals is not None
        varnames = self.getcode().getvarnames()
        numlocals = self.getcode().co_nlocals

        new_fastlocals_w = [None] * numlocals

        for i in range(min(len(varnames), numlocals)):
            name = varnames[i]
            w_value = self.space.finditem_str(w_locals, name)
            if w_value is not None:
                new_fastlocals_w[i] = w_value

        self.setfastscope(new_fastlocals_w)

        freevarnames = self.pycode.co_cellvars
        if self.pycode.co_flags & consts.CO_OPTIMIZED:
            freevarnames = freevarnames + self.pycode.co_freevars
            # If the namespace is unoptimized, then one of the
            # following cases applies:
            # 1. It does not contain free variables, because it
            #    uses import * or is a top-level namespace.
            # 2. It is a class namespace.
            # We don't want to accidentally copy free variables
            # into the locals dict used by the class.
        for i in range(len(freevarnames)):
            name = freevarnames[i]
            cell = self._getcell(i)
            w_value = self.space.finditem_str(w_locals, name)
            if w_value is not None:
                cell.set(w_value)

    @jit.unroll_safe
    def init_cells(self):
        """
        Initialize cellvars from self.locals_cells_stack_w.
        """
        args_to_copy = self.pycode._args_as_cellvars
        index = self.pycode.co_nlocals
        for i in range(len(args_to_copy)):
            argnum = args_to_copy[i]
            if argnum >= 0:
                cell = self.locals_cells_stack_w[index]
                assert isinstance(cell, Cell)
                cell.set(self.locals_cells_stack_w[argnum])
            index += 1

    def getclosure(self):
        return None

    def fget_code(self, space):
        return self.getcode()

    def fget_getdictscope(self, space):
        return self.getdictscope()

    def fget_w_globals(self, space):
        # bit silly, but GetSetProperty passes a space
        return self.get_w_globals()

    ### line numbers ###

    def fget_f_lineno(self, space):
        "Returns the line number of the instruction currently being executed."
        if self.get_w_f_trace() is None:
            return space.newint(self.get_last_lineno())
        else:
            return space.newint(self.getorcreatedebug().f_lineno)

    def fset_f_lineno(self, space, w_new_lineno):
        "Change the line number of the instruction currently being executed."
        try:
            new_lineno = space.int_w(w_new_lineno)
        except OperationError:
            raise oefmt(space.w_ValueError, "lineno must be an integer")

        # You can only do this from within a trace function, not via
        # _getframe or similar hackery.
        if space.int_w(self.fget_f_lasti(space)) == -1:
            raise oefmt(
                space.w_ValueError,
                "can't jump from the 'call' trace event of a new frame")
        if self.get_w_f_trace() is None:
            raise oefmt(space.w_ValueError,
                        "f_lineno can only be set by a trace function")

        code = self.pycode.co_code
        if ord(code[self.last_instr]) == YIELD_VALUE:
            raise oefmt(space.w_ValueError,
                        "can't jump from a yield statement")

        # Only allow jumps when we're tracing a line event.
        d = self.getorcreatedebug()
        if not d.is_in_line_tracing:
            raise oefmt(space.w_ValueError,
                        "can only jump from a 'line' trace event")

        line = self.pycode.co_firstlineno
        if new_lineno < line:
            raise oefmt(space.w_ValueError,
                        "line %d comes before the current code block",
                        new_lineno)
        elif new_lineno == line:
            new_lasti = 0
        else:
            # Find the bytecode offset for the start of the given
            # line, or the first code-owning line after it.
            lnotab = self.pycode.co_lnotab
            addr = 0
            new_lasti = -1
            for offset in xrange(0, len(lnotab), 2):
                addr += ord(lnotab[offset])
                line_offset = ord(lnotab[offset + 1])
                if line_offset >= 0x80:
                    line_offset -= 0x100
                line += line_offset
                if line >= new_lineno:
                    new_lasti = addr
                    new_lineno = line
                    break

        # If we didn't reach the requested line, return an error.
        if new_lasti == -1:
            raise oefmt(space.w_ValueError,
                        "line %d comes after the current code block",
                        new_lineno)

        min_addr = min(new_lasti, self.last_instr)
        max_addr = max(new_lasti, self.last_instr)

        # You can't jump onto a line with an 'except' statement on it -
        # they expect to have an exception on the top of the stack, which
        # won't be true if you jump to them.  They always start with code
        # that either pops the exception using POP_TOP (plain 'except:'
        # lines do this) or duplicates the exception on the stack using
        # DUP_TOP (if there's an exception type specified).  See compile.c,
        # 'com_try_except' for the full details.  There aren't any other
        # cases (AFAIK) where a line's code can start with DUP_TOP or
        # POP_TOP, but if any ever appear, they'll be subject to the same
        # restriction (but with a different error message).
        if ord(code[new_lasti]) in (DUP_TOP, POP_TOP):
            raise oefmt(space.w_ValueError,
                        "can't jump to 'except' line as there's no exception")

        # You can't jump into or out of a 'finally' block because the 'try'
        # block leaves something on the stack for the END_FINALLY to clean
        # up.      So we walk the bytecode, maintaining a simulated blockstack.
        # When we reach the old or new address and it's in a 'finally' block
        # we note the address of the corresponding SETUP_FINALLY.  The jump
        # is only legal if neither address is in a 'finally' block or
        # they're both in the same one.  'blockstack' is a stack of the
        # bytecode addresses of the SETUP_X opcodes, and 'in_finally' tracks
        # whether we're in a 'finally' block at each blockstack level.

        # PYPY NOTE: CPython (at least 3.5.2+) doesn't check except blocks,
        # but that results in crashes.  But for now I'm going to follow the
        # logic of CPython 3.6.9 exactly anyway, which is quite messy already.

        f_lasti_setup_addr = -1
        new_lasti_setup_addr = -1
        blockstack = []  # current blockstack (addresses of SETUP_*)
        in_finally = []  # list of flags "is this a finally block?"
        addr = 0
        while addr < len(code):
            assert addr & 1 == 0
            op = ord(code[addr])
            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH,
                      SETUP_ASYNC_WITH):
                blockstack.append(addr)
                in_finally.append(False)
            elif op == POP_BLOCK:
                if len(blockstack) == 0:
                    raise oefmt(
                        space.w_SystemError,
                        "POP_BLOCK not properly nested in this bytecode")
                setup_op = ord(code[blockstack[-1]])
                if setup_op in (SETUP_FINALLY, SETUP_WITH, SETUP_ASYNC_WITH):
                    in_finally[-1] = True
                else:
                    del blockstack[-1]
            elif op == END_FINALLY:
                # Ignore END_FINALLYs for SETUP_EXCEPTs - they exist
                # in the bytecode but don't correspond to an actual
                # 'finally' block.  (If len(blockstack) is 0, we must
                # be seeing such an END_FINALLY.)
                if len(blockstack) > 0:
                    setup_op = ord(code[blockstack[-1]])
                    if setup_op in (SETUP_FINALLY, SETUP_WITH,
                                    SETUP_ASYNC_WITH):
                        del blockstack[-1]

            # For the addresses we're interested in, see whether they're
            # within a 'finally' block and if so, remember the address
            # of the SETUP_FINALLY.
            if addr == new_lasti or addr == self.last_instr:
                setup_addr = -1
                for i in xrange(len(blockstack) - 1, -1, -1):
                    if in_finally[i]:
                        setup_addr = blockstack[i]
                        break

                if setup_addr != -1:
                    if addr == new_lasti:
                        new_lasti_setup_addr = setup_addr
                    if addr == self.last_instr:
                        f_lasti_setup_addr = setup_addr
            addr += 2

        # Verify that the blockstack tracking code didn't get lost.
        if len(blockstack) != 0:
            raise oefmt(space.w_SystemError,
                        "blocks not properly nested in this bytecode")

        # After all that, are we jumping into / out of a 'finally' block?
        if new_lasti_setup_addr != f_lasti_setup_addr:
            raise oefmt(
                space.w_ValueError,
                "can't jump into or out of a 'finally' block "
                "(%d -> %d)", f_lasti_setup_addr, new_lasti_setup_addr)

        # now we know we're not jumping into or out of a place which
        # needs a SysExcInfoRestorer.  Check that we're not jumping
        # *into* a block, but only (potentially) out of some blocks.

        # Police block-jumping (you can't jump into the middle of a block)
        # and ensure that the blockstack finishes up in a sensible state (by
        # popping any blocks we're jumping out of).  We look at all the
        # blockstack operations between the current position and the new
        # one, and keep track of how many blocks we drop out of on the way.
        # By also keeping track of the lowest blockstack position we see, we
        # can tell whether the jump goes into any blocks without coming out
        # again - in that case we raise an exception below.

        delta_iblock = min_delta_iblock = 0
        addr = min_addr
        while addr < max_addr:
            assert addr & 1 == 0
            op = ord(code[addr])

            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH,
                      SETUP_ASYNC_WITH):
                delta_iblock += 1
            elif op == POP_BLOCK:
                delta_iblock -= 1

            min_delta_iblock = min(min_delta_iblock, delta_iblock)
            addr += 2

        # 'min_delta_iblock' is <= 0; its absolute value is the number of
        # blocks we exit.  'go_iblock' is the delta number of blocks
        # between the last_instr and the new_lasti, in this order.
        if new_lasti > self.last_instr:
            go_iblock = delta_iblock  # Forwards jump.
        else:
            go_iblock = -delta_iblock  # Backwards jump.

        if go_iblock > min_delta_iblock:
            raise oefmt(space.w_ValueError,
                        "can't jump into the middle of a block")
        assert go_iblock <= 0

        # Pop any blocks that we're jumping out of.
        from pypy.interpreter.pyopcode import FinallyBlock
        for ii in range(-go_iblock):
            block = self.pop_block()
            block.cleanupstack(self)
            if (isinstance(block, FinallyBlock) and ord(
                    code[block.handlerposition]) == WITH_CLEANUP_START):
                self.popvalue()  # Pop the exit function.

        d.f_lineno = new_lineno
        assert new_lasti & 1 == 0
        self.last_instr = new_lasti

    def get_last_lineno(self):
        "Returns the line number of the instruction currently being executed."
        return pytraceback.offset2lineno(self.pycode, self.last_instr)

    def fget_f_builtins(self, space):
        return self.get_builtin().getdict(space)

    def get_f_back(self):
        return ExecutionContext.getnextframe_nohidden(self)

    def fget_f_back(self, space):
        return self.get_f_back()

    def fget_f_lasti(self, space):
        return self.space.newint(self.last_instr)

    def fget_f_trace(self, space):
        return self.get_w_f_trace()

    def fset_f_trace(self, space, w_trace):
        if space.is_w(w_trace, space.w_None):
            self.getorcreatedebug().w_f_trace = None
        else:
            d = self.getorcreatedebug()
            d.w_f_trace = w_trace
            d.f_lineno = self.get_last_lineno()

    def fdel_f_trace(self, space):
        self.getorcreatedebug().w_f_trace = None

    def fget_f_trace_lines(self, space):
        return space.newbool(self.get_f_trace_lines())

    def fset_f_trace_lines(self, space, w_trace):
        self.getorcreatedebug().f_trace_lines = space.is_true(w_trace)

    def fget_f_trace_opcodes(self, space):
        return space.newbool(self.get_f_trace_opcodes())

    def fset_f_trace_opcodes(self, space, w_trace):
        self.getorcreatedebug().f_trace_opcodes = space.is_true(w_trace)

    def get_generator(self):
        if self.space.config.translation.rweakref:
            return self.f_generator_wref()
        else:
            return self.f_generator_nowref

    def descr_clear(self, space):
        """F.clear(): clear most references held by the frame"""
        # Clears a random subset of the attributes: the local variables
        # and the w_locals.  Note that CPython doesn't clear f_locals
        # (which can create leaks) but it's hard to notice because
        # the next Python-level read of 'frame.f_locals' will clear it.
        if not self.frame_finished_execution:
            if not self._is_generator_or_coroutine():
                raise oefmt(space.w_RuntimeError,
                            "cannot clear an executing frame")
            gen = self.get_generator()
            if gen is not None:
                if gen.running:
                    raise oefmt(space.w_RuntimeError,
                                "cannot clear an executing frame")
                # xxx CPython raises the RuntimeWarning "coroutine was never
                # awaited" in this case too.  Does it make any sense?
                gen.descr_close()

        debug = self.getdebug()
        if debug is not None:
            debug.w_f_trace = None
            if debug.w_locals is not None:
                debug.w_locals = space.newdict()

        # clear the locals, including the cell/free vars, and the stack
        for i in range(len(self.locals_cells_stack_w)):
            w_oldvalue = self.locals_cells_stack_w[i]
            if isinstance(w_oldvalue, Cell):
                w_newvalue = Cell(None, w_oldvalue.family)
            else:
                w_newvalue = None
            self.locals_cells_stack_w[i] = w_newvalue
        self.valuestackdepth = 0
        self.lastblock = None  # the FrameBlock chained list

    def _convert_unexpected_exception(self, e):
        from pypy.interpreter import error

        operr = error.get_converted_unexpected_exception(self.space, e)
        pytraceback.record_application_traceback(self.space, operr, self,
                                                 self.last_instr)
        raise operr

    def descr_repr(self, space):
        code = self.pycode
        moreinfo = ", file '%s', line %s, code %s" % (
            code.co_filename, self.get_last_lineno(), code.co_name)
        return self.getrepr(space, "frame", moreinfo)
Exemple #8
0
# ------------------------------------------------------------
# builder.getlength()


@always_inline
def ll_getlength(ll_builder):
    num_chars_missing_from_last_piece = (ll_builder.current_end -
                                         ll_builder.current_pos)
    return ll_builder.total_size - num_chars_missing_from_last_piece


# ------------------------------------------------------------
# builder.build()


@jit.look_inside_iff(lambda ll_builder: jit.isvirtual(ll_builder))
def ll_build(ll_builder):
    # NB. usually the JIT doesn't look inside this function; it does
    # so only in the simplest example where it could virtualize everything
    if ll_builder.extra_pieces:
        ll_fold_pieces(ll_builder)
    elif ll_builder.current_pos != ll_builder.total_size:
        ll_shrink_final(ll_builder)
    return ll_builder.current_buf


def ll_shrink_final(ll_builder):
    final_size = ll_builder.current_pos
    ll_assert(final_size <= ll_builder.total_size,
              "final_size > ll_builder.total_size?")
    buf = rgc.ll_shrink_array(ll_builder.current_buf, final_size)
Exemple #9
0
    class abstract_callable(Callable):

        _immutable_fields_ = ["val_%d" % x for x in arg_iter]

        def __init__(self, term_name, args, signature):
            raise NotImplementedError

        def _init_values(self, args):
            if args is None:
                return
            for x in arg_iter:
                setattr(self, 'val_%d' % x, args[x])

        def _make_new(self, name, signature):
            raise NotImplementedError("abstract base class")

        def arguments(self):
            result = [None] * n_args
            for x in arg_iter:
                result[x] = getattr(self, 'val_%d' % x)
            return result

        def argument_at(self, i):
            for x in arg_iter:
                if x == i:
                    return getattr(self, 'val_%d' % x)
            raise IndexError

        def argument_count(self):
            return n_args

        def quick_unify_check(self, other, similarity=None):
            other = other.dereference(None)
            if isinstance(other, Var):
                return True
            if not isinstance(other, Callable):
                return False
            if similarity and similarity.get_score_signatures(self.signature(), other.signature()) < similarity.threshold:
                return False
            if not similarity and not self.signature().eq(other.signature()):
                return False
            if not isinstance(other, abstract_callable):
                return Callable.quick_unify_check(self, other)
            for x in arg_iter:
                a = getattr(self, 'val_%d' % x)
                b = getattr(other, 'val_%d' % x)
                if not a.quick_unify_check(b):
                    return False
            return True

        def nonvar_unify_and_standardize_apart(self, other, heap, env, similarity=None):
            if not isinstance(other, abstract_callable):
                raise UnificationFailed
            if self.signature().eq(other.signature()):
                for x in arg_iter:
                    a = getattr(self, 'val_%d' % x)
                    b = getattr(other, 'val_%d' % x)
                    a.unify_and_standardize_apart(b, heap, env)
            else:
                raise UnificationFailed

        def copy_standardize_apart(self, heap, env):
            result = self._make_new(self.name(), self.signature())
            reuse = True
            i = 0
            for i in arg_iter:
                arg = getattr(self, 'val_%d' % i)
                cloned, arg_is_reused = arg.copy_standardize_apart(heap, env)
                reuse = reuse & arg_is_reused
                setattr(result, 'val_%d' % i, cloned)
                i += 1
            if reuse:
                return self, True
            else:
                # XXX what about the variable shunting in Callable.build
                return result, False

        @specialize.arg(3)
        def nonvar_unify(self, other, heap, occurs_check, similarity=None):
            if not isinstance(other, abstract_callable):
                raise UnificationFailed
            if self.signature().eq(other.signature()):
                return self._nonvar_unify_jit(other, heap, occurs_check)
            else:
                raise UnificationFailed

        @specialize.arg(3)
        @jit.look_inside_iff(lambda self, other, heap, occurs_check:
                jit.isvirtual(self) or jit.isvirtual(other) or
                jit.isconstant(self) or jit.isconstant(other))
        def _nonvar_unify_jit(self, other, heap, occurs_check):
                for x in arg_iter:
                    a = getattr(self, 'val_%d' % x)
                    b = getattr(other, 'val_%d' % x)
                    a.unify(b, heap, occurs_check)

        @specialize.arg(1)
        def _copy_term(self, copy_individual, heap, *extraargs):
            result = self._make_new(self.name(), self.signature())
            newinstance = False
            i = 0
            for i in arg_iter:
                arg = getattr(self, 'val_%d' % i)
                cloned = copy_individual(arg, i, heap, *extraargs)
                newinstance = newinstance | (cloned is not arg)
                setattr(result, 'val_%d' % i, cloned)
                i += 1
            if newinstance:
                # XXX what about the variable shunting in Callable.build
                return result
            else:
                return self
Exemple #10
0
#    struct dicttable {
#        int num_live_items;
#        int num_ever_used_items;
#        int resize_counter;
#        {byte, short, int, long} *indexes;
#        dictentry *entries;
#        lookup_function_no; # one of the four possible functions for different
#                       # size dicts; the rest of the word is a counter for how
#                       # many 'entries' at the start are known to be deleted
#        (Function DICTKEY, DICTKEY -> bool) *fnkeyeq;
#        (Function DICTKEY -> int) *fnkeyhash;
#    }
#
#

@jit.look_inside_iff(lambda d, key, hash, flag: jit.isvirtual(d))
@jit.oopspec('ordereddict.lookup(d, key, hash, flag)')
def ll_call_lookup_function(d, key, hash, flag):
    fun = d.lookup_function_no & FUNC_MASK
    # This likely() here forces gcc to compile the check for fun == FUNC_BYTE
    # first.  Otherwise, this is a regular switch and gcc (at least 4.7)
    # compiles this as a series of checks, with the FUNC_BYTE case last.
    # It sounds minor, but it is worth 6-7% on a PyPy microbenchmark.
    if likely(fun == FUNC_BYTE):
        return ll_dict_lookup(d, key, hash, flag, TYPE_BYTE)
    elif fun == FUNC_SHORT:
        return ll_dict_lookup(d, key, hash, flag, TYPE_SHORT)
    elif IS_64BIT and fun == FUNC_INT:
        return ll_dict_lookup(d, key, hash, flag, TYPE_INT)
    elif fun == FUNC_LONG:
        return ll_dict_lookup(d, key, hash, flag, TYPE_LONG)
Exemple #11
0
class LLOrderedDict(object):
    INIT_SIZE = 8
    HIGHEST_BIT = intmask(1 << (LONG_BIT - 1))
    MASK = intmask(HIGHEST_BIT - 1)
    PERTURB_SHIFT = 5

    @staticmethod
    def ll_valid_from_flag(entries, i):
        return entries[i].valid

    @staticmethod
    def ll_everused_from_flag(entries, i):
        return entries[i].everused

    @staticmethod
    def ll_mark_deleted_in_flag(entries, i):
        entries[i].valid = False

    @staticmethod
    def ll_hashkey_custom(d, key):
        DICT = lltype.typeOf(d).TO
        return hlinvoke(DICT.r_hashkey, d.hashkey_func, key)

    @staticmethod
    def ll_keyeq_custom(d, key1, key2):
        DICT = lltype.typeOf(d).TO
        return hlinvoke(DICT.r_keyeq, d.keyeq_func, key1, key2)

    @staticmethod
    def ll_hash_recompute(entries, i):
        ENTRIES = lltype.typeOf(entries).TO
        return ENTRIES.fast_hash_func(entries[i].key)

    @staticmethod
    def ll_hash_from_cache(entries, i):
        return entries[i].hash

    @staticmethod
    def recast(P, v):
        if isinstance(P, lltype.Ptr):
            return lltype.cast_pointer(P, v)
        else:
            return v

    @staticmethod
    def ll_newdict(DICT):
        d = lltype.malloc(DICT)
        d.entries = lltype.malloc(DICT.entries.TO, LLOrderedDict.INIT_SIZE, zero=True)
        d.num_items = 0
        d.first_entry = -1
        d.last_entry = -1
        d.resize_counter = LLOrderedDict.INIT_SIZE * 2
        return d

    @staticmethod
    def ll_len(d):
        return d.num_items

    @staticmethod
    @jit.look_inside_iff(lambda d, key, hash:
                         jit.isvirtual(d) and jit.isconstant(key))
    def ll_lookup(d, key, hash):
        entries = d.entries
        mask = len(entries) - 1
        i = hash & mask
        if entries.valid(i):
            checkingkey = entries[i].key
            if checkingkey == key:
                return i
            if d.keyeq is not None and entries.hash(i) == hash:
                found = d.keyeq(checkingkey, key)
                if d.paranoia:
                    if (entries != d.entries or
                        not entries.valid(i) or entries[i].key != checkingkey):
                        return LLOrderedDict.ll_lookup(d, key, hash)
                if found:
                    return i
            freeslot = -1
        elif entries.everused(i):
            freeslot = i
        else:
            return i | LLOrderedDict.HIGHEST_BIT

        perturb = r_uint(hash)
        while True:
            i = r_uint(i)
            i = (i << 2) + i + perturb + 1
            i = intmask(i) & mask
            if not entries.everused(i):
                if freeslot == -1:
                    freeslot = i
                return freeslot | LLOrderedDict.HIGHEST_BIT
            elif entries.valid(i):
                checkingkey = entries[i].key
                if checkingkey == key:
                    return i
                if d.keyeq is not None and entries.hash(i) == hash:
                    found = d.keyeq(checkingkey, key)
                    if d.paranoia:
                        if (entries != d.entries or
                            not entries.valid(i) or entries[i].key != checkingkey):
                            return LLOrderedDict.ll_lookup(d, key, hash)
                    if found:
                        return i
            elif freeslot == -1:
                freeslot = i
            perturb >>= LLOrderedDict.PERTURB_SHIFT

    @staticmethod
    def ll_setitem(d, key, value):
        hash = d.hashkey(key)
        i = LLOrderedDict.ll_lookup(d, key, hash)
        LLOrderedDict.ll_setitem_lookup_done(d, key, value, hash, i)

    @staticmethod
    def ll_setitem_lookup_done(d, key, value, hash, i):
        valid = (i & LLOrderedDict.HIGHEST_BIT) == 0
        i &= LLOrderedDict.MASK
        everused = d.entries.everused(i)
        ENTRY = lltype.typeOf(d.entries).TO.OF
        entry = d.entries[i]
        entry.value = value
        if valid:
            return
        entry.key = key
        if hasattr(ENTRY, "hash"):
            entry.hash = hash
        if hasattr(ENTRY, "valid"):
            entry.valid = True
        d.num_items += 1
        if d.first_entry == -1:
            d.first_entry = i
        else:
            d.entries[d.last_entry].next = i
        entry.prev = d.last_entry
        d.last_entry = i
        entry.next = -1
        if not everused:
            if hasattr(ENTRY, "everused"):
                entry.everused = True
            d.resize_counter -= 3
            if d.resize_counter <= 0:
                LLOrderedDict.ll_resize(d)

    @staticmethod
    def ll_getitem(d, key):
        i = LLOrderedDict.ll_lookup(d, key, d.hashkey(key))
        if not i & LLOrderedDict.HIGHEST_BIT:
            return d.entries[i].value
        else:
            raise KeyError

    @staticmethod
    def ll_delitem(d, key):
        i = LLOrderedDict.ll_lookup(d, key, d.hashkey(key))
        if i & LLOrderedDict.HIGHEST_BIT:
            raise KeyError
        LLOrderedDict._ll_del(d, i)

    @staticmethod
    def _ll_del(d, i):
        d.entries.mark_deleted(i)
        d.num_items -= 1
        entry = d.entries[i]
        if entry.prev == -1:
            d.first_entry = entry.next
        else:
            d.entries[entry.prev].next = entry.next
        if entry.next == -1:
            d.last_entry = entry.prev
        else:
            d.entries[entry.next].prev = entry.prev

        ENTRIES = lltype.typeOf(d.entries).TO
        ENTRY = ENTRIES.OF
        if ENTRIES.must_clear_key:
            entry.key = lltype.nullptr(ENTRY.key.TO)
        if ENTRIES.must_clear_value:
            entry.value = lltype.nullptr(ENTRY.value.TO)

    @staticmethod
    def ll_contains(d, key):
        i = LLOrderedDict.ll_lookup(d, key, d.hashkey(key))
        return not bool(i & LLOrderedDict.HIGHEST_BIT)

    @staticmethod
    def ll_resize(d):
        old_entries = d.entries
        if d.num_items > 50000:
            new_estimate = d.num_items * 2
        else:
            new_estimate = d.num_items * 4

        new_size = LLOrderedDict.INIT_SIZE
        while new_size <= new_estimate:
            new_size *= 2

        d.entries = lltype.malloc(lltype.typeOf(old_entries).TO, new_size, zero=True)
        d.num_items = 0
        d.resize_counter = new_size * 2

        i = d.first_entry
        d.first_entry = -1
        d.last_entry = -1

        while i != -1:
            hash = old_entries.hash(i)
            entry = old_entries[i]
            LLOrderedDict.ll_insert_clean(d, entry.key, entry.value, hash)
            i = entry.next

    @staticmethod
    def ll_insert_clean(d, key, value, hash):
        i = LLOrderedDict.ll_lookup_clean(d, hash)
        ENTRY = lltype.typeOf(d.entries).TO.OF
        entry = d.entries[i]
        entry.value = value
        entry.key = key
        if hasattr(ENTRY, "hash"):
            entry.hash = hash
        if hasattr(ENTRY, "valid"):
            entry.valid = True
        if hasattr(ENTRY, "everused"):
            entry.everused = True
        d.num_items += 1
        if d.first_entry == -1:
            d.first_entry = i
        else:
            d.entries[d.last_entry].next = i
        entry.prev = d.last_entry
        d.last_entry = i
        entry.next = -1
        d.resize_counter -= 3

    @staticmethod
    def ll_lookup_clean(d, hash):
        entries = d.entries
        mask = len(entries) - 1
        i = hash & mask
        perturb = r_uint(hash)
        while entries.everused(i):
            i = r_uint(i)
            i = (i << 2) + i + perturb + 1
            i = intmask(i) & mask
            perturb >>= LLOrderedDict.PERTURB_SHIFT
        return i

    @staticmethod
    def ll_keys(LIST, d):
        res = LIST.ll_newlist(d.num_items)
        ELEM = lltype.typeOf(res.ll_items()).TO.OF
        i = 0
        idx = d.first_entry
        while idx != -1:
            res.ll_items()[i] = LLOrderedDict.recast(ELEM, d.entries[idx].key)
            idx = d.entries[idx].next
            i += 1
        return res

    @staticmethod
    def ll_values(LIST, d):
        res = LIST.ll_newlist(d.num_items)
        ELEM = lltype.typeOf(res.ll_items()).TO.OF
        i = 0
        idx = d.first_entry
        while idx != -1:
            res.ll_items()[i] = LLOrderedDict.recast(ELEM, d.entries[idx].value)
            idx = d.entries[idx].next
            i += 1
        return res

    @staticmethod
    def ll_get(d, key, default):
        i = LLOrderedDict.ll_lookup(d, key, d.hashkey(key))
        if not i & LLOrderedDict.HIGHEST_BIT:
            return d.entries[i].value
        else:
            return default

    @staticmethod
    def ll_pop(d, key):
        i = LLOrderedDict.ll_lookup(d, key, d.hashkey(key))
        if not i & LLOrderedDict.HIGHEST_BIT:
            value = d.entries[i].value
            LLOrderedDict._ll_del(d, i)
            return value
        else:
            raise KeyError

    @staticmethod
    def ll_pop_default(d, key, default):
        try:
            return LLOrderedDict.ll_pop(d, key)
        except KeyError:
            return default

    @staticmethod
    def ll_popitem(RESTYPE, d):
        if not d.num_items:
            raise KeyError
        entry = d.entries[d.first_entry]

        r = lltype.malloc(RESTYPE.TO)
        r.item0 = LLOrderedDict.recast(RESTYPE.TO.item0, entry.key)
        r.item1 = LLOrderedDict.recast(RESTYPE.TO.item1, entry.value)

        LLOrderedDict._ll_del(d, d.first_entry)

        return r

    @staticmethod
    def ll_update(d, other):
        idx = other.first_entry
        while idx != -1:
            entry = other.entries[idx]
            i = LLOrderedDict.ll_lookup(d, entry.key, other.entries.hash(idx))
            LLOrderedDict.ll_setitem_lookup_done(d, entry.key, entry.value, other.entries.hash(idx), i)
            idx = entry.next

    @staticmethod
    def ll_clear(d):
        if d.num_items == 0:
            return
        d.entries = lltype.malloc(lltype.typeOf(d.entries).TO, LLOrderedDict.INIT_SIZE, zero=True)
        d.num_items = 0
        d.first_entry = -1
        d.last_entry = -1
        d.resize_counter = LLOrderedDict.INIT_SIZE * 2

    @staticmethod
    def ll_copy(d):
        DICT = lltype.typeOf(d).TO
        new_d = lltype.malloc(DICT)
        new_d.entries = lltype.malloc(DICT.entries.TO, len(d.entries), zero=True)
        new_d.num_items = d.num_items
        new_d.resize_counter = d.resize_counter
        new_d.first_entry = d.first_entry
        new_d.last_entry = d.last_entry
        if hasattr(DICT, "hashkey_func"):
            new_d.hashkey_func = d.hashkey_func
        if hasattr(DICT, "keyeq_func"):
            new_d.keyeq_func = d.keyeq_func
        for i in xrange(len(d.entries)):
            entry = d.entries[i]
            new_entry = new_d.entries[i]
            new_entry.key = entry.key
            new_entry.value = entry.value
            new_entry.next = entry.next
            new_entry.prev = entry.prev
            new_entry.everused = entry.everused
            new_entry.valid = entry.valid
            if hasattr(DICT.entries.TO.OF, "hash"):
                new_entry.hash = entry.hash
        return new_d

    @staticmethod
    def ll_newdictiter(ITER, d):
        it = lltype.malloc(ITER)
        it.d = d
        it.index = d.first_entry
        return it

    @staticmethod
    def ll_dictiternext(RESTYPE, it):
        if it.index == -1:
            raise StopIteration
        r = lltype.malloc(RESTYPE.TO)
        entry = it.d.entries[it.index]
        r.item0 = LLOrderedDict.recast(RESTYPE.TO.item0, entry.key)
        r.item1 = LLOrderedDict.recast(RESTYPE.TO.item1, entry.value)
        it.index = entry.next
        return r
Exemple #12
0
class PyFrame(W_Root):
    """Represents a frame for a regular Python function
    that needs to be interpreted.

    Public fields:
     * 'space' is the object space this frame is running in
     * 'code' is the PyCode object this frame runs
     * 'w_locals' is the locals dictionary to use, if needed, stored on a
       debug object
     * 'w_globals' is the attached globals dictionary
     * 'builtin' is the attached built-in module
     * 'valuestack_w', 'blockstack', control the interpretation

    Cell Vars:
        my local variables that are exposed to my inner functions
    Free Vars:
        variables coming from a parent function in which i'm nested
    'closure' is a list of Cell instances: the received free vars.
    """

    __metaclass__ = extendabletype

    frame_finished_execution = False
    last_instr = -1
    last_exception = None
    f_backref = jit.vref_None

    escaped = False  # see mark_as_escaped()
    debugdata = None

    pycode = None  # code object executed by that frame
    locals_cells_stack_w = None  # the list of all locals, cells and the valuestack
    valuestackdepth = 0  # number of items on valuestack
    lastblock = None

    # other fields:

    # builtin - builtin cache, only if honor__builtins__ is True
    # defaults to False

    # there is also self.space which is removed by the annotator

    # additionally JIT uses vable_token field that is representing
    # frame current virtualizable state as seen by the JIT

    def __init__(self, space, code, w_globals, outer_func):
        if not we_are_translated():
            assert type(self) == space.FrameClass, (
                "use space.FrameClass(), not directly PyFrame()")
        self = hint(self, access_directly=True, fresh_virtualizable=True)
        assert isinstance(code, pycode.PyCode)
        self.space = space
        self.pycode = code
        if code.frame_stores_global(w_globals):
            self.getorcreatedebug().w_globals = w_globals
        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        size = code.co_nlocals + ncellvars + nfreevars + code.co_stacksize
        # the layout of this list is as follows:
        # | local vars | cells | stack |
        self.locals_cells_stack_w = [None] * size
        self.valuestackdepth = code.co_nlocals + ncellvars + nfreevars
        make_sure_not_resized(self.locals_cells_stack_w)
        check_nonneg(self.valuestackdepth)
        #
        if space.config.objspace.honor__builtins__:
            self.builtin = space.builtin.pick_builtin(w_globals)
        # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
        # class bodies only have CO_NEWLOCALS.
        self.initialize_frame_scopes(outer_func, code)

    def getdebug(self):
        return self.debugdata

    def getorcreatedebug(self):
        if self.debugdata is None:
            self.debugdata = FrameDebugData(self.pycode)
        return self.debugdata

    def get_w_globals(self):
        debugdata = self.getdebug()
        if debugdata is not None:
            return debugdata.w_globals
        return jit.promote(self.pycode).w_globals

    def get_w_f_trace(self):
        d = self.getdebug()
        if d is None:
            return None
        return d.w_f_trace

    def get_is_being_profiled(self):
        d = self.getdebug()
        if d is None:
            return False
        return d.is_being_profiled

    def get_w_locals(self):
        d = self.getdebug()
        if d is None:
            return None
        return d.w_locals

    @not_rpython
    def __repr__(self):
        # useful in tracebacks
        return "<%s.%s executing %s at line %s" % (
            self.__class__.__module__, self.__class__.__name__, self.pycode,
            self.get_last_lineno())

    def _getcell(self, varindex):
        cell = self.locals_cells_stack_w[varindex + self.pycode.co_nlocals]
        assert isinstance(cell, Cell)
        return cell

    def mark_as_escaped(self):
        """
        Must be called on frames that are exposed to applevel, e.g. by
        sys._getframe().  This ensures that the virtualref holding the frame
        is properly forced by ec.leave(), and thus the frame will be still
        accessible even after the corresponding C stack died.
        """
        self.escaped = True

    def append_block(self, block):
        assert block.previous is self.lastblock
        self.lastblock = block

    def pop_block(self):
        block = self.lastblock
        self.lastblock = block.previous
        return block

    def blockstack_non_empty(self):
        return self.lastblock is not None

    def get_blocklist(self):
        """Returns a list containing all the blocks in the frame"""
        lst = []
        block = self.lastblock
        while block is not None:
            lst.append(block)
            block = block.previous
        return lst

    def set_blocklist(self, lst):
        self.lastblock = None
        i = len(lst) - 1
        while i >= 0:
            block = lst[i]
            i -= 1
            block.previous = self.lastblock
            self.lastblock = block

    def get_builtin(self):
        if self.space.config.objspace.honor__builtins__:
            return self.builtin
        else:
            return self.space.builtin

    @jit.unroll_safe
    def initialize_frame_scopes(self, outer_func, code):
        # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
        # class bodies only have CO_NEWLOCALS.
        # CO_NEWLOCALS: make a locals dict unless optimized is also set
        # CO_OPTIMIZED: no locals dict needed at all
        flags = code.co_flags
        if not (flags & pycode.CO_OPTIMIZED):
            if flags & pycode.CO_NEWLOCALS:
                self.getorcreatedebug().w_locals = self.space.newdict(
                    module=True)
            else:
                w_globals = self.get_w_globals()
                assert w_globals is not None
                self.getorcreatedebug().w_locals = w_globals

        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        if not nfreevars:
            if not ncellvars:
                return  # no cells needed - fast path
        elif outer_func is None:
            space = self.space
            raise oefmt(
                space.w_TypeError,
                "directly executed code object may not contain free "
                "variables")
        if outer_func and outer_func.closure:
            closure_size = len(outer_func.closure)
        else:
            closure_size = 0
        if closure_size != nfreevars:
            raise ValueError("code object received a closure with "
                             "an unexpected number of free variables")
        index = code.co_nlocals
        for i in range(ncellvars):
            self.locals_cells_stack_w[index] = Cell(
                None, self.pycode.cell_families[i])
            index += 1
        for i in range(nfreevars):
            self.locals_cells_stack_w[index] = outer_func.closure[i]
            index += 1

    def run(self):
        """Start this frame's execution."""
        if self.getcode().co_flags & pycode.CO_GENERATOR:
            from pypy.interpreter.generator import GeneratorIterator
            return GeneratorIterator(self)
        else:
            return self.execute_frame()

    def execute_frame(self, w_inputvalue=None, operr=None):
        """Execute this frame.  Main entry point to the interpreter.
        The optional arguments are there to handle a generator's frame:
        w_inputvalue is for generator.send() and operr is for
        generator.throw().
        """
        # the following 'assert' is an annotation hint: it hides from
        # the annotator all methods that are defined in PyFrame but
        # overridden in the {,Host}FrameClass subclasses of PyFrame.
        assert (isinstance(self, self.space.FrameClass)
                or not self.space.config.translating)
        executioncontext = self.space.getexecutioncontext()
        executioncontext.enter(self)
        got_exception = True
        w_exitvalue = self.space.w_None
        try:
            executioncontext.call_trace(self)
            #
            try:
                if operr is not None:
                    next_instr = self.handle_operation_error(
                        executioncontext, operr)
                    self.last_instr = intmask(next_instr - 1)
                else:
                    # Execution starts just after the last_instr.  Initially,
                    # last_instr is -1.  After a generator suspends it points to
                    # the YIELD_VALUE instruction.
                    next_instr = r_uint(self.last_instr + 1)
                    if next_instr != 0:
                        self.pushvalue(w_inputvalue)
                w_exitvalue = self.dispatch(self.pycode, next_instr,
                                            executioncontext)
            except OperationError:
                raise
            except Exception as e:  # general fall-back
                raise self._convert_unexpected_exception(e)
            finally:
                executioncontext.return_trace(self, w_exitvalue)
            # it used to say self.last_exception = None
            # this is now done by the code in pypyjit module
            # since we don't want to invalidate the virtualizable
            # for no good reason
            got_exception = False
        finally:
            executioncontext.leave(self, w_exitvalue, got_exception)
        return w_exitvalue

    execute_frame.insert_stack_check_here = True

    # stack manipulation helpers
    def pushvalue(self, w_object):
        depth = self.valuestackdepth
        self.locals_cells_stack_w[depth] = ll_assert_not_none(w_object)
        self.valuestackdepth = depth + 1

    def pushvalue_none(self):
        depth = self.valuestackdepth
        # the entry is already None, and remains None
        assert self.locals_cells_stack_w[depth] is None
        self.valuestackdepth = depth + 1

    def assert_stack_index(self, index):
        if we_are_translated():
            return
        assert self._check_stack_index(index)

    def _check_stack_index(self, index):
        code = self.pycode
        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        stackstart = code.co_nlocals + ncellvars + nfreevars
        return index >= stackstart

    def popvalue(self):
        return ll_assert_not_none(self.popvalue_maybe_none())

    def popvalue_maybe_none(self):
        depth = self.valuestackdepth - 1
        self.assert_stack_index(depth)
        assert depth >= 0
        w_object = self.locals_cells_stack_w[depth]
        self.locals_cells_stack_w[depth] = None
        self.valuestackdepth = depth
        return w_object

    # we need two popvalues that return different data types:
    # one in case we want list another in case of tuple
    def _new_popvalues():
        @jit.unroll_safe
        def popvalues(self, n):
            values_w = [None] * n
            while True:
                n -= 1
                if n < 0:
                    break
                values_w[n] = self.popvalue()
            return values_w

        return popvalues

    popvalues = _new_popvalues()
    popvalues_mutable = _new_popvalues()
    del _new_popvalues

    @jit.unroll_safe
    def peekvalues(self, n):
        values_w = [None] * n
        base = self.valuestackdepth - n
        self.assert_stack_index(base)
        assert base >= 0
        while True:
            n -= 1
            if n < 0:
                break
            values_w[n] = self.locals_cells_stack_w[base + n]
        return values_w

    @jit.unroll_safe
    def dropvalues(self, n):
        n = hint(n, promote=True)
        finaldepth = self.valuestackdepth - n
        self.assert_stack_index(finaldepth)
        assert finaldepth >= 0
        while True:
            n -= 1
            if n < 0:
                break
            self.locals_cells_stack_w[finaldepth + n] = None
        self.valuestackdepth = finaldepth

    @jit.unroll_safe
    def pushrevvalues(self, n, values_w):  # n should be len(values_w)
        make_sure_not_resized(values_w)
        while True:
            n -= 1
            if n < 0:
                break
            self.pushvalue(values_w[n])

    @jit.unroll_safe
    def dupvalues(self, n):
        delta = n - 1
        while True:
            n -= 1
            if n < 0:
                break
            w_value = self.peekvalue(delta)
            self.pushvalue(w_value)

    def peekvalue(self, index_from_top=0):
        # NOTE: top of the stack is peekvalue(0).
        # Contrast this with CPython where it's PEEK(-1).
        return ll_assert_not_none(self.peekvalue_maybe_none(index_from_top))

    def peekvalue_maybe_none(self, index_from_top=0):
        index_from_top = hint(index_from_top, promote=True)
        index = self.valuestackdepth + ~index_from_top
        self.assert_stack_index(index)
        assert index >= 0
        return self.locals_cells_stack_w[index]

    def settopvalue(self, w_object, index_from_top=0):
        index_from_top = hint(index_from_top, promote=True)
        index = self.valuestackdepth + ~index_from_top
        self.assert_stack_index(index)
        assert index >= 0
        self.locals_cells_stack_w[index] = ll_assert_not_none(w_object)

    @jit.unroll_safe
    def dropvaluesuntil(self, finaldepth):
        depth = self.valuestackdepth - 1
        finaldepth = hint(finaldepth, promote=True)
        assert finaldepth >= 0
        while depth >= finaldepth:
            self.locals_cells_stack_w[depth] = None
            depth -= 1
        self.valuestackdepth = finaldepth

    def make_arguments(self, nargs, methodcall=False):
        return Arguments(self.space,
                         self.peekvalues(nargs),
                         methodcall=methodcall)

    def argument_factory(self,
                         arguments,
                         keywords,
                         keywords_w,
                         w_star,
                         w_starstar,
                         methodcall=False):
        return Arguments(self.space,
                         arguments,
                         keywords,
                         keywords_w,
                         w_star,
                         w_starstar,
                         methodcall=methodcall)

    @jit.dont_look_inside
    def descr__reduce__(self, space):
        from pypy.interpreter.mixedmodule import MixedModule
        w_mod = space.getbuiltinmodule('_pickle_support')
        mod = space.interp_w(MixedModule, w_mod)
        new_inst = mod.get('frame_new')
        w_tup_state = self._reduce_state(space)
        nt = space.newtuple
        return nt([new_inst, nt([]), w_tup_state])

    @jit.dont_look_inside
    def _reduce_state(self, space):
        from pypy.module._pickle_support import maker  # helper fns
        nt = space.newtuple

        if self.get_w_f_trace() is None:
            f_lineno = self.get_last_lineno()
        else:
            f_lineno = self.getorcreatedebug().f_lineno

        nlocals = self.pycode.co_nlocals
        values_w = self.locals_cells_stack_w
        w_locals_cells_stack = maker.slp_into_tuple_with_nulls(space, values_w)

        w_blockstack = nt(
            [block._get_state_(space) for block in self.get_blocklist()])
        if self.last_exception is None:
            w_exc_value = space.w_None
            w_tb = space.w_None
        else:
            w_exc_value = self.last_exception.get_w_value(space)
            w_tb = self.last_exception.get_w_traceback(space)

        d = self.getorcreatedebug()
        w_backref = self.f_backref()
        if w_backref is None:
            w_backref = space.w_None
        tup_state = [
            w_backref,
            self.get_builtin(),
            self.pycode,
            w_locals_cells_stack,
            w_blockstack,
            w_exc_value,  # last_exception
            w_tb,  #
            self.get_w_globals(),
            space.newint(self.last_instr),
            space.newbool(self.frame_finished_execution),
            space.newint(f_lineno),
            space.w_None,  #XXX placeholder for f_locals

            #f_restricted requires no additional data!
            space.w_None,
            space.newint(d.instr_lb),
            space.newint(d.instr_ub),
            space.newint(d.instr_prev_plus_one),
            space.newint(self.valuestackdepth),
        ]
        return nt(tup_state)

    @jit.dont_look_inside
    def descr__setstate__(self, space, w_args):
        from pypy.module._pickle_support import maker  # helper fns
        from pypy.interpreter.pycode import PyCode
        from pypy.interpreter.module import Module
        args_w = space.unpackiterable(w_args, 17)
        w_f_back, w_builtin, w_pycode, w_locals_cells_stack, w_blockstack, w_exc_value, w_tb,\
            w_globals, w_last_instr, w_finished, w_f_lineno, w_f_locals, \
            w_f_trace, w_instr_lb, w_instr_ub, w_instr_prev_plus_one, w_stackdepth = args_w

        new_frame = self
        pycode = space.interp_w(PyCode, w_pycode)

        values_w = maker.slp_from_tuple_with_nulls(space, w_locals_cells_stack)
        nfreevars = len(pycode.co_freevars)
        closure = None
        if nfreevars:
            base = pycode.co_nlocals + len(pycode.co_cellvars)
            closure = values_w[base:base + nfreevars]

        # do not use the instance's __init__ but the base's, because we set
        # everything like cells from here
        # XXX hack
        from pypy.interpreter.function import Function
        outer_func = Function(space, None, closure=closure, forcename="fake")
        PyFrame.__init__(self, space, pycode, w_globals, outer_func)
        f_back = space.interp_w(PyFrame, w_f_back, can_be_None=True)
        new_frame.f_backref = jit.non_virtual_ref(f_back)

        if space.config.objspace.honor__builtins__:
            new_frame.builtin = space.interp_w(Module, w_builtin)
        else:
            assert space.interp_w(Module, w_builtin) is space.builtin
        new_frame.set_blocklist([
            unpickle_block(space, w_blk)
            for w_blk in space.unpackiterable(w_blockstack)
        ])
        self.locals_cells_stack_w = values_w[:]
        valuestackdepth = space.int_w(w_stackdepth)
        if not self._check_stack_index(valuestackdepth):
            raise oefmt(space.w_ValueError, "invalid stackdepth")
        assert valuestackdepth >= 0
        self.valuestackdepth = valuestackdepth
        if space.is_w(w_exc_value, space.w_None):
            new_frame.last_exception = None
        else:
            from pypy.interpreter.pytraceback import PyTraceback
            tb = space.interp_w(PyTraceback, w_tb)
            new_frame.last_exception = OperationError(space.type(w_exc_value),
                                                      w_exc_value, tb)
        new_frame.last_instr = space.int_w(w_last_instr)
        new_frame.frame_finished_execution = space.is_true(w_finished)
        d = new_frame.getorcreatedebug()
        d.f_lineno = space.int_w(w_f_lineno)

        if space.is_w(w_f_trace, space.w_None):
            d.w_f_trace = None
        else:
            d.w_f_trace = w_f_trace

        d.instr_lb = space.int_w(w_instr_lb)  #the three for tracing
        d.instr_ub = space.int_w(w_instr_ub)
        d.instr_prev_plus_one = space.int_w(w_instr_prev_plus_one)

    def hide(self):
        return self.pycode.hidden_applevel

    def getcode(self):
        return hint(self.pycode, promote=True)

    @jit.look_inside_iff(lambda self, scope_w: jit.isvirtual(scope_w))
    def setfastscope(self, scope_w):
        """Initialize the fast locals from a list of values,
        where the order is according to self.pycode.signature()."""
        scope_len = len(scope_w)
        if scope_len > self.pycode.co_nlocals:
            raise ValueError("new fastscope is longer than the allocated area")
        # don't assign directly to 'locals_cells_stack_w[:scope_len]' to be
        # virtualizable-friendly
        for i in range(scope_len):
            self.locals_cells_stack_w[i] = scope_w[i]
        self.init_cells()

    def getdictscope(self):
        """
        Get the locals as a dictionary
        """
        self.fast2locals()
        return self.debugdata.w_locals

    def setdictscope(self, w_locals):
        """
        Initialize the locals from a dictionary.
        """
        self.getorcreatedebug().w_locals = w_locals
        self.locals2fast()

    @jit.unroll_safe
    def fast2locals(self):
        # Copy values from the fastlocals to self.w_locals
        d = self.getorcreatedebug()
        if d.w_locals is None:
            d.w_locals = self.space.newdict()
        varnames = self.getcode().getvarnames()
        for i in range(min(len(varnames), self.getcode().co_nlocals)):
            name = varnames[i]
            w_value = self.locals_cells_stack_w[i]
            if w_value is not None:
                self.space.setitem_str(d.w_locals, name, w_value)
            else:
                w_name = self.space.newtext(name)
                try:
                    self.space.delitem(d.w_locals, w_name)
                except OperationError as e:
                    if not e.match(self.space, self.space.w_KeyError):
                        raise

        # cellvars are values exported to inner scopes
        # freevars are values coming from outer scopes
        # (see locals2fast for why CO_OPTIMIZED)
        freevarnames = self.pycode.co_cellvars
        if self.pycode.co_flags & consts.CO_OPTIMIZED:
            freevarnames = freevarnames + self.pycode.co_freevars
        for i in range(len(freevarnames)):
            name = freevarnames[i]
            cell = self._getcell(i)
            try:
                w_value = cell.get()
            except ValueError:
                pass
            else:
                self.space.setitem_str(d.w_locals, name, w_value)

    @jit.unroll_safe
    def locals2fast(self):
        # Copy values from self.w_locals to the fastlocals
        w_locals = self.getorcreatedebug().w_locals
        assert w_locals is not None
        varnames = self.getcode().getvarnames()
        numlocals = self.getcode().co_nlocals

        new_fastlocals_w = [None] * numlocals

        for i in range(min(len(varnames), numlocals)):
            name = varnames[i]
            w_value = self.space.finditem_str(w_locals, name)
            if w_value is not None:
                new_fastlocals_w[i] = w_value

        self.setfastscope(new_fastlocals_w)

        freevarnames = self.pycode.co_cellvars
        if self.pycode.co_flags & consts.CO_OPTIMIZED:
            freevarnames = freevarnames + self.pycode.co_freevars
            # If the namespace is unoptimized, then one of the
            # following cases applies:
            # 1. It does not contain free variables, because it
            #    uses import * or is a top-level namespace.
            # 2. It is a class namespace.
            # We don't want to accidentally copy free variables
            # into the locals dict used by the class.
        for i in range(len(freevarnames)):
            name = freevarnames[i]
            cell = self._getcell(i)
            w_value = self.space.finditem_str(w_locals, name)
            if w_value is not None:
                cell.set(w_value)

    @jit.unroll_safe
    def init_cells(self):
        """
        Initialize cellvars from self.locals_cells_stack_w.
        """
        args_to_copy = self.pycode._args_as_cellvars
        index = self.pycode.co_nlocals
        for i in range(len(args_to_copy)):
            argnum = args_to_copy[i]
            if argnum >= 0:
                cell = self.locals_cells_stack_w[index]
                assert isinstance(cell, Cell)
                cell.set(self.locals_cells_stack_w[argnum])
            index += 1

    def getclosure(self):
        return None

    def fget_code(self, space):
        return self.getcode()

    def fget_getdictscope(self, space):
        return self.getdictscope()

    def fget_w_globals(self, space):
        # bit silly, but GetSetProperty passes a space
        return self.get_w_globals()

    ### line numbers ###

    def fget_f_lineno(self, space):
        "Returns the line number of the instruction currently being executed."
        if self.get_w_f_trace() is None:
            return space.newint(self.get_last_lineno())
        else:
            return space.newint(self.getorcreatedebug().f_lineno)

    def fset_f_lineno(self, space, w_new_lineno):
        "Returns the line number of the instruction currently being executed."
        try:
            new_lineno = space.int_w(w_new_lineno)
        except OperationError:
            raise oefmt(space.w_ValueError, "lineno must be an integer")

        # You can only do this from within a trace function, not via
        # _getframe or similar hackery.
        if space.int_w(self.fget_f_lasti(space)) == -1:
            raise oefmt(
                space.w_ValueError,
                "can't jump from the 'call' trace event of a new frame")
        if self.get_w_f_trace() is None:
            raise oefmt(space.w_ValueError,
                        "f_lineno can only be set by a trace function.")

        code = self.pycode.co_code
        if ord(code[self.last_instr]) == YIELD_VALUE:
            raise oefmt(space.w_ValueError,
                        "can't jump from a yield statement")

        # Only allow jumps when we're tracing a line event.
        d = self.getorcreatedebug()
        if not d.is_in_line_tracing:
            raise oefmt(space.w_ValueError,
                        "can only jump from a 'line' trace event")

        line = self.pycode.co_firstlineno
        if new_lineno < line:
            raise oefmt(space.w_ValueError,
                        "line %d comes before the current code.", new_lineno)
        elif new_lineno == line:
            new_lasti = 0
        else:
            new_lasti = -1
            addr = 0
            lnotab = self.pycode.co_lnotab
            for offset in xrange(0, len(lnotab), 2):
                addr += ord(lnotab[offset])
                line += ord(lnotab[offset + 1])
                if line >= new_lineno:
                    new_lasti = addr
                    new_lineno = line
                    break

        if new_lasti == -1:
            raise oefmt(space.w_ValueError,
                        "line %d comes after the current code.", new_lineno)

        # Don't jump to a line with an except in it.
        if ord(code[new_lasti]) in (DUP_TOP, POP_TOP):
            raise oefmt(space.w_ValueError,
                        "can't jump to 'except' line as there's no exception")

        # Don't jump into or out of a finally block.
        f_lasti_setup_addr = -1
        new_lasti_setup_addr = -1
        blockstack = []
        addr = 0
        while addr < len(code):
            op = ord(code[addr])
            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH):
                blockstack.append([addr, False])
            elif op == POP_BLOCK:
                setup_op = ord(code[blockstack[-1][0]])
                if setup_op == SETUP_FINALLY or setup_op == SETUP_WITH:
                    blockstack[-1][1] = True
                else:
                    blockstack.pop()
            elif op == END_FINALLY:
                if len(blockstack) > 0:
                    setup_op = ord(code[blockstack[-1][0]])
                    if setup_op == SETUP_FINALLY or setup_op == SETUP_WITH:
                        blockstack.pop()

            if addr == new_lasti or addr == self.last_instr:
                for ii in range(len(blockstack)):
                    setup_addr, in_finally = blockstack[~ii]
                    if in_finally:
                        if addr == new_lasti:
                            new_lasti_setup_addr = setup_addr
                        if addr == self.last_instr:
                            f_lasti_setup_addr = setup_addr
                        break

            if op >= HAVE_ARGUMENT:
                addr += 3
            else:
                addr += 1

        assert len(blockstack) == 0

        if new_lasti_setup_addr != f_lasti_setup_addr:
            raise oefmt(
                space.w_ValueError,
                "can't jump into or out of a 'finally' block %d -> %d",
                f_lasti_setup_addr, new_lasti_setup_addr)

        if new_lasti < self.last_instr:
            min_addr = new_lasti
            max_addr = self.last_instr
        else:
            min_addr = self.last_instr
            max_addr = new_lasti

        delta_iblock = min_delta_iblock = 0
        addr = min_addr
        while addr < max_addr:
            op = ord(code[addr])

            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH):
                delta_iblock += 1
            elif op == POP_BLOCK:
                delta_iblock -= 1
                if delta_iblock < min_delta_iblock:
                    min_delta_iblock = delta_iblock

            if op >= stdlib_opcode.HAVE_ARGUMENT:
                addr += 3
            else:
                addr += 1

        f_iblock = 0
        block = self.lastblock
        while block:
            f_iblock += 1
            block = block.previous
        min_iblock = f_iblock + min_delta_iblock
        if new_lasti > self.last_instr:
            new_iblock = f_iblock + delta_iblock
        else:
            new_iblock = f_iblock - delta_iblock

        if new_iblock > min_iblock:
            raise oefmt(space.w_ValueError,
                        "can't jump into the middle of a block")

        # Pop any blocks that we're jumping out of.
        from pypy.interpreter.pyopcode import FinallyBlock
        while f_iblock > new_iblock:
            block = self.pop_block()
            block.cleanup(self)
            f_iblock -= 1
            if (isinstance(block, FinallyBlock)
                    and ord(code[block.handlerposition]) == WITH_CLEANUP):
                self.popvalue()  # Pop the exit function.

        d.f_lineno = new_lineno
        self.last_instr = new_lasti

    def get_last_lineno(self):
        "Returns the line number of the instruction currently being executed."
        return pytraceback.offset2lineno(self.pycode, self.last_instr)

    def fget_f_builtins(self, space):
        return self.get_builtin().getdict(space)

    def get_f_back(self):
        return ExecutionContext.getnextframe_nohidden(self)

    def fget_f_back(self, space):
        return self.get_f_back()

    def fget_f_lasti(self, space):
        return self.space.newint(self.last_instr)

    def fget_f_trace(self, space):
        return self.get_w_f_trace()

    def fset_f_trace(self, space, w_trace):
        if space.is_w(w_trace, space.w_None):
            self.getorcreatedebug().w_f_trace = None
        else:
            d = self.getorcreatedebug()
            d.w_f_trace = w_trace
            d.f_lineno = self.get_last_lineno()

    def fdel_f_trace(self, space):
        self.getorcreatedebug().w_f_trace = None

    def fget_f_exc_type(self, space):
        if self.last_exception is not None:
            f = self.f_backref()
            while f is not None and f.last_exception is None:
                f = f.f_backref()
            if f is not None:
                return f.last_exception.w_type
        return space.w_None

    def fget_f_exc_value(self, space):
        if self.last_exception is not None:
            f = self.f_backref()
            while f is not None and f.last_exception is None:
                f = f.f_backref()
            if f is not None:
                return f.last_exception.get_w_value(space)
        return space.w_None

    def fget_f_exc_traceback(self, space):
        if self.last_exception is not None:
            f = self.f_backref()
            while f is not None and f.last_exception is None:
                f = f.f_backref()
            if f is not None:
                return f.last_exception.get_w_traceback(space)
        return space.w_None

    def fget_f_restricted(self, space):
        if space.config.objspace.honor__builtins__:
            return space.newbool(self.builtin is not space.builtin)
        return space.w_False

    @jit.unroll_safe
    @specialize.arg(2)
    def _exc_info_unroll(self, space, for_hidden=False):
        """Return the most recent OperationError being handled in the
        call stack
        """
        frame = self
        while frame:
            last = frame.last_exception
            if last is not None:
                if last is get_cleared_operation_error(self.space):
                    break
                if for_hidden or not frame.hide():
                    return last
            frame = frame.f_backref()
        return None

    def _convert_unexpected_exception(self, e):
        from pypy.interpreter import error

        operr = error.get_converted_unexpected_exception(self.space, e)
        pytraceback.record_application_traceback(self.space, operr, self,
                                                 self.last_instr)
        raise operr
Exemple #13
0
def unroll_pred(lst, idx, unroll_to=0):
    if not jit.we_are_jitted():
        return False
    return not jit.isvirtual(lst) and idx > unroll_to
Exemple #14
0
def ll_pop(func, l, index):
    length = l.ll_length()
    if index < 0:
        index += length
    if func is dum_checkidx:
        if index < 0 or index >= length:
            raise IndexError
    else:
        ll_assert(index >= 0, "negative list pop index out of bound")
        ll_assert(index < length, "list pop index out of bound")
    res = l.ll_getitem_fast(index)
    ll_delitem_nonneg(dum_nocheck, l, index)
    return res

@jit.look_inside_iff(lambda l: jit.isvirtual(l))
def ll_reverse(l):
    length = l.ll_length()
    i = 0
    length_1_i = length-1-i
    while i < length_1_i:
        tmp = l.ll_getitem_fast(i)
        l.ll_setitem_fast(i, l.ll_getitem_fast(length_1_i))
        l.ll_setitem_fast(length_1_i, tmp)
        i += 1
        length_1_i -= 1

def ll_getitem_nonneg(func, basegetitem, l, index):
    ll_assert(index >= 0, "unexpectedly negative list getitem index")
    if func is dum_checkidx:
        if index >= l.ll_length():
Exemple #15
0
def ll_dict_getitem(d, key):
    i = ll_dict_lookup(d, key, d.keyhash(key))
    if not i & HIGHEST_BIT:
        return ll_get_value(d, i)
    else:
        raise KeyError

def ll_dict_setitem(d, key, value):
    hash = d.keyhash(key)
    i = ll_dict_lookup(d, key, hash)
    return _ll_dict_setitem_lookup_done(d, key, value, hash, i)

# It may be safe to look inside always, it has a few branches though, and their
# frequencies needs to be investigated.
@jit.look_inside_iff(lambda d, key, value, hash, i: jit.isvirtual(d) and jit.isconstant(key))
def _ll_dict_setitem_lookup_done(d, key, value, hash, i):
    valid = (i & HIGHEST_BIT) == 0
    i = i & MASK
    ENTRY = lltype.typeOf(d.entries).TO.OF
    entry = d.entries[i]
    if not d.entries.everused(i):
        # a new entry that was never used before
        ll_assert(not valid, "valid but not everused")
        rc = d.resize_counter - 3
        if rc <= 0:       # if needed, resize the dict -- before the insertion
            ll_dict_resize(d)
            i = ll_dict_lookup_clean(d, hash)  # then redo the lookup for 'key'
            entry = d.entries[i]
            rc = d.resize_counter - 3
            ll_assert(rc > 0, "ll_dict_resize failed?")
Exemple #16
0
class W_ArrayObject(W_Object):
    """Abstract base class.  Concrete subclasses use various strategies.
    This base class defines the general methods that can be implemented
    without needing to call (too often) the arraylen(), _getitem_str()
    and _getitem_int() methods.
    """
    @staticmethod
    def new_array_from_list(space, lst_w):
        return W_ListArrayObject(space, lst_w)

    @staticmethod
    def new_array_from_rdict(space, dct_w):
        return W_RDictArrayObject(space, dct_w, compute_next_idx(dct_w))

    @staticmethod
    @jit.look_inside_iff(
        lambda space, pairs_ww, allow_bogus: jit.isvirtual(pairs_ww))
    def new_array_from_pairs(space, pairs_ww, allow_bogus):
        rdct_w = new_rdict()
        next_idx = 0
        for w_key, w_value in pairs_ww:
            if w_key is not None:
                try:
                    as_int, as_str = convert_to_index(space,
                                                      w_key,
                                                      allow_bogus=allow_bogus)
                except CannotConvertToIndex:
                    continue
                if as_str is not None:
                    # it was string, maybe it can be integer
                    try:
                        as_int = try_convert_str_to_int(as_str)
                        # yes it can
                        as_str = None
                    except ValueError:
                        pass
            # no key, just increment next_idx
            else:
                as_int, as_str = next_idx, None
            if as_str is None:
                if as_int >= next_idx:
                    next_idx = as_int + 1
                as_str = str(as_int)
            rdct_w[as_str] = w_value

        return W_RDictArrayObject(space, rdct_w, next_idx=next_idx)

    def copy_item(self):
        return self.copy()

    def is_true(self, space):
        return self.arraylen() > 0

    def int_w(self, space):
        return int(self.is_true(space))

    def as_int_arg(self, space):
        raise ConvertError('cannot use array as integer argument')

    def as_number(self, space):
        return space.wrap(self.is_true(space))

    def abs(self, space):
        return space.w_False

    def _lookup_item_ref(self, space, w_arg):
        """Return a possibly virtual reference to the item,
        or None if the lookup fails
        """
        try:
            as_int, as_str = convert_to_index(space, w_arg)
        except CannotConvertToIndex:
            space.ec.warn("Illegal offset type")
            return None
        if as_str is None:
            r_item = self._getitem_int(as_int)
            if r_item is None:
                return None
        else:
            r_item = self._getitem_str(as_str)
            if r_item is None:
                return None
        return r_item

    def getitem(self, space, w_arg, give_notice=False):
        try:
            as_int, as_str = convert_to_index(space, w_arg)
        except CannotConvertToIndex:
            space.ec.warn("Illegal offset type")
            return space.w_Null
        if as_str is None:
            r_item = self._getitem_int(as_int)
            if r_item is None:
                if give_notice:
                    space.ec.notice("Undefined offset: %d" % as_int)
                return space.w_Null
        else:
            r_item = self._getitem_str(as_str)
            if r_item is None:
                if give_notice:
                    space.ec.notice("Undefined index: %s" % as_str)
                return space.w_Null
        if isinstance(r_item, VirtualReference):
            return r_item.deref()
        else:
            return r_item

    def setitem2_maybe_inplace(self, space, w_arg, w_value, unique_item=False):
        try:
            as_int, as_str = convert_to_index(space, w_arg)
        except CannotConvertToIndex:
            space.ec.warn("Illegal offset type")
            return self, space.w_Null
        if as_str is None:
            w_arr = self._setitem_int(as_int, w_value, False, unique_item)
        else:
            w_arr = self._setitem_str(as_str, w_value, False, unique_item)
        return w_arr, w_value

    def _setitem_ref(self, space, w_arg, w_ref):
        try:
            as_int, as_str = convert_to_index(space, w_arg)
        except CannotConvertToIndex:
            space.ec.warn("Illegal offset type")
            return self
        if as_str is None:
            return self._setitem_int(as_int, w_ref, True)
        else:
            return self._setitem_str(as_str, w_ref, True)

    def appenditem_inplace(self, space, w_item, as_ref=False):
        # For now this always succeeds in appending the item in-place.
        # It may need to be reconsidered if we add more strategies.
        self._appenditem(w_item, as_ref)
        return w_item

    def packitem_maybe_inplace(self, space, w_arg, w_value):
        as_int, as_str = convert_to_index(space, w_arg)
        if as_str is not None:
            try:
                try_convert_str_to_int(as_str)
            except ValueError:
                # really not an int
                return self._setitem_str(as_str, w_value, False)
        self._appenditem(w_value)
        return self

    def _unsetitem(self, space, w_arg):
        as_int, as_str = convert_to_index(space, w_arg)
        if as_str is None:
            return self._unsetitem_int(as_int)
        else:
            return self._unsetitem_str(as_str)

    def isset_index(self, space, w_index):
        as_int, as_str = convert_to_index(space, w_index)
        if as_str is None:
            return self._isset_int(as_int)
        else:
            return self._isset_str(as_str)

    def _getitem_int(self, index):
        raise NotImplementedError("abstract")

    def _getitem_str(self, key):
        raise NotImplementedError("abstract")

    def _appenditem(self, w_obj, as_ref=False):
        raise NotImplementedError("abstract")

    def _setitem_int(self, index, w_value, as_ref, unique_item=False):
        raise NotImplementedError("abstract")

    def _setitem_str(self, key, w_value, as_ref, unique_item=False):
        # Note: might be occasionally called with a string like "5" too
        raise NotImplementedError("abstract")

    def _unsetitem_int(self, index):
        raise NotImplementedError("abstract")

    def _unsetitem_str(self, key):
        raise NotImplementedError("abstract")

    def _isset_int(self, index):
        raise NotImplementedError("abstract")

    def _isset_str(self, key):
        raise NotImplementedError("abstract")

    def arraylen(self):
        raise NotImplementedError("abstract")

    def as_rdict(self):
        raise NotImplementedError("abstract")

    def _each(self, space):
        raise NotImplementedError("abstract")

    def _current(self):
        raise NotImplementedError("abstract")

    def next(self, space):
        length = self.arraylen()
        current_idx = self.current_idx + 1
        if current_idx >= length:
            self.current_idx = length
            return w_False
        self.current_idx = current_idx
        return self._current()

    def _key(self, space):
        raise NotImplementedError("abstract")

    def _inplace_pop(self, space):
        raise NotImplementedError("abstract")

    def get_rdict_from_array(self):
        raise NotImplementedError("abstract")

    def as_dict(self):
        "NOT_RPYTHON: for tests only"
        return self.as_rdict()

    def var_dump(self, space, indent, recursion):
        return array_var_dump(self.as_rdict(), space, indent, recursion, self,
                              'array')

    def var_export(self, space, indent, recursion, suffix):
        return array_var_export(self.as_rdict(), space, indent, recursion,
                                self, suffix)

    def str(self, space, quiet=False):
        if not quiet:
            space.ec.notice("Array to string conversion")
        return "Array"

    def repr(self):
        return "Array"

    def dump(self):
        items = []
        next = 0
        for key, w_value in self.as_rdict().items():
            dumpvalue = w_value.dump()
            try:
                numkey = int(key)
            except ValueError:
                items.append('%s=>%s' % (key, dumpvalue))
                continue
            if numkey == next:
                items.append(dumpvalue)
            else:
                items.append('%d=>%s' % (numkey, dumpvalue))
            next = numkey + 1
        return "array(%s)" % ', '.join(items)

    def as_pair_list(self, space):
        pairs = []
        with space.iter(self) as w_iter:
            while not w_iter.done():
                w_key, w_val = w_iter.next_item(space)
                pairs.append((w_key, w_val))
        return pairs

    def eval_static(self, space):
        return self

    def serialize(self, space, builder, memo):
        builder.append("a:")
        builder.append(str(self.arraylen()))
        builder.append(":{")
        memo.add_counter()
        with space.iter(self) as itr:
            while not itr.done():
                w_key, w_value = itr.next_item(space)
                w_key.serialize(space, builder, memo)
                if w_value.serialize(space, builder, memo):
                    memo.add_counter()
        builder.append("}")
        return False  # counted above already

    def add(self, space, other_array):
        assert isinstance(other_array, W_ArrayObject)

        d = self.as_rdict()
        for k, w_v in other_array.as_rdict().iteritems():
            if k not in d:
                d[k] = w_v
        return space.new_array_from_rdict(d)
Exemple #17
0
def listeq_unroll_case(l1, l2, eqfn):
    if jit.isvirtual(l1) and l1.ll_length() < 10:
        return True
    if jit.isvirtual(l2) and l2.ll_length() < 10:
        return True
    return False
Exemple #18
0
def ll_dict_getitem(d, key):
    i = ll_dict_lookup(d, key, d.keyhash(key))
    if not i & HIGHEST_BIT:
        return ll_get_value(d, i)
    else:
        raise KeyError

def ll_dict_setitem(d, key, value):
    hash = d.keyhash(key)
    i = ll_dict_lookup(d, key, hash)
    return _ll_dict_setitem_lookup_done(d, key, value, hash, i)

# It may be safe to look inside always, it has a few branches though, and their
# frequencies needs to be investigated.
@jit.look_inside_iff(lambda d, key, value, hash, i: jit.isvirtual(d) and jit.isconstant(key))
def _ll_dict_setitem_lookup_done(d, key, value, hash, i):
    valid = (i & HIGHEST_BIT) == 0
    i = i & MASK
    ENTRY = lltype.typeOf(d.entries).TO.OF
    entry = d.entries[i]
    if not d.entries.everused(i):
        # a new entry that was never used before
        ll_assert(not valid, "valid but not everused")
        rc = d.resize_counter - 3
        if rc <= 0:       # if needed, resize the dict -- before the insertion
            ll_dict_resize(d)
            i = ll_dict_lookup_clean(d, hash)  # then redo the lookup for 'key'
            entry = d.entries[i]
            rc = d.resize_counter - 3
            ll_assert(rc > 0, "ll_dict_resize failed?")
Exemple #19
0
def from_list_unroll_pred(lst, idx, unroll_to=0):
    if not jit.we_are_jitted():
        return False
    if unroll_to == -1:
        return False
    return not jit.isvirtual(lst) and idx > unroll_to
Exemple #20
0
class W_CTypeStructOrUnion(W_CType):
    _immutable_fields_ = [
        'alignment?', 'fields_list?[*]', 'fields_dict?', 'custom_field_pos?',
        'with_var_array?'
    ]
    # fields added by complete_struct_or_union():
    alignment = -1
    fields_list = None
    fields_dict = None
    custom_field_pos = False
    with_var_array = False

    def __init__(self, space, name):
        W_CType.__init__(self, space, -1, name, len(name))

    def check_complete(self, w_errorcls=None):
        if self.fields_dict is None:
            space = self.space
            raise oefmt(w_errorcls or space.w_TypeError,
                        "'%s' is opaque or not completed yet", self.name)

    def _alignof(self):
        self.check_complete(w_errorcls=self.space.w_ValueError)
        return self.alignment

    def _fget(self, attrchar):
        if attrchar == 'f':  # fields
            space = self.space
            if self.size < 0:
                return space.w_None
            result = [None] * len(self.fields_list)
            for fname, field in self.fields_dict.iteritems():
                i = self.fields_list.index(field)
                result[i] = space.newtuple(
                    [space.wrap(fname), space.wrap(field)])
            return space.newlist(result)
        return W_CType._fget(self, attrchar)

    def convert_to_object(self, cdata):
        space = self.space
        self.check_complete()
        return cdataobj.W_CData(space, cdata, self)

    def copy_and_convert_to_object(self, cdata):
        space = self.space
        self.check_complete()
        ob = cdataobj.W_CDataNewOwning(space, self.size, self)
        misc._raw_memcopy(cdata, ob._cdata, self.size)
        keepalive_until_here(ob)
        return ob

    def typeoffsetof_field(self, fieldname, following):
        self.check_complete()
        space = self.space
        try:
            cfield = self.fields_dict[fieldname]
        except KeyError:
            raise OperationError(space.w_KeyError, space.wrap(fieldname))
        if cfield.bitshift >= 0:
            raise OperationError(space.w_TypeError,
                                 space.wrap("not supported for bitfields"))
        return (cfield.ctype, cfield.offset)

    def _copy_from_same(self, cdata, w_ob):
        if isinstance(w_ob, cdataobj.W_CData):
            if w_ob.ctype is self and self.size >= 0:
                misc._raw_memcopy(w_ob._cdata, cdata, self.size)
                keepalive_until_here(w_ob)
                return True
        return False

    def _check_only_one_argument_for_union(self, w_ob):
        pass

    def convert_from_object(self, cdata, w_ob):
        if not self._copy_from_same(cdata, w_ob):
            self.convert_struct_from_object(cdata, w_ob, optvarsize=-1)

    @jit.look_inside_iff(
        lambda self, cdata, w_ob, optvarsize: jit.isvirtual(w_ob))
    def convert_struct_from_object(self, cdata, w_ob, optvarsize):
        self._check_only_one_argument_for_union(w_ob)

        space = self.space
        if (space.isinstance_w(w_ob, space.w_list)
                or space.isinstance_w(w_ob, space.w_tuple)):
            lst_w = space.listview(w_ob)
            if len(lst_w) > len(self.fields_list):
                raise oefmt(space.w_ValueError,
                            "too many initializers for '%s' (got %d)",
                            self.name, len(lst_w))
            for i in range(len(lst_w)):
                optvarsize = self.fields_list[i].write_v(
                    cdata, lst_w[i], optvarsize)
            return optvarsize

        elif space.isinstance_w(w_ob, space.w_dict):
            lst_w = space.fixedview(w_ob)
            for i in range(len(lst_w)):
                w_key = lst_w[i]
                key = space.str_w(w_key)
                try:
                    cf = self.fields_dict[key]
                except KeyError:
                    space.raise_key_error(w_key)
                    assert 0
                optvarsize = cf.write_v(cdata, space.getitem(w_ob, w_key),
                                        optvarsize)
            return optvarsize

        else:
            if optvarsize == -1:
                msg = "list or tuple or dict or struct-cdata"
            else:
                msg = "list or tuple or dict"
            raise self._convert_error(msg, w_ob)

    @jit.elidable
    def _getcfield_const(self, attr):
        return self.fields_dict[attr]

    def getcfield(self, attr):
        if self.fields_dict is not None:
            self = jit.promote(self)
            attr = jit.promote_string(attr)
            try:
                return self._getcfield_const(attr)
            except KeyError:
                pass
        return W_CType.getcfield(self, attr)
Exemple #21
0
#        int num_live_items;
#        int num_ever_used_items;
#        int resize_counter;
#        {byte, short, int, long} *indexes;
#        dictentry *entries;
#        lookup_function_no; # one of the four possible functions for different
#                       # size dicts; the rest of the word is a counter for how
#                       # many 'entries' at the start are known to be deleted
#        (Function DICTKEY, DICTKEY -> bool) *fnkeyeq;
#        (Function DICTKEY -> int) *fnkeyhash;
#    }
#
#


@jit.look_inside_iff(lambda d, key, hash, flag: jit.isvirtual(d))
@jit.oopspec("ordereddict.lookup(d, key, hash, flag)")
def ll_call_lookup_function(d, key, hash, flag):
    fun = d.lookup_function_no & FUNC_MASK
    # This likely() here forces gcc to compile the check for fun == FUNC_BYTE
    # first.  Otherwise, this is a regular switch and gcc (at least 4.7)
    # compiles this as a series of checks, with the FUNC_BYTE case last.
    # It sounds minor, but it is worth 6-7% on a PyPy microbenchmark.
    if likely(fun == FUNC_BYTE):
        return ll_dict_lookup(d, key, hash, flag, TYPE_BYTE)
    elif fun == FUNC_SHORT:
        return ll_dict_lookup(d, key, hash, flag, TYPE_SHORT)
    elif IS_64BIT and fun == FUNC_INT:
        return ll_dict_lookup(d, key, hash, flag, TYPE_INT)
    elif fun == FUNC_LONG:
        return ll_dict_lookup(d, key, hash, flag, TYPE_LONG)
Exemple #22
0
class W_CTypeStructOrUnion(W_CType):
    _immutable_fields_ = [
        'alignment?', '_fields_list?[*]', '_fields_dict?',
        '_custom_field_pos?', '_with_var_array?'
    ]

    # three possible states:
    # - "opaque": for opaque C structs; self.size < 0.
    # - "lazy": for non-opaque C structs whose _fields_list, _fields_dict,
    #       _custom_field_pos and _with_var_array are not filled yet; can be
    #       filled by calling force_lazy_struct().
    #       (But self.size and .alignment are already set and won't change.)
    # - "forced": for non-opaque C structs which are fully ready.

    # fields added by complete_struct_or_union():
    alignment = -1
    _fields_list = None
    _fields_dict = None
    _custom_field_pos = False
    _with_var_array = False

    def __init__(self, space, name):
        W_CType.__init__(self, space, -1, name, len(name))

    def check_complete(self, w_errorcls=None):
        # Check ONLY that are are not opaque.  Complain if we are.
        if self.size < 0:
            space = self.space
            raise oefmt(w_errorcls or space.w_TypeError,
                        "'%s' is opaque or not completed yet", self.name)

    def force_lazy_struct(self):
        # Force a "lazy" struct to become "forced"; complain if we are "opaque".
        if self._fields_list is None:
            self.check_complete()
            #
            from pypy.module._cffi_backend import realize_c_type
            realize_c_type.do_realize_lazy_struct(self)

    def _alignof(self):
        self.check_complete(w_errorcls=self.space.w_ValueError)
        if self.alignment == -1:
            self.force_lazy_struct()
            assert self.alignment > 0
        return self.alignment

    def _fget(self, attrchar):
        if attrchar == 'f':  # fields
            space = self.space
            if self.size < 0:
                return space.w_None
            self.force_lazy_struct()
            result = [None] * len(self._fields_list)
            for fname, field in self._fields_dict.iteritems():
                i = self._fields_list.index(field)
                result[i] = space.newtuple(
                    [space.wrap(fname), space.wrap(field)])
            return space.newlist(result)
        return W_CType._fget(self, attrchar)

    def convert_to_object(self, cdata):
        space = self.space
        self.check_complete()
        return cdataobj.W_CData(space, cdata, self)

    def copy_and_convert_to_object(self, source):
        space = self.space
        self.check_complete()
        ptr = lltype.malloc(rffi.CCHARP.TO,
                            self.size,
                            flavor='raw',
                            zero=False)
        misc._raw_memcopy(source, ptr, self.size)
        return cdataobj.W_CDataNewStd(space, ptr, self)

    def typeoffsetof_field(self, fieldname, following):
        self.force_lazy_struct()
        space = self.space
        try:
            cfield = self._fields_dict[fieldname]
        except KeyError:
            raise OperationError(space.w_KeyError, space.wrap(fieldname))
        if cfield.bitshift >= 0:
            raise OperationError(space.w_TypeError,
                                 space.wrap("not supported for bitfields"))
        return (cfield.ctype, cfield.offset)

    def _copy_from_same(self, cdata, w_ob):
        if isinstance(w_ob, cdataobj.W_CData):
            if w_ob.ctype is self and self.size >= 0:
                with w_ob as ptr:
                    misc._raw_memcopy(ptr, cdata, self.size)
                return True
        return False

    def _check_only_one_argument_for_union(self, w_ob):
        pass

    def convert_from_object(self, cdata, w_ob):
        if not self._copy_from_same(cdata, w_ob):
            self.convert_struct_from_object(cdata, w_ob, optvarsize=-1)

    @jit.look_inside_iff(
        lambda self, cdata, w_ob, optvarsize: jit.isvirtual(w_ob))
    def convert_struct_from_object(self, cdata, w_ob, optvarsize):
        self.force_lazy_struct()
        self._check_only_one_argument_for_union(w_ob)

        space = self.space
        if (space.isinstance_w(w_ob, space.w_list)
                or space.isinstance_w(w_ob, space.w_tuple)):
            lst_w = space.listview(w_ob)
            if len(lst_w) > len(self._fields_list):
                raise oefmt(space.w_ValueError,
                            "too many initializers for '%s' (got %d)",
                            self.name, len(lst_w))
            for i in range(len(lst_w)):
                optvarsize = self._fields_list[i].write_v(
                    cdata, lst_w[i], optvarsize)
            return optvarsize

        elif space.isinstance_w(w_ob, space.w_dict):
            lst_w = space.fixedview(w_ob)
            for i in range(len(lst_w)):
                w_key = lst_w[i]
                key = space.str_w(w_key)
                try:
                    cf = self._fields_dict[key]
                except KeyError:
                    space.raise_key_error(w_key)
                    assert 0
                optvarsize = cf.write_v(cdata, space.getitem(w_ob, w_key),
                                        optvarsize)
            return optvarsize

        else:
            if optvarsize == -1:
                msg = "list or tuple or dict or struct-cdata"
            else:
                msg = "list or tuple or dict"
            raise self._convert_error(msg, w_ob)

    @jit.elidable
    def _getcfield_const(self, attr):
        return self._fields_dict[attr]

    def getcfield(self, attr):
        ready = self._fields_dict is not None
        if not ready and self.size >= 0:
            self.force_lazy_struct()
            ready = True
        if ready:
            self = jit.promote(self)
            attr = jit.promote_string(attr)
            try:
                return self._getcfield_const(attr)
            except KeyError:
                pass
        return W_CType.getcfield(self, attr)
Exemple #23
0
class PyFrame(W_Root):
    """Represents a frame for a regular Python function
    that needs to be interpreted.

    Public fields:
     * 'space' is the object space this frame is running in
     * 'code' is the PyCode object this frame runs
     * 'w_locals' is the locals dictionary to use, if needed, stored on a
       debug object
     * 'w_globals' is the attached globals dictionary
     * 'builtin' is the attached built-in module
     * 'valuestack_w', 'blockstack', control the interpretation

    Cell Vars:
        my local variables that are exposed to my inner functions
    Free Vars:
        variables coming from a parent function in which i'm nested
    'closure' is a list of Cell instances: the received free vars.
    """

    __metaclass__ = extendabletype

    frame_finished_execution = False
    f_generator_wref         = rweakref.dead_ref  # for generators/coroutines
    f_generator_nowref       = None               # (only one of the two attrs)
    last_instr               = -1
    f_backref                = jit.vref_None
    
    escaped                  = False  # see mark_as_escaped()
    debugdata                = None

    pycode = None # code object executed by that frame
    locals_cells_stack_w = None # the list of all locals, cells and the valuestack
    valuestackdepth = 0 # number of items on valuestack
    lastblock = None

    # other fields:
    
    # builtin - builtin cache, only if honor__builtins__ is True
    # defaults to False

    # there is also self.space which is removed by the annotator

    # additionally JIT uses vable_token field that is representing
    # frame current virtualizable state as seen by the JIT

    def __init__(self, space, code, w_globals, outer_func):
        if not we_are_translated():
            assert type(self) == space.FrameClass, (
                "use space.FrameClass(), not directly PyFrame()")
        self = hint(self, access_directly=True, fresh_virtualizable=True)
        assert isinstance(code, pycode.PyCode)
        self.space = space
        self.pycode = code
        if code.frame_stores_global(w_globals):
            self.getorcreatedebug().w_globals = w_globals
        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        size = code.co_nlocals + ncellvars + nfreevars + code.co_stacksize
        # the layout of this list is as follows:
        # | local vars | cells | stack |
        self.locals_cells_stack_w = [None] * size
        self.valuestackdepth = code.co_nlocals + ncellvars + nfreevars
        make_sure_not_resized(self.locals_cells_stack_w)
        check_nonneg(self.valuestackdepth)
        #
        if space.config.objspace.honor__builtins__:
            self.builtin = space.builtin.pick_builtin(w_globals)
        # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
        # class bodies only have CO_NEWLOCALS.
        self.initialize_frame_scopes(outer_func, code)

    def getdebug(self):
        return self.debugdata

    def getorcreatedebug(self):
        if self.debugdata is None:
            self.debugdata = FrameDebugData(self.pycode)
        return self.debugdata

    def get_w_globals(self):
        debugdata = self.getdebug()
        if debugdata is not None:
            return debugdata.w_globals
        return jit.promote(self.pycode).w_globals

    def get_w_f_trace(self):
        d = self.getdebug()
        if d is None:
            return None
        return d.w_f_trace

    def get_is_being_profiled(self):
        d = self.getdebug()
        if d is None:
            return False
        return d.is_being_profiled

    def get_w_locals(self):
        d = self.getdebug()
        if d is None:
            return None
        return d.w_locals

    @not_rpython
    def __repr__(self):
        # useful in tracebacks
        return "<%s.%s executing %s at line %s" % (
            self.__class__.__module__, self.__class__.__name__,
            self.pycode, self.get_last_lineno())

    def _getcell(self, varindex):
        cell = self.locals_cells_stack_w[varindex + self.pycode.co_nlocals]
        assert isinstance(cell, Cell)
        return cell

    def mark_as_escaped(self):
        """
        Must be called on frames that are exposed to applevel, e.g. by
        sys._getframe().  This ensures that the virtualref holding the frame
        is properly forced by ec.leave(), and thus the frame will be still
        accessible even after the corresponding C stack died.
        """
        self.escaped = True

    def append_block(self, block):
        assert block.previous is self.lastblock
        self.lastblock = block

    def pop_block(self):
        block = self.lastblock
        self.lastblock = block.previous
        return block

    def blockstack_non_empty(self):
        return self.lastblock is not None

    def get_blocklist(self):
        """Returns a list containing all the blocks in the frame"""
        lst = []
        block = self.lastblock
        while block is not None:
            lst.append(block)
            block = block.previous
        return lst

    def set_blocklist(self, lst):
        self.lastblock = None
        i = len(lst) - 1
        while i >= 0:
            block = lst[i]
            i -= 1
            block.previous = self.lastblock
            self.lastblock = block

    def get_builtin(self):
        if self.space.config.objspace.honor__builtins__:
            return self.builtin
        else:
            return self.space.builtin

    @jit.unroll_safe
    def initialize_frame_scopes(self, outer_func, code):
        # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
        # class bodies only have CO_NEWLOCALS.
        # CO_NEWLOCALS: make a locals dict unless optimized is also set
        # CO_OPTIMIZED: no locals dict needed at all
        flags = code.co_flags
        if not (flags & pycode.CO_OPTIMIZED):
            if flags & pycode.CO_NEWLOCALS:
                self.getorcreatedebug().w_locals = self.space.newdict(module=True)
            else:
                w_globals = self.get_w_globals()
                assert w_globals is not None
                self.getorcreatedebug().w_locals = w_globals

        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        if not nfreevars:
            if not ncellvars:
                return            # no cells needed - fast path
        elif outer_func is None:
            space = self.space
            raise oefmt(space.w_TypeError,
                        "directly executed code object may not contain free "
                        "variables")
        if outer_func and outer_func.closure:
            closure_size = len(outer_func.closure)
        else:
            closure_size = 0
        if closure_size != nfreevars:
            raise ValueError("code object received a closure with "
                                 "an unexpected number of free variables")
        index = code.co_nlocals
        for i in range(ncellvars):
            self.locals_cells_stack_w[index] = Cell()
            index += 1
        for i in range(nfreevars):
            self.locals_cells_stack_w[index] = outer_func.closure[i]
            index += 1

    def _is_generator_or_coroutine(self):
        return (self.getcode().co_flags & (pycode.CO_COROUTINE |
                                           pycode.CO_GENERATOR)) != 0

    def run(self, name=None, qualname=None):
        """Start this frame's execution."""
        if self._is_generator_or_coroutine():
            return self.initialize_as_generator(name, qualname)
        else:
            # untranslated: check that sys_exc_info is exactly
            # restored after running any Python function.
            # Translated: actually save and restore it, as an attempt to
            # work around rare cases that can occur if RecursionError or
            # MemoryError is raised at just the wrong place
            executioncontext = self.space.getexecutioncontext()
            exc_on_enter = executioncontext.sys_exc_info()
            if we_are_translated():
                try:
                    return self.execute_frame()
                finally:
                    executioncontext.set_sys_exc_info(exc_on_enter)
            else:
                # untranslated, we check consistency, but not in case of
                # interp-level exceptions different than OperationError
                # (e.g. a random failing test, or a pytest Skipped exc.)
                try:
                    w_res = self.execute_frame()
                    assert exc_on_enter is executioncontext.sys_exc_info()
                except OperationError:
                    assert exc_on_enter is executioncontext.sys_exc_info()
                    raise
                return w_res
    run._always_inline_ = True

    def initialize_as_generator(self, name, qualname):
        space = self.space
        if self.getcode().co_flags & pycode.CO_COROUTINE:
            from pypy.interpreter.generator import Coroutine
            gen = Coroutine(self, name, qualname)
            ec = space.getexecutioncontext()
            w_wrapper = ec.w_coroutine_wrapper_fn
        else:
            from pypy.interpreter.generator import GeneratorIterator
            gen = GeneratorIterator(self, name, qualname)
            ec = None
            w_wrapper = None

        if space.config.translation.rweakref:
            self.f_generator_wref = rweakref.ref(gen)
        else:
            self.f_generator_nowref = gen
        w_gen = gen

        if w_wrapper is not None:
            if ec.in_coroutine_wrapper:
                raise oefmt(space.w_RuntimeError,
                            "coroutine wrapper %R attempted "
                            "to recursively wrap %R",
                            w_wrapper, self.getcode())
            ec.in_coroutine_wrapper = True
            try:
                w_gen = space.call_function(w_wrapper, w_gen)
            finally:
                ec.in_coroutine_wrapper = False
        return w_gen

    def execute_frame(self, in_generator=None, w_arg_or_err=None):
        """Execute this frame.  Main entry point to the interpreter.
        'in_generator' is non-None iff we are starting or resuming
        a generator or coroutine frame; in that case, w_arg_or_err
        is the input argument -or- an SApplicationException instance.
        """
        from pypy.interpreter import pyopcode
        # the following 'assert' is an annotation hint: it hides from
        # the annotator all methods that are defined in PyFrame but
        # overridden in the {,Host}FrameClass subclasses of PyFrame.
        assert (isinstance(self, self.space.FrameClass) or
                not self.space.config.translating)
        executioncontext = self.space.getexecutioncontext()
        executioncontext.enter(self)
        got_exception = True
        w_exitvalue = self.space.w_None
        try:
            executioncontext.call_trace(self)
            #
            # Execution starts just after the last_instr.  Initially,
            # last_instr is -1.  After a generator suspends it points to
            # the YIELD_VALUE/YIELD_FROM instruction.
            try:
                try:
                    if in_generator is None:
                        assert self.last_instr == -1
                        next_instr = r_uint(0)
                    else:
                        next_instr = in_generator.resume_execute_frame(
                                                        self, w_arg_or_err)
                except pyopcode.Yield:
                    w_exitvalue = self.popvalue()
                else:
                    w_exitvalue = self.dispatch(self.pycode, next_instr,
                                                executioncontext)
            except OperationError:
                raise
            except Exception as e:      # general fall-back
                raise self._convert_unexpected_exception(e)
            finally:
                executioncontext.return_trace(self, w_exitvalue)
            got_exception = False
        finally:
            executioncontext.leave(self, w_exitvalue, got_exception)
        return w_exitvalue
    execute_frame.insert_stack_check_here = True

    # stack manipulation helpers
    def pushvalue(self, w_object):
        depth = self.valuestackdepth
        self.locals_cells_stack_w[depth] = ll_assert_not_none(w_object)
        self.valuestackdepth = depth + 1

    def pushvalue_none(self):
        depth = self.valuestackdepth
        # the entry is already None, and remains None
        assert self.locals_cells_stack_w[depth] is None
        self.valuestackdepth = depth + 1

    def _check_stack_index(self, index):
        # will be completely removed by the optimizer if only used in an assert
        # and if asserts are disabled
        code = self.pycode
        ncellvars = len(code.co_cellvars)
        nfreevars = len(code.co_freevars)
        stackstart = code.co_nlocals + ncellvars + nfreevars
        return index >= stackstart

    def popvalue(self):
        return ll_assert_not_none(self.popvalue_maybe_none())

    def popvalue_maybe_none(self):
        depth = self.valuestackdepth - 1
        assert self._check_stack_index(depth)
        assert depth >= 0
        w_object = self.locals_cells_stack_w[depth]
        self.locals_cells_stack_w[depth] = None
        self.valuestackdepth = depth
        return w_object


    # we need two popvalues that return different data types:
    # one in case we want list another in case of tuple
    def _new_popvalues():
        @jit.unroll_safe
        def popvalues(self, n):
            values_w = [None] * n
            while True:
                n -= 1
                if n < 0:
                    break
                values_w[n] = self.popvalue()
            return values_w
        return popvalues
    popvalues = _new_popvalues()
    popvalues_mutable = _new_popvalues()
    del _new_popvalues

    @jit.unroll_safe
    def peekvalues(self, n):
        values_w = [None] * n
        base = self.valuestackdepth - n
        assert self._check_stack_index(base)
        assert base >= 0
        while True:
            n -= 1
            if n < 0:
                break
            values_w[n] = self.locals_cells_stack_w[base+n]
        return values_w

    @jit.unroll_safe
    def dropvalues(self, n):
        n = hint(n, promote=True)
        finaldepth = self.valuestackdepth - n
        assert self._check_stack_index(finaldepth)
        assert finaldepth >= 0
        while True:
            n -= 1
            if n < 0:
                break
            self.locals_cells_stack_w[finaldepth+n] = None
        self.valuestackdepth = finaldepth

    @jit.unroll_safe
    def pushrevvalues(self, n, values_w): # n should be len(values_w)
        make_sure_not_resized(values_w)
        while True:
            n -= 1
            if n < 0:
                break
            self.pushvalue(values_w[n])

    @jit.unroll_safe
    def dupvalues(self, n):
        delta = n-1
        while True:
            n -= 1
            if n < 0:
                break
            w_value = self.peekvalue(delta)
            self.pushvalue(w_value)

    def peekvalue(self, index_from_top=0):
        # NOTE: top of the stack is peekvalue(0).
        # Contrast this with CPython where it's PEEK(-1).
        return ll_assert_not_none(self.peekvalue_maybe_none(index_from_top))

    def peekvalue_maybe_none(self, index_from_top=0):
        index_from_top = hint(index_from_top, promote=True)
        index = self.valuestackdepth + ~index_from_top
        assert self._check_stack_index(index)
        assert index >= 0
        return self.locals_cells_stack_w[index]

    def settopvalue(self, w_object, index_from_top=0):
        index_from_top = hint(index_from_top, promote=True)
        index = self.valuestackdepth + ~index_from_top
        assert self._check_stack_index(index)
        assert index >= 0
        self.locals_cells_stack_w[index] = ll_assert_not_none(w_object)

    @jit.unroll_safe
    def dropvaluesuntil(self, finaldepth):
        depth = self.valuestackdepth - 1
        finaldepth = hint(finaldepth, promote=True)
        assert finaldepth >= 0
        while depth >= finaldepth:
            self.locals_cells_stack_w[depth] = None
            depth -= 1
        self.valuestackdepth = finaldepth

    def make_arguments(self, nargs, methodcall=False):
        return Arguments(
                self.space, self.peekvalues(nargs), methodcall=methodcall)

    def argument_factory(self, arguments, keywords, keywords_w, w_star, w_starstar, methodcall=False):
        return Arguments(
                self.space, arguments, keywords, keywords_w, w_star,
                w_starstar, methodcall=methodcall)

    def hide(self):
        return self.pycode.hidden_applevel

    def getcode(self):
        return hint(self.pycode, promote=True)

    @jit.look_inside_iff(lambda self, scope_w: jit.isvirtual(scope_w))
    def setfastscope(self, scope_w):
        """Initialize the fast locals from a list of values,
        where the order is according to self.pycode.signature()."""
        scope_len = len(scope_w)
        if scope_len > self.pycode.co_nlocals:
            raise ValueError("new fastscope is longer than the allocated area")
        # don't assign directly to 'locals_cells_stack_w[:scope_len]' to be
        # virtualizable-friendly
        for i in range(scope_len):
            self.locals_cells_stack_w[i] = scope_w[i]
        self.init_cells()

    def getdictscope(self):
        """
        Get the locals as a dictionary
        """
        self.fast2locals()
        return self.debugdata.w_locals

    def setdictscope(self, w_locals):
        """
        Initialize the locals from a dictionary.
        """
        self.getorcreatedebug().w_locals = w_locals
        self.locals2fast()

    @jit.unroll_safe
    def fast2locals(self):
        # Copy values from the fastlocals to self.w_locals
        d = self.getorcreatedebug()
        if d.w_locals is None:
            d.w_locals = self.space.newdict()
        varnames = self.getcode().getvarnames()
        for i in range(min(len(varnames), self.getcode().co_nlocals)):
            name = varnames[i]
            w_value = self.locals_cells_stack_w[i]
            if w_value is not None:
                self.space.setitem_str(d.w_locals, name, w_value)
            else:
                w_name = self.space.newtext(name)
                try:
                    self.space.delitem(d.w_locals, w_name)
                except OperationError as e:
                    if not e.match(self.space, self.space.w_KeyError):
                        raise

        # cellvars are values exported to inner scopes
        # freevars are values coming from outer scopes
        # (see locals2fast for why CO_OPTIMIZED)
        freevarnames = self.pycode.co_cellvars
        if self.pycode.co_flags & consts.CO_OPTIMIZED:
            freevarnames = freevarnames + self.pycode.co_freevars
        for i in range(len(freevarnames)):
            name = freevarnames[i]
            cell = self._getcell(i)
            try:
                w_value = cell.get()
            except ValueError:
                pass
            else:
                self.space.setitem_str(d.w_locals, name, w_value)


    @jit.unroll_safe
    def locals2fast(self):
        # Copy values from self.w_locals to the fastlocals
        w_locals = self.getorcreatedebug().w_locals
        assert w_locals is not None
        varnames = self.getcode().getvarnames()
        numlocals = self.getcode().co_nlocals

        new_fastlocals_w = [None] * numlocals

        for i in range(min(len(varnames), numlocals)):
            name = varnames[i]
            w_value = self.space.finditem_str(w_locals, name)
            if w_value is not None:
                new_fastlocals_w[i] = w_value

        self.setfastscope(new_fastlocals_w)

        freevarnames = self.pycode.co_cellvars
        if self.pycode.co_flags & consts.CO_OPTIMIZED:
            freevarnames = freevarnames + self.pycode.co_freevars
            # If the namespace is unoptimized, then one of the
            # following cases applies:
            # 1. It does not contain free variables, because it
            #    uses import * or is a top-level namespace.
            # 2. It is a class namespace.
            # We don't want to accidentally copy free variables
            # into the locals dict used by the class.
        for i in range(len(freevarnames)):
            name = freevarnames[i]
            cell = self._getcell(i)
            w_value = self.space.finditem_str(w_locals, name)
            if w_value is not None:
                cell.set(w_value)

    @jit.unroll_safe
    def init_cells(self):
        """
        Initialize cellvars from self.locals_cells_stack_w.
        """
        args_to_copy = self.pycode._args_as_cellvars
        index = self.pycode.co_nlocals
        for i in range(len(args_to_copy)):
            argnum = args_to_copy[i]
            if argnum >= 0:
                cell = self.locals_cells_stack_w[index]
                assert isinstance(cell, Cell)
                cell.set(self.locals_cells_stack_w[argnum])
            index += 1

    def getclosure(self):
        return None

    def fget_code(self, space):
        return self.getcode()

    def fget_getdictscope(self, space):
        return self.getdictscope()

    def fget_w_globals(self, space):
        # bit silly, but GetSetProperty passes a space
        return self.get_w_globals()


    ### line numbers ###

    def fget_f_lineno(self, space):
        "Returns the line number of the instruction currently being executed."
        if self.get_w_f_trace() is None:
            return space.newint(self.get_last_lineno())
        else:
            return space.newint(self.getorcreatedebug().f_lineno)

    def fset_f_lineno(self, space, w_new_lineno):
        "Change the line number of the instruction currently being executed."
        try:
            new_lineno = space.int_w(w_new_lineno)
        except OperationError:
            raise oefmt(space.w_ValueError, "lineno must be an integer")

        if self.get_w_f_trace() is None:
            raise oefmt(space.w_ValueError,
                        "f_lineno can only be set by a trace function.")

        line = self.pycode.co_firstlineno
        if new_lineno < line:
            raise oefmt(space.w_ValueError,
                        "line %d comes before the current code.", new_lineno)
        elif new_lineno == line:
            new_lasti = 0
        else:
            new_lasti = -1
            addr = 0
            lnotab = self.pycode.co_lnotab
            for offset in xrange(0, len(lnotab), 2):
                addr += ord(lnotab[offset])
                line += ord(lnotab[offset + 1])
                if line >= new_lineno:
                    new_lasti = addr
                    new_lineno = line
                    break

        if new_lasti == -1:
            raise oefmt(space.w_ValueError,
                        "line %d comes after the current code.", new_lineno)

        # Don't jump to a line with an except in it.
        code = self.pycode.co_code
        if ord(code[new_lasti]) in (DUP_TOP, POP_TOP):
            raise oefmt(space.w_ValueError,
                        "can't jump to 'except' line as there's no exception")

        # Don't jump inside or out of an except or a finally block.
        # Note that CPython doesn't check except blocks,
        # but that results in crashes (tested on 3.5.2+).
        f_lasti_handler_addr = -1
        new_lasti_handler_addr = -1
        blockstack = []     # current blockstack (addresses of SETUP_*)
        endblock = [-1]     # current finally/except block stack
        addr = 0
        while addr < len(code):
            op = ord(code[addr])
            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH,
                      SETUP_ASYNC_WITH):
                blockstack.append(addr)
            elif op == POP_BLOCK:
                if len(blockstack) == 0:
                    raise oefmt(space.w_SystemError,
                           "POP_BLOCK not properly nested in this bytecode")
                setup_op = ord(code[blockstack.pop()])
                if setup_op != SETUP_LOOP:
                    endblock.append(addr)
            elif op == END_FINALLY:
                if len(endblock) <= 1:
                    raise oefmt(space.w_SystemError,
                           "END_FINALLY not properly nested in this bytecode")
                endblock.pop()

            if addr == new_lasti:
                new_lasti_handler_addr = endblock[-1]
            if addr == self.last_instr:
                f_lasti_handler_addr = endblock[-1]

            if op >= HAVE_ARGUMENT:
                addr += 3
            else:
                addr += 1

        if len(blockstack) != 0 or len(endblock) != 1:
            raise oefmt(space.w_SystemError,
                        "blocks not properly nested in this bytecode")

        if new_lasti_handler_addr != f_lasti_handler_addr:
            raise oefmt(space.w_ValueError,
                        "can't jump into or out of an 'expect' or "
                        "'finally' block (%d -> %d)",
                        f_lasti_handler_addr, new_lasti_handler_addr)

        # now we know we're not jumping into or out of a place which
        # needs a SysExcInfoRestorer.  Check that we're not jumping
        # *into* a block, but only (potentially) out of some blocks.
        if new_lasti < self.last_instr:
            min_addr = new_lasti
            max_addr = self.last_instr
        else:
            min_addr = self.last_instr
            max_addr = new_lasti

        delta_iblock = min_delta_iblock = 0    # see below for comment
        addr = min_addr
        while addr < max_addr:
            op = ord(code[addr])

            if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH,
                      SETUP_ASYNC_WITH):
                delta_iblock += 1
            elif op == POP_BLOCK:
                delta_iblock -= 1
                if delta_iblock < min_delta_iblock:
                    min_delta_iblock = delta_iblock

            if op >= HAVE_ARGUMENT:
                addr += 3
            else:
                addr += 1

        # 'min_delta_iblock' is <= 0; its absolute value is the number of
        # blocks we exit.  'go_iblock' is the delta number of blocks
        # between the last_instr and the new_lasti, in this order.
        if new_lasti > self.last_instr:
            go_iblock = delta_iblock
        else:
            go_iblock = -delta_iblock

        if go_iblock > min_delta_iblock:
            raise oefmt(space.w_ValueError,
                        "can't jump into the middle of a block")
        assert go_iblock <= 0

        for ii in range(-go_iblock):
            block = self.pop_block()
            block.cleanupstack(self)

        self.getorcreatedebug().f_lineno = new_lineno
        self.last_instr = new_lasti

    def get_last_lineno(self):
        "Returns the line number of the instruction currently being executed."
        return pytraceback.offset2lineno(self.pycode, self.last_instr)

    def fget_f_builtins(self, space):
        return self.get_builtin().getdict(space)

    def fget_f_back(self, space):
        f_back = ExecutionContext.getnextframe_nohidden(self)
        return f_back

    def fget_f_lasti(self, space):
        return self.space.newint(self.last_instr)

    def fget_f_trace(self, space):
        return self.get_w_f_trace()

    def fset_f_trace(self, space, w_trace):
        if space.is_w(w_trace, space.w_None):
            self.getorcreatedebug().w_f_trace = None
        else:
            d = self.getorcreatedebug()
            d.w_f_trace = w_trace
            d.f_lineno = self.get_last_lineno()

    def fdel_f_trace(self, space):
        self.getorcreatedebug().w_f_trace = None

    def fget_f_restricted(self, space):
        if space.config.objspace.honor__builtins__:
            return space.newbool(self.builtin is not space.builtin)
        return space.w_False

    def get_generator(self):
        if self.space.config.translation.rweakref:
            return self.f_generator_wref()
        else:
            return self.f_generator_nowref

    def descr_clear(self, space):
        """F.clear(): clear most references held by the frame"""
        # Clears a random subset of the attributes: the local variables
        # and the w_locals.  Note that CPython doesn't clear f_locals
        # (which can create leaks) but it's hard to notice because
        # the next Python-level read of 'frame.f_locals' will clear it.
        if not self.frame_finished_execution:
            if not self._is_generator_or_coroutine():
                raise oefmt(space.w_RuntimeError,
                            "cannot clear an executing frame")
            gen = self.get_generator()
            if gen is not None:
                if gen.running:
                    raise oefmt(space.w_RuntimeError,
                                "cannot clear an executing frame")
                # xxx CPython raises the RuntimeWarning "coroutine was never
                # awaited" in this case too.  Does it make any sense?
                gen.descr_close()

        debug = self.getdebug()
        if debug is not None:
            debug.w_f_trace = None
            if debug.w_locals is not None:
                debug.w_locals = space.newdict()

        # clear the locals, including the cell/free vars, and the stack
        for i in range(len(self.locals_cells_stack_w)):
            w_oldvalue = self.locals_cells_stack_w[i]
            if isinstance(w_oldvalue, Cell):
                w_newvalue = Cell()
            else:
                w_newvalue = None
            self.locals_cells_stack_w[i] = w_newvalue
        self.valuestackdepth = 0
        self.lastblock = None    # the FrameBlock chained list

    def _convert_unexpected_exception(self, e):
        from pypy.interpreter import error

        operr = error.get_converted_unexpected_exception(self.space, e)
        pytraceback.record_application_traceback(
            self.space, operr, self, self.last_instr)
        raise operr
Exemple #24
0
class LLHelpers(AbstractLLHelpers):
    from rpython.rtyper.annlowlevel import llstr, llunicode

    @staticmethod
    @jit.elidable
    def ll_str_mul(s, times):
        if times < 0:
            times = 0
        try:
            size = ovfcheck(len(s.chars) * times)
        except OverflowError:
            raise MemoryError
        newstr = s.malloc(size)
        i = 0
        if i < size:
            s.copy_contents(s, newstr, 0, 0, len(s.chars))
            i += len(s.chars)
        while i < size:
            if i <= size - i:
                j = i
            else:
                j = size - i
            s.copy_contents(newstr, newstr, 0, i, j)
            i += j
        return newstr

    @staticmethod
    @jit.elidable
    def ll_char_mul(ch, times):
        if typeOf(ch) is Char:
            malloc = mallocstr
        else:
            malloc = mallocunicode
        if times < 0:
            times = 0
        newstr = malloc(times)
        j = 0
        # XXX we can use memset here, not sure how useful this is
        while j < times:
            newstr.chars[j] = ch
            j += 1
        return newstr

    @staticmethod
    def ll_strlen(s):
        return len(s.chars)

    @staticmethod
    @signature(types.any(), types.int(), returns=types.any())
    def ll_stritem_nonneg(s, i):
        chars = s.chars
        ll_assert(i >= 0, "negative str getitem index")
        ll_assert(i < len(chars), "str getitem index out of bound")
        return chars[i]

    @staticmethod
    def ll_chr2str(ch):
        if typeOf(ch) is Char:
            malloc = mallocstr
        else:
            malloc = mallocunicode
        s = malloc(1)
        s.chars[0] = ch
        return s

    # @jit.look_inside_iff(lambda str: jit.isconstant(len(str.chars)) and len(str.chars) == 1)
    @staticmethod
    @jit.oopspec("str.str2unicode(str)")
    def ll_str2unicode(str):
        lgt = len(str.chars)
        s = mallocunicode(lgt)
        for i in range(lgt):
            if ord(str.chars[i]) > 127:
                raise UnicodeDecodeError
            s.chars[i] = cast_primitive(UniChar, str.chars[i])
        return s

    @staticmethod
    def ll_str2bytearray(str):
        from rpython.rtyper.lltypesystem.rbytearray import BYTEARRAY

        lgt = len(str.chars)
        b = malloc(BYTEARRAY, lgt)
        for i in range(lgt):
            b.chars[i] = str.chars[i]
        return b

    @staticmethod
    def ll_strhash(s):
        if s:
            return jit.conditional_call_elidable(s.hash, LLHelpers._ll_strhash,
                                                 s)
        else:
            return 0

    @staticmethod
    @dont_inline
    @jit.dont_look_inside
    def _ll_strhash(s):
        # unlike CPython, there is no reason to avoid to return -1
        # but our malloc initializes the memory to zero, so we use zero as the
        # special non-computed-yet value.  Also, jit.conditional_call_elidable
        # always checks for zero, for now.
        x = ll_hash_string(s)
        if x == 0:
            x = 29872897
        s.hash = x
        return x

    @staticmethod
    def ll_length(s):
        return len(s.chars)

    @staticmethod
    def ll_strfasthash(s):
        ll_assert(s.hash != 0, "ll_strfasthash: hash==0")
        return s.hash  # assumes that the hash is already computed

    @staticmethod
    @jit.elidable
    @jit.oopspec('stroruni.concat(s1, s2)')
    def ll_strconcat(s1, s2):
        len1 = s1.length()
        len2 = s2.length()
        # a single '+' like this is allowed to overflow: it gets
        # a negative result, and the gc will complain
        # the typechecks below are if TP == BYTEARRAY
        if typeOf(s1) == Ptr(STR):
            newstr = s2.malloc(len1 + len2)
            newstr.copy_contents_from_str(s1, newstr, 0, 0, len1)
        else:
            newstr = s1.malloc(len1 + len2)
            newstr.copy_contents(s1, newstr, 0, 0, len1)
        if typeOf(s2) == Ptr(STR):
            newstr.copy_contents_from_str(s2, newstr, 0, len1, len2)
        else:
            newstr.copy_contents(s2, newstr, 0, len1, len2)
        return newstr

    @staticmethod
    @jit.elidable
    def ll_strip(s, ch, left, right):
        s_len = len(s.chars)
        if s_len == 0:
            return s.empty()
        lpos = 0
        rpos = s_len - 1
        if left:
            while lpos < rpos and s.chars[lpos] == ch:
                lpos += 1
        if right:
            while lpos < rpos + 1 and s.chars[rpos] == ch:
                rpos -= 1
        if rpos < lpos:
            return s.empty()
        r_len = rpos - lpos + 1
        result = s.malloc(r_len)
        s.copy_contents(s, result, lpos, 0, r_len)
        return result

    @staticmethod
    @jit.elidable
    def ll_strip_default(s, left, right):
        s_len = len(s.chars)
        if s_len == 0:
            return s.empty()
        lpos = 0
        rpos = s_len - 1
        if left:
            while lpos < rpos and s.chars[lpos].isspace():
                lpos += 1
        if right:
            while lpos < rpos + 1 and s.chars[rpos].isspace():
                rpos -= 1
        if rpos < lpos:
            return s.empty()
        r_len = rpos - lpos + 1
        result = s.malloc(r_len)
        s.copy_contents(s, result, lpos, 0, r_len)
        return result

    @staticmethod
    @jit.elidable
    def ll_strip_multiple(s, s2, left, right):
        s_len = len(s.chars)
        if s_len == 0:
            return s.empty()
        lpos = 0
        rpos = s_len - 1
        if left:
            while lpos < rpos and LLHelpers.ll_contains(s2, s.chars[lpos]):
                lpos += 1
        if right:
            while lpos < rpos + 1 and LLHelpers.ll_contains(s2, s.chars[rpos]):
                rpos -= 1
        if rpos < lpos:
            return s.empty()
        r_len = rpos - lpos + 1
        result = s.malloc(r_len)
        s.copy_contents(s, result, lpos, 0, r_len)
        return result

    @staticmethod
    @jit.elidable
    def ll_upper(s):
        s_chars = s.chars
        s_len = len(s_chars)
        if s_len == 0:
            return s.empty()
        i = 0
        result = mallocstr(s_len)
        #        ^^^^^^^^^ specifically to explode on unicode
        while i < s_len:
            result.chars[i] = LLHelpers.ll_upper_char(s_chars[i])
            i += 1
        return result

    @staticmethod
    @jit.elidable
    def ll_lower(s):
        s_chars = s.chars
        s_len = len(s_chars)
        if s_len == 0:
            return s.empty()
        i = 0
        result = mallocstr(s_len)
        #        ^^^^^^^^^ specifically to explode on unicode
        while i < s_len:
            result.chars[i] = LLHelpers.ll_lower_char(s_chars[i])
            i += 1
        return result

    @staticmethod
    def ll_join(s, length, items):
        s_chars = s.chars
        s_len = len(s_chars)
        num_items = length
        if num_items == 0:
            return s.empty()
        itemslen = 0
        i = 0
        while i < num_items:
            try:
                itemslen = ovfcheck(itemslen + len(items[i].chars))
            except OverflowError:
                raise MemoryError
            i += 1
        try:
            seplen = ovfcheck(s_len * (num_items - 1))
        except OverflowError:
            raise MemoryError
        # a single '+' at the end is allowed to overflow: it gets
        # a negative result, and the gc will complain
        result = s.malloc(itemslen + seplen)
        res_index = len(items[0].chars)
        s.copy_contents(items[0], result, 0, 0, res_index)
        i = 1
        while i < num_items:
            s.copy_contents(s, result, 0, res_index, s_len)
            res_index += s_len
            lgt = len(items[i].chars)
            s.copy_contents(items[i], result, 0, res_index, lgt)
            res_index += lgt
            i += 1
        return result

    @staticmethod
    @jit.elidable
    @jit.oopspec('stroruni.cmp(s1, s2)')
    def ll_strcmp(s1, s2):
        if not s1 and not s2:
            return True
        if not s1 or not s2:
            return False
        chars1 = s1.chars
        chars2 = s2.chars
        len1 = len(chars1)
        len2 = len(chars2)

        if len1 < len2:
            cmplen = len1
        else:
            cmplen = len2
        i = 0
        while i < cmplen:
            diff = ord(chars1[i]) - ord(chars2[i])
            if diff != 0:
                return diff
            i += 1
        return len1 - len2

    @staticmethod
    @jit.elidable
    @jit.oopspec('stroruni.equal(s1, s2)')
    def ll_streq(s1, s2):
        if s1 == s2:  # also if both are NULLs
            return True
        if not s1 or not s2:
            return False
        len1 = len(s1.chars)
        len2 = len(s2.chars)
        if len1 != len2:
            return False
        j = 0
        chars1 = s1.chars
        chars2 = s2.chars
        while j < len1:
            if chars1[j] != chars2[j]:
                return False
            j += 1
        return True

    @staticmethod
    @jit.elidable
    def ll_startswith(s1, s2):
        len1 = len(s1.chars)
        len2 = len(s2.chars)
        if len1 < len2:
            return False
        j = 0
        chars1 = s1.chars
        chars2 = s2.chars
        while j < len2:
            if chars1[j] != chars2[j]:
                return False
            j += 1

        return True

    @staticmethod
    def ll_startswith_char(s, ch):
        if not len(s.chars):
            return False
        return s.chars[0] == ch

    @staticmethod
    @jit.elidable
    def ll_endswith(s1, s2):
        len1 = len(s1.chars)
        len2 = len(s2.chars)
        if len1 < len2:
            return False
        j = 0
        chars1 = s1.chars
        chars2 = s2.chars
        offset = len1 - len2
        while j < len2:
            if chars1[offset + j] != chars2[j]:
                return False
            j += 1

        return True

    @staticmethod
    def ll_endswith_char(s, ch):
        if not len(s.chars):
            return False
        return s.chars[len(s.chars) - 1] == ch

    @staticmethod
    @jit.elidable
    @signature(types.any(),
               types.any(),
               types.int(),
               types.int(),
               returns=types.int())
    def ll_find_char(s, ch, start, end):
        i = start
        if end > len(s.chars):
            end = len(s.chars)
        while i < end:
            if s.chars[i] == ch:
                return i
            i += 1
        return -1

    @staticmethod
    @jit.elidable
    @signature(types.any(),
               types.any(),
               types.int(),
               types.int(),
               returns=types.int())
    def ll_rfind_char(s, ch, start, end):
        if end > len(s.chars):
            end = len(s.chars)
        i = end
        while i > start:
            i -= 1
            if s.chars[i] == ch:
                return i
        return -1

    @staticmethod
    @jit.elidable
    def ll_count_char(s, ch, start, end):
        count = 0
        i = start
        if end > len(s.chars):
            end = len(s.chars)
        while i < end:
            if s.chars[i] == ch:
                count += 1
            i += 1
        return count

    @staticmethod
    @signature(types.any(),
               types.any(),
               types.int(),
               types.int(),
               returns=types.int())
    def ll_find(s1, s2, start, end):
        if start < 0:
            start = 0
        if end > len(s1.chars):
            end = len(s1.chars)
        if end - start < 0:
            return -1

        m = len(s2.chars)
        if m == 1:
            return LLHelpers.ll_find_char(s1, s2.chars[0], start, end)

        return LLHelpers.ll_search(s1, s2, start, end, FAST_FIND)

    @staticmethod
    @signature(types.any(),
               types.any(),
               types.int(),
               types.int(),
               returns=types.int())
    def ll_rfind(s1, s2, start, end):
        if start < 0:
            start = 0
        if end > len(s1.chars):
            end = len(s1.chars)
        if end - start < 0:
            return -1

        m = len(s2.chars)
        if m == 1:
            return LLHelpers.ll_rfind_char(s1, s2.chars[0], start, end)

        return LLHelpers.ll_search(s1, s2, start, end, FAST_RFIND)

    @classmethod
    def ll_count(cls, s1, s2, start, end):
        if start < 0:
            start = 0
        if end > len(s1.chars):
            end = len(s1.chars)
        if end - start < 0:
            return 0

        m = len(s2.chars)
        if m == 1:
            return cls.ll_count_char(s1, s2.chars[0], start, end)

        res = cls.ll_search(s1, s2, start, end, FAST_COUNT)
        assert res >= 0
        return res

    @staticmethod
    @jit.elidable
    def ll_search(s1, s2, start, end, mode):
        count = 0
        n = end - start
        m = len(s2.chars)

        if m == 0:
            if mode == FAST_COUNT:
                return end - start + 1
            elif mode == FAST_RFIND:
                return end
            else:
                return start

        w = n - m

        if w < 0:
            if mode == FAST_COUNT:
                return 0
            return -1

        mlast = m - 1
        skip = mlast - 1
        mask = 0

        if mode != FAST_RFIND:
            for i in range(mlast):
                mask = bloom_add(mask, s2.chars[i])
                if s2.chars[i] == s2.chars[mlast]:
                    skip = mlast - i - 1
            mask = bloom_add(mask, s2.chars[mlast])

            i = start - 1
            while i + 1 <= start + w:
                i += 1
                if s1.chars[i + m - 1] == s2.chars[m - 1]:
                    for j in range(mlast):
                        if s1.chars[i + j] != s2.chars[j]:
                            break
                    else:
                        if mode != FAST_COUNT:
                            return i
                        count += 1
                        i += mlast
                        continue

                    if i + m < len(s1.chars):
                        c = s1.chars[i + m]
                    else:
                        c = '\0'
                    if not bloom(mask, c):
                        i += m
                    else:
                        i += skip
                else:
                    if i + m < len(s1.chars):
                        c = s1.chars[i + m]
                    else:
                        c = '\0'
                    if not bloom(mask, c):
                        i += m
        else:
            mask = bloom_add(mask, s2.chars[0])
            for i in range(mlast, 0, -1):
                mask = bloom_add(mask, s2.chars[i])
                if s2.chars[i] == s2.chars[0]:
                    skip = i - 1

            i = start + w + 1
            while i - 1 >= start:
                i -= 1
                if s1.chars[i] == s2.chars[0]:
                    for j in xrange(mlast, 0, -1):
                        if s1.chars[i + j] != s2.chars[j]:
                            break
                    else:
                        return i
                    if i - 1 >= 0 and not bloom(mask, s1.chars[i - 1]):
                        i -= m
                    else:
                        i -= skip
                else:
                    if i - 1 >= 0 and not bloom(mask, s1.chars[i - 1]):
                        i -= m

        if mode != FAST_COUNT:
            return -1
        return count

    @staticmethod
    @signature(types.int(), types.any(), returns=types.any())
    @jit.look_inside_iff(
        lambda length, items: jit.loop_unrolling_heuristic(items, length))
    def ll_join_strs(length, items):
        # Special case for length 1 items, helps both the JIT and other code
        if length == 1:
            return items[0]

        num_items = length
        itemslen = 0
        i = 0
        while i < num_items:
            try:
                itemslen = ovfcheck(itemslen + len(items[i].chars))
            except OverflowError:
                raise MemoryError
            i += 1
        if typeOf(items).TO.OF.TO == STR:
            malloc = mallocstr
            copy_contents = copy_string_contents
        else:
            malloc = mallocunicode
            copy_contents = copy_unicode_contents
        result = malloc(itemslen)
        res_index = 0
        i = 0
        while i < num_items:
            item_chars = items[i].chars
            item_len = len(item_chars)
            copy_contents(items[i], result, 0, res_index, item_len)
            res_index += item_len
            i += 1
        return result

    @staticmethod
    @jit.look_inside_iff(lambda length, chars, RES: jit.isconstant(length) and
                         jit.isvirtual(chars))
    def ll_join_chars(length, chars, RES):
        # no need to optimize this, will be replaced by string builder
        # at some point soon
        num_chars = length
        if RES is StringRepr.lowleveltype:
            target = Char
            malloc = mallocstr
        else:
            target = UniChar
            malloc = mallocunicode
        result = malloc(num_chars)
        res_chars = result.chars
        i = 0
        while i < num_chars:
            res_chars[i] = cast_primitive(target, chars[i])
            i += 1
        return result

    @staticmethod
    @jit.oopspec('stroruni.slice(s1, start, stop)')
    @signature(types.any(), types.int(), types.int(), returns=types.any())
    @jit.elidable
    def _ll_stringslice(s1, start, stop):
        lgt = stop - start
        assert start >= 0
        # If start > stop, return a empty string. This can happen if the start
        # is greater than the length of the string. Use < instead of <= to avoid
        # creating another path for the JIT when start == stop.
        if lgt < 0:
            return s1.empty()
        newstr = s1.malloc(lgt)
        s1.copy_contents(s1, newstr, start, 0, lgt)
        return newstr

    @staticmethod
    def ll_stringslice_startonly(s1, start):
        return LLHelpers._ll_stringslice(s1, start, len(s1.chars))

    @staticmethod
    @signature(types.any(), types.int(), types.int(), returns=types.any())
    def ll_stringslice_startstop(s1, start, stop):
        if jit.we_are_jitted():
            if stop > len(s1.chars):
                stop = len(s1.chars)
        else:
            if stop >= len(s1.chars):
                if start == 0:
                    return s1
                stop = len(s1.chars)
        return LLHelpers._ll_stringslice(s1, start, stop)

    @staticmethod
    def ll_stringslice_minusone(s1):
        newlen = len(s1.chars) - 1
        return LLHelpers._ll_stringslice(s1, 0, newlen)

    @staticmethod
    def ll_split_chr(LIST, s, c, max):
        chars = s.chars
        strlen = len(chars)
        count = 1
        i = 0
        if max == 0:
            i = strlen
        while i < strlen:
            if chars[i] == c:
                count += 1
                if max >= 0 and count > max:
                    break
            i += 1
        res = LIST.ll_newlist(count)
        items = res.ll_items()
        i = 0
        j = 0
        resindex = 0
        if max == 0:
            j = strlen
        while j < strlen:
            if chars[j] == c:
                item = items[resindex] = s.malloc(j - i)
                item.copy_contents(s, item, i, 0, j - i)
                resindex += 1
                i = j + 1
                if max >= 0 and resindex >= max:
                    j = strlen
                    break
            j += 1
        item = items[resindex] = s.malloc(j - i)
        item.copy_contents(s, item, i, 0, j - i)
        return res

    @staticmethod
    def ll_split(LIST, s, c, max):
        count = 1
        if max == -1:
            max = len(s.chars)
        pos = 0
        last = len(s.chars)
        markerlen = len(c.chars)
        pos = s.find(c, 0, last)
        while pos >= 0 and count <= max:
            pos = s.find(c, pos + markerlen, last)
            count += 1
        res = LIST.ll_newlist(count)
        items = res.ll_items()
        pos = 0
        count = 0
        pos = s.find(c, 0, last)
        prev_pos = 0
        if pos < 0:
            items[0] = s
            return res
        while pos >= 0 and count < max:
            item = items[count] = s.malloc(pos - prev_pos)
            item.copy_contents(s, item, prev_pos, 0, pos - prev_pos)
            count += 1
            prev_pos = pos + markerlen
            pos = s.find(c, pos + markerlen, last)
        item = items[count] = s.malloc(last - prev_pos)
        item.copy_contents(s, item, prev_pos, 0, last - prev_pos)
        return res

    @staticmethod
    def ll_rsplit_chr(LIST, s, c, max):
        chars = s.chars
        strlen = len(chars)
        count = 1
        i = 0
        if max == 0:
            i = strlen
        while i < strlen:
            if chars[i] == c:
                count += 1
                if max >= 0 and count > max:
                    break
            i += 1
        res = LIST.ll_newlist(count)
        items = res.ll_items()
        i = strlen
        j = strlen
        resindex = count - 1
        assert resindex >= 0
        if max == 0:
            j = 0
        while j > 0:
            j -= 1
            if chars[j] == c:
                item = items[resindex] = s.malloc(i - j - 1)
                item.copy_contents(s, item, j + 1, 0, i - j - 1)
                resindex -= 1
                i = j
                if resindex == 0:
                    j = 0
                    break
        item = items[resindex] = s.malloc(i - j)
        item.copy_contents(s, item, j, 0, i - j)
        return res

    @staticmethod
    def ll_rsplit(LIST, s, c, max):
        count = 1
        if max == -1:
            max = len(s.chars)
        pos = len(s.chars)
        markerlen = len(c.chars)
        pos = s.rfind(c, 0, pos)
        while pos >= 0 and count <= max:
            pos = s.rfind(c, 0, pos - markerlen)
            count += 1
        res = LIST.ll_newlist(count)
        items = res.ll_items()
        pos = 0
        pos = len(s.chars)
        prev_pos = pos
        pos = s.rfind(c, 0, pos)
        if pos < 0:
            items[0] = s
            return res
        count -= 1
        while pos >= 0 and count > 0:
            item = items[count] = s.malloc(prev_pos - pos - markerlen)
            item.copy_contents(s, item, pos + markerlen, 0,
                               prev_pos - pos - markerlen)
            count -= 1
            prev_pos = pos
            pos = s.rfind(c, 0, pos)
        item = items[count] = s.malloc(prev_pos)
        item.copy_contents(s, item, 0, 0, prev_pos)
        return res

    @staticmethod
    @jit.elidable
    def ll_replace_chr_chr(s, c1, c2):
        length = len(s.chars)
        newstr = s.malloc(length)
        src = s.chars
        dst = newstr.chars
        j = 0
        while j < length:
            c = src[j]
            if c == c1:
                c = c2
            dst[j] = c
            j += 1
        return newstr

    @staticmethod
    @jit.elidable
    def ll_contains(s, c):
        chars = s.chars
        strlen = len(chars)
        i = 0
        while i < strlen:
            if chars[i] == c:
                return True
            i += 1
        return False

    @staticmethod
    @jit.elidable
    def ll_int(s, base):
        if not 2 <= base <= 36:
            raise ValueError
        chars = s.chars
        strlen = len(chars)
        i = 0
        #XXX: only space is allowed as white space for now
        while i < strlen and chars[i] == ' ':
            i += 1
        if not i < strlen:
            raise ValueError
        #check sign
        sign = 1
        if chars[i] == '-':
            sign = -1
            i += 1
        elif chars[i] == '+':
            i += 1
        # skip whitespaces between sign and digits
        while i < strlen and chars[i] == ' ':
            i += 1
        #now get digits
        val = 0
        oldpos = i
        while i < strlen:
            c = ord(chars[i])
            if ord('a') <= c <= ord('z'):
                digit = c - ord('a') + 10
            elif ord('A') <= c <= ord('Z'):
                digit = c - ord('A') + 10
            elif ord('0') <= c <= ord('9'):
                digit = c - ord('0')
            else:
                break
            if digit >= base:
                break
            val = val * base + digit
            i += 1
        if i == oldpos:
            raise ValueError  # catch strings like '+' and '+  '
        #skip trailing whitespace
        while i < strlen and chars[i] == ' ':
            i += 1
        if not i == strlen:
            raise ValueError
        return sign * val

    # interface to build strings:
    #   x = ll_build_start(n)
    #   ll_build_push(x, next_string, 0)
    #   ll_build_push(x, next_string, 1)
    #   ...
    #   ll_build_push(x, next_string, n-1)
    #   s = ll_build_finish(x)

    @staticmethod
    def ll_build_start(parts_count):
        return malloc(TEMP, parts_count)

    @staticmethod
    def ll_build_push(builder, next_string, index):
        builder[index] = next_string

    @staticmethod
    def ll_build_finish(builder):
        return LLHelpers.ll_join_strs(len(builder), builder)

    @staticmethod
    @specialize.memo()
    def ll_constant(s):
        return string_repr.convert_const(s)

    @staticmethod
    @specialize.memo()
    def ll_constant_unicode(s):
        return unicode_repr.convert_const(s)

    @classmethod
    def do_stringformat(cls, hop, sourcevarsrepr):
        s_str = hop.args_s[0]
        assert s_str.is_constant()
        is_unicode = isinstance(s_str, annmodel.SomeUnicodeString)
        if is_unicode:
            TEMPBUF = TEMP_UNICODE
        else:
            TEMPBUF = TEMP
        s = s_str.const
        things = cls.parse_fmt_string(s)
        size = inputconst(Signed, len(things))  # could be unsigned?
        cTEMP = inputconst(Void, TEMPBUF)
        cflags = inputconst(Void, {'flavor': 'gc'})
        vtemp = hop.genop("malloc_varsize", [cTEMP, cflags, size],
                          resulttype=Ptr(TEMPBUF))

        argsiter = iter(sourcevarsrepr)

        from rpython.rtyper.rclass import InstanceRepr
        for i, thing in enumerate(things):
            if isinstance(thing, tuple):
                code = thing[0]
                vitem, r_arg = argsiter.next()
                if not hasattr(r_arg, 'll_str'):
                    raise TyperError("ll_str unsupported for: %r" % r_arg)
                if code == 's':
                    if is_unicode:
                        # only UniCharRepr and UnicodeRepr has it so far
                        vchunk = hop.gendirectcall(r_arg.ll_unicode, vitem)
                    else:
                        vchunk = hop.gendirectcall(r_arg.ll_str, vitem)
                elif code == 'r' and isinstance(r_arg, InstanceRepr):
                    vchunk = hop.gendirectcall(r_arg.ll_str, vitem)
                elif code == 'd':
                    assert isinstance(r_arg, IntegerRepr)
                    #vchunk = hop.gendirectcall(r_arg.ll_str, vitem)
                    vchunk = hop.gendirectcall(ll_str.ll_int2dec, vitem)
                elif code == 'f':
                    #assert isinstance(r_arg, FloatRepr)
                    vchunk = hop.gendirectcall(r_arg.ll_str, vitem)
                elif code == 'x':
                    assert isinstance(r_arg, IntegerRepr)
                    vchunk = hop.gendirectcall(ll_str.ll_int2hex, vitem,
                                               inputconst(Bool, False))
                elif code == 'o':
                    assert isinstance(r_arg, IntegerRepr)
                    vchunk = hop.gendirectcall(ll_str.ll_int2oct, vitem,
                                               inputconst(Bool, False))
                else:
                    raise TyperError("%%%s is not RPython" % (code, ))
            else:
                if is_unicode:
                    vchunk = inputconst(unicode_repr, thing)
                else:
                    vchunk = inputconst(string_repr, thing)
            i = inputconst(Signed, i)
            if is_unicode and vchunk.concretetype != Ptr(UNICODE):
                # if we are here, one of the ll_str.* functions returned some
                # STR, so we convert it to unicode. It's a bit suboptimal
                # because we do one extra copy.
                vchunk = hop.gendirectcall(cls.ll_str2unicode, vchunk)
            hop.genop('setarrayitem', [vtemp, i, vchunk])

        hop.exception_cannot_occur()  # to ignore the ZeroDivisionError of '%'
        return hop.gendirectcall(cls.ll_join_strs, size, vtemp)

    @staticmethod
    @jit.dont_look_inside
    def ll_string2list(RESLIST, src):
        length = len(src.chars)
        lst = RESLIST.ll_newlist(length)
        dst = lst.ll_items()
        SRC = typeOf(src).TO  # STR or UNICODE
        DST = typeOf(dst).TO  # GcArray
        assert DST.OF is SRC.chars.OF
        # from here, no GC operations can happen
        asrc = llmemory.cast_ptr_to_adr(src) + (llmemory.offsetof(
            SRC, 'chars') + llmemory.itemoffsetof(SRC.chars, 0))
        adst = llmemory.cast_ptr_to_adr(dst) + llmemory.itemoffsetof(DST, 0)
        llmemory.raw_memcopy(asrc, adst, llmemory.sizeof(DST.OF) * length)
        # end of "no GC" section
        keepalive_until_here(src)
        keepalive_until_here(dst)
        return lst
Exemple #25
0
class W_CTypeStructOrUnion(W_CType):
    _immutable_fields_ = [
        'alignment?', '_fields_list?[*]', '_fields_dict?',
        '_custom_field_pos?', '_with_var_array?'
    ]
    is_indirect_arg_for_call_python = True

    # three possible states:
    # - "opaque": for opaque C structs; self.size < 0.
    # - "lazy": for non-opaque C structs whose _fields_list, _fields_dict,
    #       _custom_field_pos and _with_var_array are not filled yet; can be
    #       filled by calling force_lazy_struct().
    #       (But self.size and .alignment are already set and won't change.)
    # - "forced": for non-opaque C structs which are fully ready.

    # fields added by complete_struct_or_union():
    alignment = -1
    _fields_list = None
    _fields_dict = None
    _custom_field_pos = False
    _with_var_array = False
    _with_packed_changed = False

    def __init__(self, space, name):
        W_CType.__init__(self, space, -1, name, len(name))

    def check_complete(self, w_errorcls=None):
        # Check ONLY that are are not opaque.  Complain if we are.
        if self.size < 0:
            space = self.space
            raise oefmt(w_errorcls or space.w_TypeError,
                        "'%s' is opaque or not completed yet", self.name)

    def force_lazy_struct(self):
        # Force a "lazy" struct to become "forced"; complain if we are "opaque".
        if self._fields_list is None:
            self.check_complete()
            #
            from pypy.module._cffi_backend import realize_c_type
            realize_c_type.do_realize_lazy_struct(self)

    def _alignof(self):
        self.check_complete(w_errorcls=self.space.w_ValueError)
        if self.alignment == -1:
            self.force_lazy_struct()
            assert self.alignment > 0
        return self.alignment

    def _fget(self, attrchar):
        if attrchar == 'f':  # fields
            space = self.space
            if self.size < 0:
                return space.w_None
            self.force_lazy_struct()
            result = [None] * len(self._fields_list)
            for fname, field in self._fields_dict.iteritems():
                i = self._fields_list.index(field)
                result[i] = space.newtuple([space.newtext(fname), field])
            return space.newlist(result)
        return W_CType._fget(self, attrchar)

    def convert_to_object(self, cdata):
        space = self.space
        self.check_complete()
        return cdataobj.W_CData(space, cdata, self)

    def copy_and_convert_to_object(self, source):
        space = self.space
        self.check_complete()
        ptr = lltype.malloc(rffi.CCHARP.TO,
                            self.size,
                            flavor='raw',
                            zero=False)
        misc._raw_memcopy(source, ptr, self.size)
        return cdataobj.W_CDataNewStd(space, ptr, self)

    def typeoffsetof_field(self, fieldname, following):
        self.force_lazy_struct()
        space = self.space
        try:
            cfield = self._getcfield_const(fieldname)
        except KeyError:
            raise OperationError(space.w_KeyError, space.newtext(fieldname))
        if cfield.bitshift >= 0:
            raise oefmt(space.w_TypeError, "not supported for bitfields")
        return (cfield.ctype, cfield.offset)

    def _copy_from_same(self, cdata, w_ob):
        if isinstance(w_ob, cdataobj.W_CData):
            if w_ob.ctype is self and self.size >= 0:
                with w_ob as ptr:
                    misc._raw_memcopy(ptr, cdata, self.size)
                return True
        return False

    def convert_from_object(self, cdata, w_ob):
        if not self._copy_from_same(cdata, w_ob):
            self.convert_struct_from_object(cdata, w_ob, optvarsize=-1)

    @jit.look_inside_iff(
        lambda self, cdata, w_ob, optvarsize: jit.isvirtual(w_ob))
    def convert_struct_from_object(self, cdata, w_ob, optvarsize):
        self.force_lazy_struct()

        space = self.space
        if (space.isinstance_w(w_ob, space.w_list)
                or space.isinstance_w(w_ob, space.w_tuple)):
            lst_w = space.listview(w_ob)
            j = 0
            for w_obj in lst_w:
                try:
                    while (self._fields_list[j].flags
                           & W_CField.BF_IGNORE_IN_CTOR):
                        j += 1
                except IndexError:
                    raise oefmt(space.w_ValueError,
                                "too many initializers for '%s' (got %d)",
                                self.name, len(lst_w))
                optvarsize = self._fields_list[j].write_v(
                    cdata, w_obj, optvarsize)
                j += 1
            return optvarsize

        elif space.isinstance_w(w_ob, space.w_dict):
            lst_w = space.fixedview(w_ob)
            for i in range(len(lst_w)):
                w_key = lst_w[i]
                key = space.text_w(w_key)
                try:
                    cf = self._fields_dict[key]
                except KeyError:
                    space.raise_key_error(w_key)
                    assert 0
                optvarsize = cf.write_v(cdata, space.getitem(w_ob, w_key),
                                        optvarsize)
            return optvarsize

        else:
            if optvarsize == -1:
                msg = "list or tuple or dict or struct-cdata"
            else:
                msg = "list or tuple or dict"
            raise self._convert_error(msg, w_ob)

    @jit.elidable
    def _getcfield_const(self, attr):
        return self._fields_dict[attr]

    def getcfield(self, attr):
        # Returns a W_CField.  Error cases: returns None if we are an
        # opaque struct; or raises KeyError if the particular field
        # 'attr' does not exist.  The point of not directly building the
        # error here is to get the exact ctype in the error message: it
        # might be of the kind 'struct foo' or 'struct foo *'.
        if self._fields_dict is None:
            if self.size < 0:
                return None
            self.force_lazy_struct()
        self = jit.promote(self)
        attr = jit.promote_string(attr)
        return self._getcfield_const(attr)  # <= KeyError here

    def cdata_dir(self):
        if self.size < 0:
            return []
        self.force_lazy_struct()
        return self._fields_dict.keys()
Exemple #26
0
    ll_builder.current_pos = pos + size
    ll_builder.copy_raw_to_string(charp, ll_builder.current_buf, pos, size)

# ------------------------------------------------------------
# builder.getlength()

@always_inline
def ll_getlength(ll_builder):
    num_chars_missing_from_last_piece = (
        ll_builder.current_end - ll_builder.current_pos)
    return ll_builder.total_size - num_chars_missing_from_last_piece

# ------------------------------------------------------------
# builder.build()

@jit.look_inside_iff(lambda ll_builder: jit.isvirtual(ll_builder))
def ll_build(ll_builder):
    # NB. usually the JIT doesn't look inside this function; it does
    # so only in the simplest example where it could virtualize everything
    if ll_builder.extra_pieces:
        ll_fold_pieces(ll_builder)
    elif ll_builder.current_pos != ll_builder.total_size:
        ll_shrink_final(ll_builder)
    return ll_builder.current_buf

def ll_shrink_final(ll_builder):
    final_size = ll_builder.current_pos
    ll_assert(final_size <= ll_builder.total_size,
              "final_size > ll_builder.total_size?")
    buf = rgc.ll_shrink_array(ll_builder.current_buf, final_size)
    ll_builder.current_buf = buf
Exemple #27
0
#        int num_live_items;
#        int num_ever_used_items;
#        int resize_counter;
#        {byte, short, int, long} *indexes;
#        dictentry *entries;
#        lookup_function_no; # one of the four possible functions for different
#                       # size dicts; the rest of the word is a counter for how
#                       # many 'entries' at the start are known to be deleted
#        (Function DICTKEY, DICTKEY -> bool) *fnkeyeq;
#        (Function DICTKEY -> int) *fnkeyhash;
#    }
#
#


@jit.look_inside_iff(lambda d, key, hash, flag: jit.isvirtual(d))
@jit.oopspec('ordereddict.lookup(d, key, hash, flag)')
def ll_call_lookup_function(d, key, hash, flag):
    fun = d.lookup_function_no & FUNC_MASK
    # This likely() here forces gcc to compile the check for fun == FUNC_BYTE
    # first.  Otherwise, this is a regular switch and gcc (at least 4.7)
    # compiles this as a series of checks, with the FUNC_BYTE case last.
    # It sounds minor, but it is worth 6-7% on a PyPy microbenchmark.
    if likely(fun == FUNC_BYTE):
        return ll_dict_lookup(d, key, hash, flag, TYPE_BYTE)
    elif fun == FUNC_SHORT:
        return ll_dict_lookup(d, key, hash, flag, TYPE_SHORT)
    elif IS_64BIT and fun == FUNC_INT:
        return ll_dict_lookup(d, key, hash, flag, TYPE_INT)
    elif fun == FUNC_LONG:
        return ll_dict_lookup(d, key, hash, flag, TYPE_LONG)