You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +08:00
Closer to being able to handle Python 3.4 bytecode. Loading of Python
Python bytecode now works. magics from 3.3 to Python 3.4 has been added. Some Python 3.4 scanner issues have been fixed.
This commit is contained in:
@@ -72,14 +72,26 @@ def _load_module(filename):
|
||||
try:
|
||||
version = float(magics.versions[magic])
|
||||
except KeyError:
|
||||
raise ImportError("Unknown magic number %s in %s" % (ord(magic[0])+256*ord(magic[1]), filename))
|
||||
if (version > 2.7) or (version < 2.5):
|
||||
raise ImportError("This is a Python %s file! Only Python 2.5 to 2.7 files are supported." % version)
|
||||
raise ImportError("Unknown magic number %s in %s" %
|
||||
(ord(magic[0])+256*ord(magic[1]), filename))
|
||||
if not (2.5 <= version <= 2.7) and not (version == 3.4):
|
||||
raise ImportError("This is a Python %s file! Only "
|
||||
"Python 2.5 to 2.7 and 3.4 files are supported."
|
||||
% version)
|
||||
|
||||
# print version
|
||||
fp.read(4) # timestamp
|
||||
|
||||
if version == PYTHON_VERSION:
|
||||
magic_int = magics.magic2int(magic)
|
||||
# Note: a higher magic number necessarily mean a later
|
||||
# release. At Pyton 3.0 the magic number decreased
|
||||
# significantly. Hence the range below. Also note
|
||||
# inclusion of the size info, occurred within a
|
||||
# Python magor/minor release. Hence the test on the
|
||||
# magic value rather than PYTHON_VERSION
|
||||
if 3200 <= magic_int < 20121:
|
||||
fp.read(4) # size mod 2**32
|
||||
bytecode = fp.read()
|
||||
co = marshal.loads(bytecode)
|
||||
else:
|
||||
@@ -112,6 +124,9 @@ def uncompyle(version, co, out=None, showasm=0, showast=0):
|
||||
elif version == 2.5:
|
||||
import uncompyle6.scanners.scanner25 as scan
|
||||
scanner = scan.Scanner25()
|
||||
elif version == 3.4:
|
||||
import uncompyle6.scanners.scanner34 as scan
|
||||
scanner = scan.Scanner34()
|
||||
scanner.setShowAsm(showasm, out)
|
||||
tokens, customize = scanner.disassemble(co)
|
||||
|
||||
|
@@ -9,18 +9,22 @@ When the two are the same, you can simply use marshal.loads()
|
||||
to prodoce a code object
|
||||
"""
|
||||
|
||||
import marshal, pickle, sys, types
|
||||
|
||||
import imp, marshal, pickle, sys, types
|
||||
import dis as Mdis
|
||||
|
||||
from struct import unpack
|
||||
|
||||
import uncompyle6
|
||||
from uncompyle6.magics import magic2int
|
||||
|
||||
internStrings = []
|
||||
|
||||
# XXX For backwards compatibility
|
||||
disco = Mdis.disassemble
|
||||
|
||||
PYTHON3 = (sys.version_info >= (3, 0))
|
||||
PYTHON_MAGIC_INT = magic2int(imp.get_magic())
|
||||
|
||||
if PYTHON3:
|
||||
def long(n): return n
|
||||
|
||||
@@ -34,7 +38,12 @@ def marshalLoad(fp):
|
||||
|
||||
def load(fp):
|
||||
"""
|
||||
Load marshal
|
||||
marshal.load() written in Python. When the Python bytecode magic loaded is the
|
||||
same magic for the running Python interpreter, we can simply use the
|
||||
Python-supplied mashal.load().
|
||||
|
||||
However we need to use this when versions are different since the internal
|
||||
code structures are different. Sigh.
|
||||
"""
|
||||
global internStrings
|
||||
|
||||
@@ -56,17 +65,25 @@ def load(fp):
|
||||
co_name = load(fp)
|
||||
co_firstlineno = unpack('i', fp.read(4))[0]
|
||||
co_lnotab = load(fp)
|
||||
if PYTHON3:
|
||||
# The Python3 code object is different than Python2's which
|
||||
# we are reading if we get here. In particular, it has a
|
||||
# kwonlyargcount parameter which we set to 0.
|
||||
# Also various parameters which were strings are now
|
||||
# The Python3 code object is different than Python2's which
|
||||
# we are reading if we get here.
|
||||
# Also various parameters which were strings are now
|
||||
# bytes (which is probably more logical).
|
||||
return Code(co_argcount, 0, co_nlocals, co_stacksize, co_flags,
|
||||
bytes(co_code, encoding='utf-8'),
|
||||
co_consts, co_names, co_varnames, co_filename, co_name,
|
||||
co_firstlineno, bytes(co_lnotab, encoding='utf-8'),
|
||||
co_freevars, co_cellvars)
|
||||
if PYTHON3:
|
||||
if PYTHON_MAGIC_INT > 3020:
|
||||
# In later Python3 versions, there is a
|
||||
# kwonlyargcount parameter which we set to 0.
|
||||
return Code(co_argcount, 0, co_nlocals, co_stacksize, co_flags,
|
||||
bytes(co_code, encoding='utf-8'),
|
||||
co_consts, co_names, co_varnames, co_filename, co_name,
|
||||
co_firstlineno, bytes(co_lnotab, encoding='utf-8'),
|
||||
co_freevars, co_cellvars)
|
||||
else:
|
||||
return Code(co_argcount, 0, co_nlocals, co_stacksize, co_flags,
|
||||
bytes(co_code, encoding='utf-8'),
|
||||
co_consts, co_names, co_varnames, co_filename, co_name,
|
||||
co_firstlineno, bytes(co_lnotab, encoding='utf-8'),
|
||||
co_freevars, co_cellvars)
|
||||
else:
|
||||
return Code(co_argcount, co_nlocals, co_stacksize, co_flags, co_code,
|
||||
co_consts, co_names, co_varnames, co_filename, co_name,
|
||||
|
@@ -2,7 +2,7 @@ from __future__ import print_function
|
||||
|
||||
import struct, sys
|
||||
|
||||
__all__ = ['magics', 'versions']
|
||||
__all__ = ['magics', 'versions', 'magics2int']
|
||||
|
||||
def __build_magic(magic):
|
||||
if (sys.version_info >= (3, 0)):
|
||||
@@ -10,6 +10,9 @@ def __build_magic(magic):
|
||||
else:
|
||||
return struct.pack('Hcc', magic, '\r', '\n')
|
||||
|
||||
def magic2int (magic):
|
||||
return struct.unpack('Hcc', magic)[0]
|
||||
|
||||
by_magic = {}
|
||||
by_version = {}
|
||||
|
||||
@@ -21,6 +24,7 @@ def __by_version(magics):
|
||||
|
||||
versions = {
|
||||
# taken from from Python/import.c
|
||||
# or importlib/_bootstrap.py
|
||||
# magic, version
|
||||
__build_magic(20121): '1.5', # 1.5, 1.5.1, 1.5.2
|
||||
__build_magic(50428): '1.6', # 1.6
|
||||
@@ -69,6 +73,21 @@ versions = {
|
||||
__build_magic(3160): '3.2', # 3.2a0 (add SETUP_WITH)
|
||||
__build_magic(3170): '3.2', # 3.2a1 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR)
|
||||
__build_magic(3180): '3.2', # 3.2a2 (add DELETE_DEREF)
|
||||
__build_magic(3190): '3.3', # Python 3.3a0 3190 __class__ super closure changed
|
||||
__build_magic(3200): '3.3', # Python 3.3a0 3200 (__qualname__ added)
|
||||
__build_magic(3200): '3.3', # 3210 (added size modulo 2**32 to the pyc header)
|
||||
__build_magic(3220): '3.3', # Python 3.3a1 3220 (changed PEP 380 implementation)
|
||||
__build_magic(3230): '3.3', # Python 3.3a4 3230 (revert changes to implicit __class__ closure)
|
||||
__build_magic(3250): '3.4', # Python 3.4a1 3250 (evaluate positional default arg
|
||||
# keyword-only defaults)
|
||||
__build_magic(3260): '3.4', # Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override
|
||||
# free vars)
|
||||
__build_magic(3270): '3.4', # Python 3.4a1 3270 (various tweaks to the __class__ closure)
|
||||
__build_magic(3280): '3.4', # Python 3.4a1 3280 (remove implicit class argument)
|
||||
__build_magic(3290): '3.4', # Python 3.4a4 3290 (changes to __qualname__ computation)
|
||||
__build_magic(3300): '3.4', # Python 3.4a4 3300 (more changes to __qualname__ computation)
|
||||
__build_magic(3310): '3.4', # Python 3.4rc2 3310 (alter __qualname__ computation)
|
||||
|
||||
}
|
||||
|
||||
magics = __by_version(versions)
|
||||
@@ -81,7 +100,6 @@ def test():
|
||||
magic_20 = magics['2.0']
|
||||
current = imp.get_magic()
|
||||
current_version = struct.unpack('HBB', current)[0]
|
||||
from trepan.api import debug; debug()
|
||||
magic_current = by_magic[ current ]
|
||||
print(type(magic_20), len(magic_20), repr(magic_20))
|
||||
print()
|
||||
|
@@ -17,12 +17,12 @@ from uncompyle6.scanner import Token
|
||||
# Get all the opcodes into globals
|
||||
globals().update(dis.opmap)
|
||||
from uncompyle6.opcodes.opcode_27 import *
|
||||
import scanner as scan
|
||||
import uncompyle6.scanner as scan
|
||||
|
||||
|
||||
class Scanner34(scan.Scanner):
|
||||
def __init__(self):
|
||||
self.Token = scan.Scanner.__init__(self, 2.7) # check
|
||||
self.Token = scan.Scanner.__init__(self, 3.4) # check
|
||||
|
||||
def run(self, bytecode):
|
||||
code_object = marshal.loads(bytecode)
|
||||
@@ -60,8 +60,7 @@ class Scanner34(scan.Scanner):
|
||||
op = code[offset]
|
||||
# Create token and fill all the fields we can
|
||||
# w/o touching arguments
|
||||
current_token = Token()
|
||||
current_token.type = dis.opname[op]
|
||||
current_token = Token(dis.opname[op])
|
||||
current_token.offset = offset
|
||||
current_token.linestart = True if offset in self.linestarts else False
|
||||
if op >= dis.HAVE_ARGUMENT:
|
||||
@@ -135,6 +134,16 @@ class Scanner34(scan.Scanner):
|
||||
for _ in range(self.op_size(op)):
|
||||
self.prev_op.append(offset)
|
||||
|
||||
def op_size(self, op):
|
||||
"""
|
||||
Return size of operator with its arguments
|
||||
for given opcode <op>.
|
||||
"""
|
||||
if op < dis.HAVE_ARGUMENT:
|
||||
return 1
|
||||
else:
|
||||
return 3
|
||||
|
||||
def find_jump_targets(self):
|
||||
"""
|
||||
Detect all offsets in a byte code which are jump targets.
|
||||
|
Reference in New Issue
Block a user