示例#1
0
  def insert( self, ip_next, op, raw, arg, typ ) :

    ip = self._ip
    oplen = ip_next-ip

    blk = self._blk
    if blk is None :

      blk = Block()

      self._blocks[ ip ] = blk
      self._blk = blk

    apply_delta,force_flush,targets = self._effects[op]

    delta = None
    if apply_delta :
      blk.delta += opcode.stack_effect( op, arg )
      blk.max_delta = max( blk.max_delta, blk.delta )
      delta = blk.delta

    blk.instructions.append((ip,oplen,op,raw,arg,typ,delta))

    if force_flush or ip_next in self._labeled :
      for dst,*eff in targets :
        blk.targets[dst( ip_next, raw )] = eff
      blk.delta_includes_last = (delta is not None)
      self._blk = None

    self._ip = ip_next
示例#2
0
def stack_table(d):
    from collections import OrderedDict, defaultdict
    stacks = defaultdict(set)
    stack_names = defaultdict(set)
    for k, v in d.items():
        try:
            stack = opcode.stack_effect(k)
            stacks[stack].add(k)
            stack_names[stack].add(v)
        except ValueError:
            try:
                stack = opcode.stack_effect(k, 0)
                stacks[stack].add(k)
                stack_names[stack].add(v)
            except ValueError:
                pass

    return stacks, stack_names
示例#3
0
    def add(self, opcode, *args):
        delta = stack_effect(opcode, *args)
        self.stk_ptr += delta
        assert self.stk_ptr >= 0, self.stk_ptr
        if self.stk_ptr > self.stk_use:
            self.stk_use = self.stk_ptr

        self.buf.writebin("B", opcode)
        if args != ():
            arg = args[0]
        fl, extra = upyopcodes.mp_opcode_type(opcode)

        if opcode in (opmap["CALL_FUNCTION"], opmap["CALL_FUNCTION_VAR_KW"],
                      opmap["CALL_METHOD"], opmap["CALL_METHOD_VAR_KW"]):
            MPYWriter.write_uint(None, args[0] + (args[1] << 8), self.buf)
        elif opcode == opmap["LOAD_CONST_SMALL_INT"]:
            MPYWriter.write_int(None, arg, self.buf)
        elif opcode == opmap["LOAD_CONST_OBJ"]:
            MPYWriter.write_uint(None, len(self.mpy_consts), self.buf)
            self.mpy_consts.append(arg)
        elif opcode in (opmap["MAKE_FUNCTION"], opmap["MAKE_FUNCTION_DEFARGS"],
                        opmap["MAKE_CLOSURE"], opmap["MAKE_CLOSURE_DEFARGS"]):
            if self.codeobj_off >= 0:
                MPYWriter.write_uint(None,
                                     len(self.mpy_codeobjs) + self.codeobj_off,
                                     self.buf)
                self.mpy_codeobjs.append(arg)
            else:
                MPYWriter.write_uint(None, len(self.mpy_consts), self.buf)
                self.mpy_consts.append(arg)
            if opcode in (opmap["MAKE_CLOSURE"],
                          opmap["MAKE_CLOSURE_DEFARGS"]):
                self.buf.writebin("B", args[1])
        elif opcode == opmap["RAISE_VARARGS"]:
            self.buf.writebin("B", arg)
        elif fl == upyopcodes.MP_OPCODE_OFFSET:
            self.buf.writebin("<H", arg)
        elif fl == upyopcodes.MP_OPCODE_QSTR:
            if self.only_for_mpy:
                self.buf.writebin("<H", 0)
            else:
                self.buf.writebin("<H", str2qstr(arg))
            # cache
            if opcode in upyopcodes.hascache:
                self.buf.writebin("B", 0)
            self.co_names.append(arg)
        elif fl == upyopcodes.MP_OPCODE_VAR_UINT:
            MPYWriter.write_uint(None, arg, self.buf)

        if opcode in (opmap["SETUP_EXCEPT"], opmap["SETUP_FINALLY"],
                      opmap["SETUP_WITH"]):
            self.exc_stk_ptr += 1
            if self.exc_stk_ptr > self.exc_stk_use:
                self.exc_stk_use = self.exc_stk_ptr
        elif opcode == opmap["END_FINALLY"]:
            self.exc_stk_ptr -= 1
示例#4
0
    def add(self, opcode, *args):
        delta = stack_effect(opcode, *args)
        self.stk_ptr += delta
        assert self.stk_ptr >= 0, self.stk_ptr
        if self.stk_ptr > self.stk_use:
            self.stk_use = self.stk_ptr

        self.buf.writebin("B", opcode)
        if args != ():
            arg = args[0]
        fl, extra = upyopcodes.mp_opcode_type(opcode)

        if opcode == opmap["CALL_FUNCTION"]:
            MPYOutput.write_uint(None, args[0] + (args[1] << 8), self.buf)
        elif opcode == opmap["LOAD_CONST_SMALL_INT"]:
            MPYOutput.write_int(None, arg, self.buf)
        elif opcode == opmap["LOAD_CONST_OBJ"]:
            MPYOutput.write_uint(None, len(self.co_consts), self.buf)
            self.co_consts.append(arg)
        elif opcode == opmap["MAKE_FUNCTION"]:
            MPYOutput.write_uint(None, len(self.co_consts), self.buf)
            self.co_consts.append(arg)
        elif fl == upyopcodes.MP_OPCODE_OFFSET:
            self.buf.writebin("<H", arg)
        elif fl == upyopcodes.MP_OPCODE_QSTR:
            if self.only_for_mpy:
                self.buf.writebin("<H", 0)
            else:
                self.buf.writebin("<H", id(sys.intern(arg)) >> 2)
            # cache
            if opcode in upyopcodes.hascache:
                self.buf.writebin("B", 0)
            self.co_names.append(arg)
        elif fl == upyopcodes.MP_OPCODE_VAR_UINT:
            MPYOutput.write_uint(None, arg, self.buf)

        if opcode in (opmap["SETUP_EXCEPT"], opmap["SETUP_FINALLY"]):
            self.exc_stk_ptr += 1
            if self.exc_stk_ptr > self.exc_stk_use:
                self.exc_stk_use = self.exc_stk_ptr
        elif opcode == opmap["END_FINALLY"]:
            self.exc_stk_ptr -= 1
示例#5
0
 def update_event(self, inp=-1):
     self.set_output_val(0, opcode.stack_effect(self.input(0),
                                                self.input(1)))
示例#6
0
def _find_lambdex_blocks(code: types.CodeType) -> Sequence[_LambdexBlock]:
    """
    Find all lambdex block in `code.co_code`.

    The returned sequence does not assume any ordering (so that you may sort by yourself).
    """
    # Storing them locally for faster accessing
    names = code.co_names
    consts = code.co_consts
    freevars = code.co_freevars
    cellvars = code.co_cellvars
    closures = cellvars + freevars

    # Variables related to offset-lineno lookup
    linestarts = list(dis.findlinestarts(code))
    i_linestarts = -1
    n_linestarts = len(linestarts)
    lineno = -1  # The current line number

    # Variables related to currently processing blocks
    # NOTE that blocks may be cascaded (e.g., another lambdex being the default arg value
    # of one lambdex), so we need a stack-like structure
    blocks = []
    curr_block = None

    stack_depth = 0  # A single integer emulating stack evolution
    prev_op = None  # The previous op

    for offset, op, arg in dis._unpack_opargs(code.co_code):
        # Update lineno if necessary
        if (n_linestarts - 1 > i_linestarts
                and linestarts[i_linestarts + 1][0] <= offset):
            i_linestarts += 1
            lineno = linestarts[i_linestarts][1]

        # If matched a LOAD_GLOBAL / LOAD_NAME def_, start a new block
        if op in {LOAD_GLOBAL, LOAD_NAME} and names[arg] in get_declarers():
            curr_block = _LambdexBlock()
            curr_block.keyword = names[arg]
            curr_block.lineno = lineno
            curr_block.offset_start = offset
            curr_block.stack_depth = stack_depth
            curr_block.offset_start_make_lambda = offset
            blocks.append(curr_block)

        # A jump op may be encountered when building default arg values, e.g.,
        # `and`, `or` or `...if...else...` expressions.  We won't perform any
        # updates on the current block, but only discover new blocks before
        # reaching the jump target.  Before this time, the stack may be messed
        # up, so we restore the `stack_depth` after jumping.

        # We record metadata of jumping only if
        #  1) some blocks are being processed;
        #  2) the currently processed block is not jumping.
        if not blocks or curr_block.offset_jump is not None:
            pass
        elif op in HASJABS:
            effect = JABS_STACK_EFFECT_AFTER_JUMP[op]
            curr_block.offset_jump = arg
            curr_block.stack_depth_after_jump = stack_depth + effect
        elif op in HASJREL:
            effect = JREL_STACK_EFFECT_AFTER_JUMP[op]
            curr_block.offset_jump = arg + offset + 2
            curr_block.stack_depth_after_jump = stack_depth + effect

        # If reaching a jump target, we restore the stack depth
        if blocks and curr_block.offset_jump == offset:
            curr_block.offset_jump = None
            stack_depth = curr_block.stack_depth_after_jump

        # Update the stack depth as if (op, arg) is performed
        if (
                op != EXTENDED_ARG
        ):  # In Python <= 3.7, EXTENDED_ARG as argument will cause ValueError
            stack_depth += stack_effect(op, arg)

        # In the following branches, we update the current block and decide whether
        # the block is finished or broken
        if not blocks or curr_block.offset_jump is not None:
            pass
        elif curr_block.stack_depth >= stack_depth:
            # If the function `def_` or `def_.<ident>` popped unexpectedly,
            # we consider the current block as broken
            blocks.pop()
            if blocks:
                curr_block = blocks[-1]
        elif op == LOAD_METHOD and offset == curr_block.offset_start + 2:
            # If LOAD_METHOD met just after offset_start, record the name as identifier
            curr_block.identifier = names[arg]
            curr_block.offset_start_make_lambda = offset
        elif op == LOAD_CONST and iscode(consts[arg]):
            # If loading a code object, store it in `.lambda_node` (so that the last one preserved)
            curr_block.lambda_code = consts[arg]
            curr_block.code_const_idx = arg
        elif op == LOAD_CLOSURE:
            # If LOAD_CLOSURE met, record the arg as a freevar
            curr_block.freevars.append(closures[arg])
            curr_block.freevar_opargs.append(arg)
        elif prev_op == LOAD_CLOSURE and op == BUILD_TUPLE:
            # If making closure tuple, record the offset
            curr_block.offset_end_make_closure_tuple = offset
        elif op == MAKE_FUNCTION:
            # If MAKE_FUNCTION met, record the offset (so that the last one preserved)
            curr_block.make_function_mode = arg
        elif (op in {CALL_FUNCTION, CALL_METHOD}
              and stack_depth == curr_block.stack_depth + 1):
            # If CALL_FUNCTION / CALL_METHOD met and the stack is balanced, finish the current block
            curr_block.offset_end = offset
            yield blocks.pop()
            if blocks:
                curr_block = blocks[-1]

        prev_op = op
示例#7
0
def stack_effects(opcode, oparg=None):
    return opcode.stack_effect(opcode=opcode, oparg=oparg)