You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Fix up looping by reinstating JUMP_ABSOLUTE -> JUMP_BACK or CONTINUE
get jump offsets into jump attributes. Fix up 3.2 scanner paritally and use that in 3.4 for in cross version disassembly.
This commit is contained in:
Binary file not shown.
@@ -5,6 +5,14 @@ def handle(module):
|
||||
module = exc
|
||||
return module
|
||||
|
||||
def handle2(module):
|
||||
if module == 'foo':
|
||||
try:
|
||||
module = 1
|
||||
except ImportError as exc:
|
||||
module = exc
|
||||
return module
|
||||
|
||||
try:
|
||||
pass
|
||||
except ImportError as exc:
|
||||
|
2
test/simple_source/looping/10_while.py
Normal file
2
test/simple_source/looping/10_while.py
Normal file
@@ -0,0 +1,2 @@
|
||||
while __name__ != '__main__':
|
||||
b = 4
|
@@ -1,2 +0,0 @@
|
||||
while a:
|
||||
b = c
|
5
test/simple_source/stmts/15_for_if.py
Normal file
5
test/simple_source/stmts/15_for_if.py
Normal file
@@ -0,0 +1,5 @@
|
||||
if __name__:
|
||||
for i in (1,2):
|
||||
x = 3
|
||||
pass
|
||||
pass
|
@@ -71,7 +71,7 @@ class Python3Parser(PythonParser):
|
||||
_come_from ::= COME_FROM
|
||||
_come_from ::=
|
||||
|
||||
list_for ::= expr FOR_ITER designator list_iter JUMP_ABSOLUTE
|
||||
list_for ::= expr FOR_ITER designator list_iter JUMP_BACK
|
||||
list_if ::= expr jmp_false list_iter
|
||||
list_if_not ::= expr jmp_true list_iter
|
||||
|
||||
@@ -407,7 +407,7 @@ class Python3Parser(PythonParser):
|
||||
testtrue ::= expr jmp_true
|
||||
|
||||
_ifstmts_jump ::= return_if_stmts
|
||||
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM
|
||||
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM _come_from
|
||||
|
||||
iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE
|
||||
|
||||
@@ -459,7 +459,7 @@ class Python3Parser(PythonParser):
|
||||
# This is used in Python 3 in
|
||||
# "except ... as e" to remove 'e' after the c_stmts_opt finishes
|
||||
except_suite_finalize ::= SETUP_FINALLY c_stmts_opt except_var_finalize
|
||||
END_FINALLY JUMP_FORWARD
|
||||
END_FINALLY _jump
|
||||
|
||||
except_var_finalize ::= POP_BLOCK POP_EXCEPT LOAD_CONST COME_FROM LOAD_CONST
|
||||
designator del_stmt
|
||||
@@ -490,7 +490,7 @@ class Python3Parser(PythonParser):
|
||||
|
||||
whilestmt ::= SETUP_LOOP
|
||||
testexpr
|
||||
l_stmts_opt JUMP_ABSOLUTE
|
||||
l_stmts_opt JUMP_BACK
|
||||
POP_BLOCK COME_FROM
|
||||
|
||||
whilestmt ::= SETUP_LOOP
|
||||
@@ -515,11 +515,11 @@ class Python3Parser(PythonParser):
|
||||
_for ::= GET_ITER FOR_ITER
|
||||
_for ::= LOAD_CONST FOR_LOOP
|
||||
|
||||
for_block ::= l_stmts_opt JUMP_ABSOLUTE
|
||||
for_block ::= l_stmts_opt JUMP_BACK
|
||||
for_block ::= return_stmts _come_from
|
||||
|
||||
forstmt ::= SETUP_LOOP expr _for designator
|
||||
for_block POP_BLOCK COME_FROM
|
||||
for_block POP_BLOCK _come_from
|
||||
|
||||
forelsestmt ::= SETUP_LOOP expr _for designator
|
||||
for_block POP_BLOCK else_suite COME_FROM
|
||||
|
@@ -34,11 +34,11 @@ class Scanner32(scan.Scanner):
|
||||
tokens = self.tokenize(code_object)
|
||||
return tokens
|
||||
|
||||
def disassemble(self, co):
|
||||
def disassemble(self, co, classname=None):
|
||||
"""
|
||||
Convert code object <co> into a sequence of tokens.
|
||||
|
||||
Based on dis.disassemble() function.
|
||||
The below is based on (an older version?) of Python dis.disassemble_bytes().
|
||||
"""
|
||||
# Container for tokens
|
||||
tokens = []
|
||||
@@ -47,9 +47,64 @@ class Scanner32(scan.Scanner):
|
||||
codelen = len(code)
|
||||
self.build_lines_data(co)
|
||||
self.build_prev_op()
|
||||
|
||||
# Get jump targets
|
||||
# Format: {target offset: [jump offsets]}
|
||||
jump_targets = self.find_jump_targets()
|
||||
|
||||
# self.lines contains (block,addrLastInstr)
|
||||
if classname:
|
||||
classname = '_' + classname.lstrip('_') + '__'
|
||||
|
||||
def unmangle(name):
|
||||
if name.startswith(classname) and name[-2:] != '__':
|
||||
return name[len(classname) - 2:]
|
||||
return name
|
||||
|
||||
# free = [ unmangle(name) for name in (co.co_cellvars + co.co_freevars) ]
|
||||
# names = [ unmangle(name) for name in co.co_names ]
|
||||
# varnames = [ unmangle(name) for name in co.co_varnames ]
|
||||
else:
|
||||
# free = co.co_cellvars + co.co_freevars
|
||||
# names = co.co_names
|
||||
# varnames = co.co_varnames
|
||||
pass
|
||||
|
||||
# Scan for assertions. Later we will
|
||||
# turn 'LOAD_GLOBAL' to 'LOAD_ASSERT' for those
|
||||
# assertions
|
||||
|
||||
self.load_asserts = set()
|
||||
for i in self.op_range(0, codelen):
|
||||
if self.code[i] == POP_JUMP_IF_TRUE and self.code[i+3] == LOAD_GLOBAL:
|
||||
if names[self.get_argument(i+3)] == 'AssertionError':
|
||||
self.load_asserts.add(i+3)
|
||||
|
||||
# FIXME: reinstate code
|
||||
# cf = self.find_jump_targets(self.code)
|
||||
# # contains (code, [addrRefToCode])
|
||||
# last_stmt = self.next_stmt[0]
|
||||
# i = self.next_stmt[last_stmt]
|
||||
# replace = {}
|
||||
# while i < n-1:
|
||||
# if self.lines[last_stmt].next > i:
|
||||
# if self.code[last_stmt] == PRINT_ITEM:
|
||||
# if self.code[i] == PRINT_ITEM:
|
||||
# replace[i] = 'PRINT_ITEM_CONT'
|
||||
# elif self.code[i] == PRINT_NEWLINE:
|
||||
# replace[i] = 'PRINT_NEWLINE_CONT'
|
||||
# last_stmt = i
|
||||
# i = self.next_stmt[i]
|
||||
|
||||
# imports = self.all_instr(0, n, (IMPORT_NAME, IMPORT_FROM, IMPORT_STAR))
|
||||
# if len(imports) > 1:
|
||||
# last_import = imports[0]
|
||||
# for i in imports[1:]:
|
||||
# if self.lines[last_import].next > i:
|
||||
# if self.code[last_import] == IMPORT_NAME == self.code[i]:
|
||||
# replace[i] = 'IMPORT_NAME_CONT'
|
||||
# last_import = i
|
||||
|
||||
# Initialize extended arg at 0. When extended arg op is encountered,
|
||||
# variable preserved for next cycle and added as arg for next op
|
||||
extended_arg = 0
|
||||
@@ -63,6 +118,8 @@ class Scanner32(scan.Scanner):
|
||||
offset='{}_{}'.format(offset, jump_idx)))
|
||||
jump_idx += 1
|
||||
op = code[offset]
|
||||
op_name = opname[op]
|
||||
|
||||
# Create token and fill all the fields we can
|
||||
# w/o touching arguments
|
||||
current_token = Token(dis.opname[op])
|
||||
@@ -97,6 +154,17 @@ class Scanner32(scan.Scanner):
|
||||
if free is None:
|
||||
free = co.co_cellvars + co.co_freevars
|
||||
current_token.pattr = free[oparg]
|
||||
if op == JUMP_ABSOLUTE:
|
||||
current_token.pattr = current_token.attr = oparg
|
||||
target = self.get_target(offset)
|
||||
if target < offset:
|
||||
if offset in self.stmts and self.code[offset+3] not in (END_FINALLY, POP_BLOCK) \
|
||||
and offset not in self.not_continue:
|
||||
op_name = 'CONTINUE'
|
||||
else:
|
||||
op_name = 'JUMP_BACK'
|
||||
current_token.type = op_name
|
||||
|
||||
tokens.append(current_token)
|
||||
return tokens, customize
|
||||
|
||||
|
@@ -20,7 +20,7 @@ from collections import namedtuple
|
||||
from array import array
|
||||
|
||||
from uncompyle6 import PYTHON_VERSION
|
||||
from uncompyle6.scanner import Token, L65536
|
||||
from uncompyle6.scanner import Token
|
||||
|
||||
import uncompyle6.opcodes.opcode_34
|
||||
# Get all the opcodes into globals
|
||||
@@ -145,6 +145,17 @@ class Scanner34(scan.Scanner):
|
||||
if inst.opname != 'BUILD_SLICE':
|
||||
customize[opname] = inst.argval
|
||||
|
||||
elif opname == 'JUMP_ABSOLUTE':
|
||||
pattr = inst.argval
|
||||
target = self.get_target(inst.offset)
|
||||
if target < inst.offset:
|
||||
if (inst.offset in self.stmts and
|
||||
self.code[inst.offset+3] not in (END_FINALLY, POP_BLOCK)
|
||||
and offset not in self.not_continue):
|
||||
opname = 'CONTINUE'
|
||||
else:
|
||||
opname = 'JUMP_BACK'
|
||||
|
||||
elif inst.offset in self.load_asserts:
|
||||
opname = 'LOAD_ASSERT'
|
||||
|
||||
@@ -160,71 +171,8 @@ class Scanner34(scan.Scanner):
|
||||
pass
|
||||
return tokens, {}
|
||||
|
||||
def disassemble_cross_version(self, co):
|
||||
"""
|
||||
Convert code object <co> into a sequence of tokens
|
||||
FIXME: the below is not based on the current Python 3.4 dis.disassemble_bytes().
|
||||
Fix that.
|
||||
"""
|
||||
# Container for tokens
|
||||
tokens = []
|
||||
customize = {}
|
||||
self.code = code = array('B', co.co_code)
|
||||
codelen = len(code)
|
||||
self.build_lines_data(co)
|
||||
self.build_prev_op()
|
||||
# Get jump targets
|
||||
# Format: {target offset: [jump offsets]}
|
||||
jump_targets = self.find_jump_targets()
|
||||
# Initialize extended arg at 0. When extended arg op is encountered,
|
||||
# variable preserved for next cycle and added as arg for next op
|
||||
extended_arg = 0
|
||||
free = None
|
||||
for offset in self.op_range(0, codelen):
|
||||
# Add jump target tokens
|
||||
if offset in jump_targets:
|
||||
jump_idx = 0
|
||||
for jump_offset in jump_targets[offset]:
|
||||
tokens.append(Token('COME_FROM', None, repr(jump_offset),
|
||||
offset='{}_{}'.format(offset, jump_idx)))
|
||||
jump_idx += 1
|
||||
op = code[offset]
|
||||
# Create token and fill all the fields we can
|
||||
# w/o touching arguments
|
||||
current_token = Token(dis.opname[op])
|
||||
current_token.offset = offset
|
||||
|
||||
if offset in self.linestarts:
|
||||
current_token.linestart = self.linestarts[offset]
|
||||
else:
|
||||
current_token.linestart = None
|
||||
|
||||
if op >= dis.HAVE_ARGUMENT:
|
||||
# Calculate op's argument value based on its argument and
|
||||
# preceding extended argument, if any
|
||||
oparg = code[offset+1] + code[offset+2]*256 + extended_arg
|
||||
extended_arg = 0
|
||||
if op == dis.EXTENDED_ARG:
|
||||
extended_arg = oparg * L65536
|
||||
|
||||
# Fill token's attr/pattr fields
|
||||
current_token.attr = oparg
|
||||
if op in dis.hasconst:
|
||||
current_token.pattr = repr(co.co_consts[oparg])
|
||||
elif op in dis.hasname:
|
||||
current_token.pattr = co.co_names[oparg]
|
||||
elif op in dis.hasjrel:
|
||||
current_token.pattr = repr(offset + 3 + oparg)
|
||||
elif op in dis.haslocal:
|
||||
current_token.pattr = co.co_varnames[oparg]
|
||||
elif op in dis.hascompare:
|
||||
current_token.pattr = dis.cmp_op[oparg]
|
||||
elif op in dis.hasfree:
|
||||
if free is None:
|
||||
free = co.co_cellvars + co.co_freevars
|
||||
current_token.pattr = free[oparg]
|
||||
tokens.append(current_token)
|
||||
return tokens, customize
|
||||
def disassemble_cross_version(self, co, classname=None):
|
||||
return scan.scanner32().disassemble(self, co, classname)
|
||||
|
||||
def build_lines_data(self, code_obj):
|
||||
"""
|
||||
|
Reference in New Issue
Block a user