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
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
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
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
def update_event(self, inp=-1): self.set_output_val(0, opcode.stack_effect(self.input(0), self.input(1)))
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
def stack_effects(opcode, oparg=None): return opcode.stack_effect(opcode=opcode, oparg=oparg)