You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
As part of tokenization for (de)parsing, we need to do something like a disassembly, but is is really a little different. Disassembly, strictly speaking, is done by the xdis module now. What "ingestion" does is massage the instruction tokens to a form that is more amenable for parsing. In sum, ingestion is different than disassembly, although disassembly is generally the first part of ingestion.
103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
# Copyright (c) 2015-2016 by Rocky Bernstein
|
|
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
|
# Copyright (c) 1999 John Aycock
|
|
|
|
"""
|
|
CPython magic- and version- independent disassembly routines
|
|
|
|
There are two reasons we can't use Python's built-in routines
|
|
from dis. First, the bytecode we are extracting may be from a different
|
|
version of Python (different magic number) than the version of Python
|
|
that is doing the extraction.
|
|
|
|
Second, we need structured instruction information for the
|
|
(de)-parsing step. Python 3.4 and up provides this, but we still do
|
|
want to run on Python 2.7.
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import sys
|
|
from collections import deque
|
|
|
|
import uncompyle6
|
|
|
|
from xdis.main import disassemble_file as xdisassemble_file
|
|
from xdis.code import iscode
|
|
from xdis.load import check_object_path, load_module
|
|
from uncompyle6.scanner import get_scanner
|
|
|
|
def disco(version, co, out=None, is_pypy=False):
|
|
"""
|
|
diassembles and deparses a given code block 'co'
|
|
"""
|
|
|
|
assert iscode(co)
|
|
|
|
# store final output stream for case of error
|
|
real_out = out or sys.stdout
|
|
print('# Python %s' % version, file=real_out)
|
|
if co.co_filename:
|
|
print('# Embedded file name: %s' % co.co_filename,
|
|
file=real_out)
|
|
|
|
scanner = get_scanner(version, is_pypy=is_pypy)
|
|
|
|
queue = deque([co])
|
|
disco_loop(scanner.ingest, queue, real_out)
|
|
|
|
|
|
def disco_loop(disasm, queue, real_out):
|
|
while len(queue) > 0:
|
|
co = queue.popleft()
|
|
if co.co_name != '<module>':
|
|
print('\n# %s line %d of %s' %
|
|
(co.co_name, co.co_firstlineno, co.co_filename),
|
|
file=real_out)
|
|
tokens, customize = disasm(co)
|
|
for t in tokens:
|
|
if iscode(t.pattr):
|
|
queue.append(t.pattr)
|
|
elif iscode(t.attr):
|
|
queue.append(t.attr)
|
|
print(t, file=real_out)
|
|
pass
|
|
pass
|
|
|
|
def disassemble_file(filename, outstream=None, native=False):
|
|
"""
|
|
disassemble Python byte-code file (.pyc)
|
|
|
|
If given a Python source file (".py") file, we'll
|
|
try to find the corresponding compiled object.
|
|
"""
|
|
if native:
|
|
xdisassemble_file(filename, outstream)
|
|
return
|
|
|
|
filename = check_object_path(filename)
|
|
version, timestamp, magic_int, co, is_pypy = load_module(filename)
|
|
if type(co) == list:
|
|
for con in co:
|
|
disco(version, con, outstream)
|
|
else:
|
|
disco(version, co, outstream, is_pypy=is_pypy)
|
|
co = None
|
|
|
|
def _test():
|
|
"""Simple test program to disassemble a file."""
|
|
argc = len(sys.argv)
|
|
if argc != 2:
|
|
if argc == 1 and uncompyle6.PYTHON3:
|
|
fn = __file__
|
|
else:
|
|
sys.stderr.write("usage: %s [-|CPython compiled file]\n" % __file__)
|
|
sys.exit(2)
|
|
else:
|
|
fn = sys.argv[1]
|
|
disassemble_file(fn, native=True)
|
|
|
|
if __name__ == "__main__":
|
|
_test()
|