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:
rocky
2015-12-14 14:55:32 -05:00
parent b5797dfa0f
commit 3e31f41552
4 changed files with 81 additions and 22 deletions

View File

@@ -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)

View File

@@ -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,

View File

@@ -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()

View File

@@ -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.