PageRenderTime 26ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/pydbgr/lib/bytecode.py

http://pydbgr.googlecode.com/
Python | 125 lines | 88 code | 11 blank | 26 comment | 19 complexity | 62d035593bdb5531673d7d29242e1f01 MD5 | raw file
Possible License(s): GPL-3.0
  1. # -*- coding: utf-8 -*-
  2. # Copyright (C) 2009 Rocky Bernstein <rocky@gnu.org>
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. '''Bytecode instruction routines'''
  17. import dis, re
  18. from opcode import opname, HAVE_ARGUMENT
  19. def op_at_code_loc(code, loc):
  20. try:
  21. op = ord(code[loc])
  22. except IndexError:
  23. return 'got IndexError'
  24. return opname[op]
  25. def op_at_frame(frame, loc=None):
  26. code = frame.f_code.co_code
  27. if loc is None: loc = frame.f_lasti
  28. return op_at_code_loc(code, loc)
  29. def next_opcode(code, offset):
  30. '''Return the next opcode and offset as a tuple. Tuple (-100,
  31. -1000) is returned when reaching the end.'''
  32. n = len(code)
  33. while offset < n:
  34. c = code[offset]
  35. op = ord(c)
  36. offset += 1
  37. if op >= HAVE_ARGUMENT:
  38. offset += 2
  39. pass
  40. yield op, offset
  41. pass
  42. yield -100, -1000
  43. pass
  44. def next_linestart(co, offset, count=1):
  45. linestarts = dict(dis.findlinestarts(co))
  46. code = co.co_code
  47. # n = len(code)
  48. # contains_cond_jump = False
  49. for op, offset in next_opcode(code, offset):
  50. if offset in linestarts:
  51. count -= 1
  52. if 0 == count:
  53. return linestarts[offset]
  54. pass
  55. pass
  56. return -1000
  57. def stmt_contains_opcode(co, lineno, query_opcode):
  58. linestarts = dict(dis.findlinestarts(co))
  59. code = co.co_code
  60. found_start = False
  61. for offset, start_line in linestarts.items():
  62. if start_line == lineno:
  63. found_start = True
  64. break
  65. pass
  66. if not found_start:
  67. return False
  68. for op, offset in next_opcode(code, offset):
  69. if -1000 == offset or linestarts.get(offset): return False
  70. opcode = opname[op]
  71. ##debug: print opcode
  72. if query_opcode == opcode:
  73. return True
  74. pass
  75. return False
  76. _re_def_str = r'^\s*def\s'
  77. _re_def = re.compile(_re_def_str)
  78. def is_def_stmt(line, frame):
  79. """Return True if we are looking at a def statement"""
  80. # Should really also check that operand of 'LOAD_CONST' is a code object
  81. return (_re_def.match(line) and op_at_frame(frame)=='LOAD_CONST'
  82. and stmt_contains_opcode(frame.f_code, frame.f_lineno,
  83. 'MAKE_FUNCTION'))
  84. _re_class = re.compile(r'^\s*class\s')
  85. def is_class_def(line, frame):
  86. """Return True if we are looking at a class definition statement"""
  87. return (_re_class.match(line)
  88. and stmt_contains_opcode(frame.f_code, frame.f_lineno,
  89. 'BUILD_CLASS'))
  90. # Demo stuff above
  91. if __name__=='__main__':
  92. import inspect
  93. def sqr(x):
  94. return x * x
  95. frame = inspect.currentframe()
  96. co = frame.f_code
  97. lineno = frame.f_lineno
  98. print 'contains MAKE_FUNCTION', stmt_contains_opcode(co, lineno-4,
  99. 'MAKE_FUNCTION')
  100. print 'contains MAKE_FUNCTION', stmt_contains_opcode(co, lineno,
  101. 'MAKE_FUNCTION')
  102. print "op at frame: ", op_at_frame(frame)
  103. print "op at frame, position 2", op_at_frame(frame, 2)
  104. print "def statement: x=5?: ", is_def_stmt('x=5', frame)
  105. # Not a "def" statement because frame is wrong spot
  106. print is_def_stmt('def foo():', frame)
  107. class Foo:
  108. pass
  109. lineno = frame.f_lineno
  110. print 'contains BUILD_CLASS', stmt_contains_opcode(co, lineno-2,
  111. 'BUILD_CLASS')
  112. print 'contains BUILD_CLASS', stmt_contains_opcode(co, lineno,
  113. 'BUILD_CLASS')
  114. pass