def disassemble(co, indent, op_counts, f): """Copied from dis module. Args: co: code object indent: indentation to print with It doesn't do the indent we want. """ def out(*args, **kwargs): print(*args, file=f, **kwargs) code = co.co_code labels = dis.findlabels(code) linestarts = dict(dis.findlinestarts(co)) n = len(code) i = 0 extended_arg = 0 free = None while i < n: c = code[i] op = ord(c) op_counts[op] += 1 if i in linestarts: if i > 0: out() prefix = linestarts[i] else: prefix = '' out('%s%4s' % (indent, prefix), end=' ') if i in labels: # Jump targets get a special symbol arrow = '>>' else: arrow = ' ' out(' %s %4r %-20s ' % (arrow, i, dis.opname[op]), end=' ') i += 1 if op >= dis.HAVE_ARGUMENT: oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg extended_arg = 0 i += 2 if op == dis.EXTENDED_ARG: extended_arg = oparg*65536L oparg_str = None if op in dis.hasconst: c = co.co_consts[oparg] if isinstance(c, types.CodeType): # %r prints a memory address, which inhibits diffing oparg_str = '(code object %s %s %s)' % ( c.co_name, c.co_filename, c.co_firstlineno) else: oparg_str = '(%r)' % (c,) elif op in dis.hasname: oparg_str = '(%s)' % (co.co_names[oparg],) elif op in dis.hasjrel: oparg_str = '(to %r)' % (i + oparg,) elif op in dis.haslocal: oparg_str = '(%s)' % (co.co_varnames[oparg],) elif op in dis.hascompare: oparg_str = '(%s)' % (dis.cmp_op[oparg],) elif op in dis.hasfree: if free is None: free = co.co_cellvars + co.co_freevars oparg_str = '(%s)' % (free[oparg],) if oparg_str: out('%5r %s' % (oparg, oparg_str), end=' ') else: out('%5r' % oparg, end=' ') out()
def run_frame(self, frame): """Run a frame until it returns or raises an exception. This function raises GuestException or returns the return value. Corresponds to PyEval_EvalFrameEx in ceval.c. That returns 'PyObject* retval' -- but how does it indicate an exception? I think retval is NULL, and then """ # bytecode offset -> line number #print('frame %s ' % frame) # NOTE: Also done in Frmae.line_number() linestarts = dict(dis.findlinestarts(frame.f_code)) #print('STARTS %s ' % linestarts) self._push_frame(frame) num_ticks = 0 while True: num_ticks += 1 opoffset = self.frame.f_lasti # For logging only byteName, arguments = self.frame.decode_next() if self.verbose: self.logTick(byteName, arguments, opoffset, linestarts) # When unwinding the block stack, we need to keep track of why we # are doing it. # NOTE: In addition to returning why == 'exception', this can also # RAISE GuestException from recursive call via call_function. why = self.dispatch(byteName, arguments) if why == 'exception': # TODO: ceval calls PyTraceBack_Here, not sure what that does. pass if why == 'reraise': why = 'exception' if why != 'yield': # NOTE: why is used in a frame INTERNALLY after bytecode dispatch. # But what about ACROSS frames. We need to unwind the call # stack too! How is that done? # I don't want it to be done with GuestException! while why and frame.block_stack: debug('WHY %s', why) debug('STACK %s', frame.block_stack) why = self.frame.handle_block_stack(why, self) if why: break # TODO: handle generator exception state self._pop_frame() if why == 'exception': exctype, value, tb = self.last_exception #debug('exctype: %s' % exctype) #debug('value: %s' % value) #debug('unused tb: %s' % tb) if self.more_info: # Recursive function calls can cause this I guess. if isinstance(value, GuestException): raise value else: # Raise an exception with the EMULATED (guest) stack frames. raise GuestException(exctype, value, self.except_frames) else: raise exctype, value, tb #debug1('num_ticks: %d' % num_ticks) return self.return_value