DRY scanner code. Allow 2.4 decompile from 3.x

This commit is contained in:
rocky
2016-07-08 21:37:09 -04:00
parent 11eddb7940
commit d6b35d57e4
5 changed files with 14 additions and 110 deletions

View File

@@ -51,7 +51,7 @@ check-bytecode-3:
#: Check deparsing bytecode that works running Python 2 and Python 3
check-bytecode: check-bytecode-3
$(PYTHON) test_pythonlib.py --bytecode-2.5 --bytecode-2.6 --bytecode-2.7
$(PYTHON) test_pythonlib.py --bytecode-2.4 --bytecode-2.5 --bytecode-2.6 --bytecode-2.7
#: Check deparsing Python 2.3
check-bytecode-2.3:

View File

@@ -24,7 +24,7 @@ from uncompyle6.scanners.tok import Token
# The byte code versions we support
if PYTHON3:
# Need to work out Python 2.3. ord's in PYTHON3
PYTHON_VERSIONS = (2.5, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5)
PYTHON_VERSIONS = (2.4, 2.5, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5)
else:
PYTHON_VERSIONS = (2.3, 2.4, 2.5, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5)

View File

@@ -8,62 +8,20 @@ information for later use in deparsing.
"""
import uncompyle6.scanners.scanner25 as scan
import uncompyle6.scanners.scanner2 as scan2
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_24
JUMP_OPs = opcode_24.JUMP_OPs
# We base this off of 2.6 instead of the other way around
# We base this off of 2.5 instead of the other way around
# because we cleaned things up this way.
# The history is that 2.7 support is the cleanest,
# then from that we got 2.6 and so on.
class Scanner24(scan.Scanner25):
def __init__(self, show_asm):
scan2.Scanner2.__init__(self, 2.4, show_asm)
self.stmt_opcodes = frozenset([
self.opc.SETUP_LOOP, self.opc.BREAK_LOOP,
self.opc.SETUP_FINALLY, self.opc.END_FINALLY,
self.opc.SETUP_EXCEPT, self.opc.POP_BLOCK,
self.opc.STORE_FAST, self.opc.DELETE_FAST,
self.opc.STORE_DEREF, self.opc.STORE_GLOBAL,
self.opc.DELETE_GLOBAL, self.opc.STORE_NAME,
self.opc.DELETE_NAME, self.opc.STORE_ATTR,
self.opc.DELETE_ATTR, self.opc.STORE_SUBSCR,
self.opc.DELETE_SUBSCR, self.opc.RETURN_VALUE,
self.opc.RAISE_VARARGS, self.opc.POP_TOP,
self.opc.PRINT_EXPR, self.opc.PRINT_ITEM,
self.opc.PRINT_NEWLINE, self.opc.PRINT_ITEM_TO,
self.opc.PRINT_NEWLINE_TO, self.opc.CONTINUE_LOOP,
self.opc.JUMP_ABSOLUTE, self.opc.EXEC_STMT,
])
# "setup" opcodes
self.setup_ops = frozenset([
self.opc.SETUP_EXCEPT, self.opc.SETUP_FINALLY,
])
# opcodes with expect a variable number pushed values whose
# count is in the opcode. For parsing we generally change the
# opcode name to include that number.
self.varargs_ops = frozenset([
self.opc.BUILD_LIST, self.opc.BUILD_TUPLE,
self.opc.BUILD_SLICE, self.opc.UNPACK_SEQUENCE,
self.opc.MAKE_FUNCTION, self.opc.CALL_FUNCTION,
self.opc.MAKE_CLOSURE, self.opc.CALL_FUNCTION_VAR,
self.opc.CALL_FUNCTION_KW, self.opc.CALL_FUNCTION_VAR_KW,
self.opc.DUP_TOPX, self.opc.RAISE_VARARGS])
# opcodes that store values into a variable
self.designator_ops = frozenset([
self.opc.STORE_FAST, self.opc.STORE_NAME,
self.opc.STORE_GLOBAL, self.opc.STORE_DEREF, self.opc.STORE_ATTR,
self.opc.STORE_SLICE_0, self.opc.STORE_SLICE_1, self.opc.STORE_SLICE_2,
self.opc.STORE_SLICE_3, self.opc.STORE_SUBSCR, self.opc.UNPACK_SEQUENCE,
self.opc.JA
])
# Python 2.7 has POP_JUMP_IF_{TRUE,FALSE}_OR_POP but < 2.7 doesn't
# Add an empty set make processing more uniform.
self.pop_jump_if_or_pop = frozenset([])
scan.Scanner25.__init__(self, show_asm)
# These are the only differences in initialization between
# 2.4, 2.5 and 2.6
self.version = 2.4
self.genexpr_name = '<generator expression>';
return

View File

@@ -8,7 +8,6 @@ information for later use in deparsing.
"""
import uncompyle6.scanners.scanner26 as scan
import uncompyle6.scanners.scanner2 as scan2
# bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_25
@@ -20,49 +19,8 @@ JUMP_OPs = opcode_25.JUMP_OPs
# then from that we got 2.6 and so on.
class Scanner25(scan.Scanner26):
def __init__(self, show_asm):
scan2.Scanner2.__init__(self, 2.5, show_asm)
self.stmt_opcodes = frozenset([
self.opc.SETUP_LOOP, self.opc.BREAK_LOOP,
self.opc.SETUP_FINALLY, self.opc.END_FINALLY,
self.opc.SETUP_EXCEPT, self.opc.POP_BLOCK,
self.opc.STORE_FAST, self.opc.DELETE_FAST,
self.opc.STORE_DEREF, self.opc.STORE_GLOBAL,
self.opc.DELETE_GLOBAL, self.opc.STORE_NAME,
self.opc.DELETE_NAME, self.opc.STORE_ATTR,
self.opc.DELETE_ATTR, self.opc.STORE_SUBSCR,
self.opc.DELETE_SUBSCR, self.opc.RETURN_VALUE,
self.opc.RAISE_VARARGS, self.opc.POP_TOP,
self.opc.PRINT_EXPR, self.opc.PRINT_ITEM,
self.opc.PRINT_NEWLINE, self.opc.PRINT_ITEM_TO,
self.opc.PRINT_NEWLINE_TO, self.opc.CONTINUE_LOOP,
self.opc.JUMP_ABSOLUTE, self.opc.EXEC_STMT,
])
# "setup" opcodes
self.setup_ops = frozenset([
self.opc.SETUP_EXCEPT, self.opc.SETUP_FINALLY,
])
# opcodes with expect a variable number pushed values whose
# count is in the opcode. For parsing we generally change the
# opcode name to include that number.
self.varargs_ops = frozenset([
self.opc.BUILD_LIST, self.opc.BUILD_TUPLE,
self.opc.BUILD_SLICE, self.opc.UNPACK_SEQUENCE,
self.opc.MAKE_FUNCTION, self.opc.CALL_FUNCTION,
self.opc.MAKE_CLOSURE, self.opc.CALL_FUNCTION_VAR,
self.opc.CALL_FUNCTION_KW, self.opc.CALL_FUNCTION_VAR_KW,
self.opc.DUP_TOPX, self.opc.RAISE_VARARGS])
# opcodes that store values into a variable
self.designator_ops = frozenset([
self.opc.STORE_FAST, self.opc.STORE_NAME,
self.opc.STORE_GLOBAL, self.opc.STORE_DEREF, self.opc.STORE_ATTR,
self.opc.STORE_SLICE_0, self.opc.STORE_SLICE_1, self.opc.STORE_SLICE_2,
self.opc.STORE_SLICE_3, self.opc.STORE_SUBSCR, self.opc.UNPACK_SEQUENCE,
self.opc.JA
])
# Python 2.7 has POP_JUMP_IF_{TRUE,FALSE}_OR_POP but < 2.7 doesn't
# Add an empty set make processing more uniform.
self.pop_jump_if_or_pop = frozenset([])
# There are no differences in initialization between
# 2.5 and 2.6
scan.Scanner26.__init__(self, show_asm)
self.version = 2.5
return

View File

@@ -1,7 +1,6 @@
# 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
"""
Python 2.6 bytecode scanner
@@ -100,7 +99,8 @@ class Scanner26(scan.Scanner2):
n = self.setup_code(co)
self.build_lines_data(co, n-1)
self.build_lines_data(co, n)
self.build_prev_op(n)
# linestarts contains block code adresses (addr,block)
self.linestarts = list(findlinestarts(co))
@@ -123,17 +123,10 @@ class Scanner26(scan.Scanner2):
varnames = co.co_varnames
self.names = names
# list of instruction to remove/add or change to match with bytecode 2.7
self.toChange = []
# Rocky: the restructure code isn't adjusting linestarts
# and this causes problems
# self.restructBytecode()
codelen = len(self.code)
self.build_prev_op(codelen)
self.load_asserts = set()
for i in self.op_range(0, codelen):
for i in self.op_range(0, n):
if (self.code[i] == self.opc.JUMP_IF_TRUE and
i + 4 < codelen and
self.code[i+3] == self.opc.POP_TOP and
@@ -233,11 +226,6 @@ class Scanner26(scan.Scanner2):
pattr = self.opc.cmp_op[oparg]
elif op in self.opc.hasfree:
pattr = free[oparg]
if offset in self.toChange:
if (self.code[offset] == self.opc.JA and
self.code[oparg] == self.opc.WITH_CLEANUP):
op_name = 'SETUP_WITH'
cf[oparg] = cf.get(oparg, []) + [offset]
if op in self.varargs_ops:
# CE - Hack for >= 2.5
# Now all values loaded via LOAD_CLOSURE are packed into