code_fns.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. # Copyright (c) 2015-2016, 2818-2022, 2024 by Rocky Bernstein
  2. # Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
  3. # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
  4. # Copyright (c) 1999 John Aycock
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. CPython magic- and version-independent disassembly routines
  20. There are two reasons we can't use Python's built-in routines
  21. from ``dis``.
  22. First, the bytecode we are extracting may be from a different
  23. version of Python (different magic number) than the version of Python
  24. that is doing the extraction.
  25. Second, we need structured instruction information for the
  26. (de)-parsing step. Python 3.4 and up provides this, but we still do
  27. want to run on earlier Python versions.
  28. """
  29. import sys
  30. from collections import deque
  31. from xdis import check_object_path, iscode, load_module
  32. from xdis.version_info import version_tuple_to_str
  33. from uncompyle6.scanner import get_scanner
  34. def disco(version, co, out=None, is_pypy=False):
  35. """
  36. disassembles and deparses a given code block ``co``.
  37. """
  38. assert iscode(co)
  39. # Store final output stream in case there is an error.
  40. real_out = out or sys.stdout
  41. print("# Python %s" % version_tuple_to_str(version), file=real_out)
  42. if co.co_filename:
  43. print("# Embedded file name: %s" % co.co_filename, file=real_out)
  44. scanner = get_scanner(version, is_pypy=is_pypy)
  45. queue = deque([co])
  46. disco_loop(scanner.ingest, queue, real_out)
  47. def disco_loop(disasm, queue, real_out):
  48. while len(queue) > 0:
  49. co = queue.popleft()
  50. if co.co_name != "<module>":
  51. if hasattr(co, "co_firstlineno"):
  52. print(
  53. "\n# %s line %d of %s"
  54. % (co.co_name, co.co_firstlineno, co.co_filename),
  55. file=real_out,
  56. )
  57. else:
  58. print(
  59. "\n# %s of %s"
  60. % (co.co_name, co.co_filename),
  61. file=real_out,
  62. )
  63. tokens, customize = disasm(co)
  64. for t in tokens:
  65. if iscode(t.pattr):
  66. queue.append(t.pattr)
  67. elif iscode(t.attr):
  68. queue.append(t.attr)
  69. print(t, file=real_out)
  70. pass
  71. pass
  72. # def disassemble_fp(fp, outstream=None):
  73. # """
  74. # disassemble Python byte-code from an open file
  75. # """
  76. # (version, timestamp, magic_int, co, is_pypy,
  77. # source_size) = load_from_fp(fp)
  78. # if type(co) == list:
  79. # for con in co:
  80. # disco(version, con, outstream)
  81. # else:
  82. # disco(version, co, outstream, is_pypy=is_pypy)
  83. # co = None
  84. def disassemble_file(filename, outstream=None):
  85. """
  86. Disassemble Python byte-code file (.pyc).
  87. If given a Python source file (".py") file, we'll
  88. try to find the corresponding compiled object.
  89. """
  90. filename = check_object_path(filename)
  91. (version, timestamp, magic_int, co, is_pypy, source_size, sip_hash) = load_module(
  92. filename
  93. )
  94. if type(co) == list:
  95. for con in co:
  96. disco(version, con, outstream)
  97. else:
  98. disco(version, co, outstream, is_pypy=is_pypy)
  99. def _test():
  100. """Simple test program to disassemble a file."""
  101. argc = len(sys.argv)
  102. if argc != 2:
  103. if argc == 1:
  104. fn = __file__
  105. else:
  106. sys.stderr.write("usage: %s [-|CPython compiled file]\n" % __file__)
  107. sys.exit(2)
  108. else:
  109. fn = sys.argv[1]
  110. disassemble_file(fn)
  111. if __name__ == "__main__":
  112. _test()