PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/reassembly.py

https://bitbucket.org/fj_/randomscripts
Python | 125 lines | 113 code | 1 blank | 11 comment | 0 complexity | 743e104f310fbf8c037511d8dba87fa1 MD5 | raw file
  1. from dis import disassemble
  2. from opcode import HAVE_ARGUMENT, opname, opmap, hasjabs
  3. import inspect
  4. from StringIO import StringIO
  5. import new
  6. '''
  7. Doesn't work.
  8. The problem is, I can't capture the local value stack and, more importantly,
  9. block stack. Which means that `for i in range(10)` can't be properly restored,
  10. given that range iterator is stored in one of the them.
  11. '''
  12. class Saved_frame(object):
  13. def __init__(self, ip, locals, retval):
  14. self.ip = ip
  15. self.locals = locals
  16. self.retval = retval
  17. def save_frame(retval):
  18. frame = inspect.currentframe(1)
  19. ldict = frame.f_locals
  20. print 'Locals: {!r}'.format(ldict)
  21. locals = []
  22. for name in frame.f_code.co_varnames:
  23. print name, ldict[name]
  24. locals.append(ldict[name])
  25. return Saved_frame(frame.f_lasti, locals, retval)
  26. class Instruction(object):
  27. def __init__(self, addr, op, arg = None):
  28. self.addr = addr
  29. self.op = op
  30. self.arg = arg
  31. def __repr__(self):
  32. s = '{:>3}: {}'.format(self.addr, opname[self.op])
  33. if self.arg is not None:
  34. s += ' ' + str(self.arg)
  35. return s
  36. def disassemble_to_list(code):
  37. lst = []
  38. n = len(code)
  39. i = 0
  40. while i < n:
  41. c = code[i]
  42. op = ord(c)
  43. addr = i
  44. i += 1
  45. oparg = None
  46. if op >= HAVE_ARGUMENT:
  47. oparg = ord(code[i]) + ord(code[i + 1])*256
  48. i += 2
  49. lst.append(Instruction(addr, op, oparg))
  50. return lst
  51. def assemble_from_list(lst, offset = 0):
  52. out = StringIO()
  53. for it in lst:
  54. out.write(chr(it.op))
  55. if it.op >= HAVE_ARGUMENT:
  56. arg = it.arg
  57. if it.op in hasjabs:
  58. arg += offset
  59. out.write(chr(arg & 0xFF))
  60. out.write(chr(arg >> 8))
  61. return out.getvalue()
  62. def patch(f):
  63. # new.function(code, globals[, name[, argdefs[, closure]]])
  64. # new.code(argcount, nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab)
  65. code = f.__code__
  66. lst = disassemble_to_list(code.co_code)
  67. local_cnt = code.co_nlocals
  68. for it in lst:
  69. if it.op == opmap['RETURN_VALUE']:
  70. return_addr = it.addr + 1
  71. break
  72. patch = [
  73. Instruction(0, opmap['LOAD_FAST'], 0),
  74. Instruction(0, opmap['UNPACK_SEQUENCE'], local_cnt),]
  75. for i in range(local_cnt):
  76. patch.append(Instruction(0, opmap['STORE_FAST'], i))
  77. assembled = assemble_from_list(patch)
  78. patch_length = len(assembled) + 3
  79. patch = [Instruction(0, opmap['JUMP_ABSOLUTE'], return_addr + patch_length)]
  80. assembled += assemble_from_list(patch)
  81. assert len(assembled) == patch_length
  82. assembled += assemble_from_list(lst, patch_length)
  83. patched_code = new.code(1, code.co_nlocals, code.co_stacksize,
  84. code.co_flags, assembled, code.co_consts, code.co_names,
  85. code.co_varnames, code.co_filename, code.co_name,
  86. code.co_firstlineno, code.co_lnotab)
  87. patched_function = new.function(patched_code, f.func_globals, 'generator', None, f.func_closure)
  88. return patched_function
  89. def facs(step = 1):
  90. acc = 1
  91. n = 0
  92. while True:
  93. # for _ in range(10):
  94. n += step
  95. acc *= n
  96. return save_frame(acc)
  97. acc += 0 # to prevent the compiler from removing the jump back
  98. facs2 = patch(facs)
  99. print disassemble(facs.__code__)
  100. print disassemble(facs2.__code__)
  101. # print disassemble(facs_patched.__code__)
  102. print 'start'
  103. frame = facs()
  104. print frame.__dict__
  105. frame = facs2(frame.locals)
  106. print frame.__dict__
  107. frame = facs2(frame.locals)
  108. print frame.__dict__