Simplify imports again using xdis 4.6.0

This commit is contained in:
rocky
2020-05-19 00:53:53 -04:00
parent 31db2f3e04
commit 5a83c7c643
12 changed files with 472 additions and 275 deletions

View File

@@ -3,7 +3,7 @@
from __future__ import print_function from __future__ import print_function
from uncompyle6.main import decompile from uncompyle6.main import decompile
from xdis.magics import sysinfo2float from xdis import sysinfo2float
import sys, inspect import sys, inspect
def uncompyle_test(): def uncompyle_test():

View File

@@ -34,8 +34,7 @@ from __future__ import print_function
import sys import sys
from collections import deque from collections import deque
from xdis import iscode, load_module from xdis import check_object_path, iscode, load_module
from xdis.load import check_object_path
from uncompyle6.scanner import get_scanner from uncompyle6.scanner import get_scanner

View File

@@ -16,8 +16,7 @@ from __future__ import print_function
import datetime, py_compile, os, subprocess, sys, tempfile import datetime, py_compile, os, subprocess, sys, tempfile
from uncompyle6 import verify, IS_PYPY, PYTHON_VERSION from uncompyle6 import verify, IS_PYPY, PYTHON_VERSION
from xdis import iscode from xdis import iscode, sysinfo2float
from xdis.magics import sysinfo2float
from uncompyle6.disas import check_object_path from uncompyle6.disas import check_object_path
from uncompyle6.semantics import pysource from uncompyle6.semantics import pysource
from uncompyle6.parser import ParserError from uncompyle6.parser import ParserError

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016, 2018-2019 by Rocky Bernstein # Copyright (c) 2016, 2018-2020 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org> # Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com> # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock # Copyright (c) 1999 John Aycock
@@ -30,9 +30,7 @@ import sys
from uncompyle6 import PYTHON3, IS_PYPY from uncompyle6 import PYTHON3, IS_PYPY
from uncompyle6.scanners.tok import Token from uncompyle6.scanners.tok import Token
import xdis import xdis
from xdis.bytecode import Bytecode, instruction_size, extended_arg_val, next_offset from xdis import Bytecode, canonic_python_version, code2num, instruction_size, extended_arg_val, next_offset
from xdis.magics import canonic_python_version
from xdis.util import code2num
# The byte code versions we support. # The byte code versions we support.
# Note: these all have to be floats # Note: these all have to be floats

View File

@@ -35,8 +35,8 @@ Finally we save token information.
from __future__ import print_function from __future__ import print_function
from xdis import iscode from xdis import iscode, instruction_size
from xdis.bytecode import instruction_size, _get_const_info from xdis.bytecode import _get_const_info
from uncompyle6.scanner import Token, parse_fn_counts from uncompyle6.scanner import Token, parse_fn_counts
import xdis import xdis

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016, 2017 by Rocky Bernstein # Copyright (c) 2016-2017, 2020 by Rocky Bernstein
""" """
Python 3.0 bytecode scanner/deparser Python 3.0 bytecode scanner/deparser
@@ -10,17 +10,19 @@ from __future__ import print_function
# bytecode verification, verify(), uses JUMP_OPs from here # bytecode verification, verify(), uses JUMP_OPs from here
from xdis.opcodes import opcode_30 as opc from xdis.opcodes import opcode_30 as opc
from xdis.bytecode import instruction_size from xdis import instruction_size
import xdis import xdis
JUMP_TF = frozenset([opc.JUMP_IF_FALSE, opc.JUMP_IF_TRUE]) JUMP_TF = frozenset([opc.JUMP_IF_FALSE, opc.JUMP_IF_TRUE])
from uncompyle6.scanners.scanner3 import Scanner3 from uncompyle6.scanners.scanner3 import Scanner3
class Scanner30(Scanner3):
class Scanner30(Scanner3):
def __init__(self, show_asm=None, is_pypy=False): def __init__(self, show_asm=None, is_pypy=False):
Scanner3.__init__(self, 3.0, show_asm, is_pypy) Scanner3.__init__(self, 3.0, show_asm, is_pypy)
return return
pass pass
def detect_control_flow(self, offset, targets, inst_index): def detect_control_flow(self, offset, targets, inst_index):
@@ -35,17 +37,18 @@ class Scanner30(Scanner3):
# Detect parent structure # Detect parent structure
parent = self.structs[0] parent = self.structs[0]
start = parent['start'] start = parent["start"]
end = parent['end'] end = parent["end"]
# Pick inner-most parent for our offset # Pick inner-most parent for our offset
for struct in self.structs: for struct in self.structs:
current_start = struct['start'] current_start = struct["start"]
current_end = struct['end'] current_end = struct["end"]
if ((current_start <= offset < current_end) if (current_start <= offset < current_end) and (
and (current_start >= start and current_end <= end)): current_start >= start and current_end <= end
start = current_start ):
end = current_end start = current_start
end = current_end
parent = struct parent = struct
if op == self.opc.SETUP_LOOP: if op == self.opc.SETUP_LOOP:
@@ -56,28 +59,35 @@ class Scanner30(Scanner3):
start += instruction_size(op, self.opc) start += instruction_size(op, self.opc)
target = self.get_target(offset) target = self.get_target(offset)
end = self.restrict_to_parent(target, parent) end = self.restrict_to_parent(target, parent)
self.setup_loops[target] = offset self.setup_loops[target] = offset
if target != end: if target != end:
self.fixed_jumps[offset] = end self.fixed_jumps[offset] = end
(line_no, next_line_byte) = self.lines[offset] (line_no, next_line_byte) = self.lines[offset]
jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, jump_back = self.last_instr(
next_line_byte, False) start, end, self.opc.JUMP_ABSOLUTE, next_line_byte, False
)
if jump_back: if jump_back:
jump_forward_offset = xdis.next_offset(code[jump_back], self.opc, jump_back) jump_forward_offset = xdis.next_offset(
code[jump_back], self.opc, jump_back
)
else: else:
jump_forward_offset = None jump_forward_offset = None
return_val_offset1 = self.prev[self.prev[end]] return_val_offset1 = self.prev[self.prev[end]]
if (jump_back and jump_back != self.prev_op[end] if (
and self.is_jump_forward(jump_forward_offset)): jump_back
if (code[self.prev_op[end]] == self.opc.RETURN_VALUE or and jump_back != self.prev_op[end]
(code[self.prev_op[end]] == self.opc.POP_BLOCK and self.is_jump_forward(jump_forward_offset)
and code[return_val_offset1] == self.opc.RETURN_VALUE)): ):
if code[self.prev_op[end]] == self.opc.RETURN_VALUE or (
code[self.prev_op[end]] == self.opc.POP_BLOCK
and code[return_val_offset1] == self.opc.RETURN_VALUE
):
jump_back = None jump_back = None
if not jump_back: if not jump_back:
# loop suite ends in return # loop suite ends in return
@@ -92,56 +102,63 @@ class Scanner30(Scanner3):
if code[self.prev_op[next_line_byte]] not in JUMP_TF: if code[self.prev_op[next_line_byte]] not in JUMP_TF:
if_offset = self.prev[next_line_byte] if_offset = self.prev[next_line_byte]
if if_offset: if if_offset:
loop_type = 'while' loop_type = "while"
self.ignore_if.add(if_offset) self.ignore_if.add(if_offset)
else: else:
loop_type = 'for' loop_type = "for"
target = next_line_byte target = next_line_byte
end = jump_back + 3 end = jump_back + 3
else: else:
if self.get_target(jump_back) >= next_line_byte: if self.get_target(jump_back) >= next_line_byte:
jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False) jump_back = self.last_instr(
start, end, self.opc.JUMP_ABSOLUTE, start, False
)
jb_inst = self.get_inst(jump_back) jb_inst = self.get_inst(jump_back)
jb_next_offset = self.next_offset(jb_inst.opcode, jump_back) jb_next_offset = self.next_offset(jb_inst.opcode, jump_back)
if end > jb_next_offset and self.is_jump_forward(end): if end > jb_next_offset and self.is_jump_forward(end):
if self.is_jump_forward(jb_next_offset): if self.is_jump_forward(jb_next_offset):
if self.get_target(jump_back+4) == self.get_target(end): if self.get_target(jump_back + 4) == self.get_target(end):
self.fixed_jumps[offset] = jump_back+4 self.fixed_jumps[offset] = jump_back + 4
end = jb_next_offset end = jb_next_offset
elif target < offset: elif target < offset:
self.fixed_jumps[offset] = jump_back+4 self.fixed_jumps[offset] = jump_back + 4
end = jb_next_offset end = jb_next_offset
target = self.get_target(jump_back) target = self.get_target(jump_back)
if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER): if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER):
loop_type = 'for' loop_type = "for"
else: else:
loop_type = 'while' loop_type = "while"
test = self.prev_op[next_line_byte] test = self.prev_op[next_line_byte]
if test == offset: if test == offset:
loop_type = 'while 1' loop_type = "while 1"
elif self.code[test] in self.opc.JUMP_OPs: elif self.code[test] in self.opc.JUMP_OPs:
self.ignore_if.add(test) self.ignore_if.add(test)
test_target = self.get_target(test) test_target = self.get_target(test)
if test_target > (jump_back+3): if test_target > (jump_back + 3):
jump_back = test_target jump_back = test_target
self.not_continue.add(jump_back) self.not_continue.add(jump_back)
self.loops.append(target) self.loops.append(target)
self.structs.append({'type': loop_type + '-loop', self.structs.append(
'start': target, {"type": loop_type + "-loop", "start": target, "end": jump_back}
'end': jump_back}) )
after_jump_offset = xdis.next_offset(code[jump_back], self.opc, jump_back) after_jump_offset = xdis.next_offset(code[jump_back], self.opc, jump_back)
if (self.get_inst(after_jump_offset).opname == 'POP_TOP'): if self.get_inst(after_jump_offset).opname == "POP_TOP":
after_jump_offset = xdis.next_offset(code[after_jump_offset], self.opc, after_jump_offset = xdis.next_offset(
after_jump_offset) code[after_jump_offset], self.opc, after_jump_offset
)
if after_jump_offset != end: if after_jump_offset != end:
self.structs.append({'type': loop_type + '-else', self.structs.append(
'start': after_jump_offset, {
'end': end}) "type": loop_type + "-else",
"start": after_jump_offset,
"end": end,
}
)
elif op in self.pop_jump_tf: elif op in self.pop_jump_tf:
start = offset + instruction_size(op, self.opc) start = offset + instruction_size(op, self.opc)
target = self.get_target(offset) target = self.get_target(offset)
@@ -149,7 +166,7 @@ class Scanner30(Scanner3):
prev_op = self.prev_op prev_op = self.prev_op
# Do not let jump to go out of parent struct bounds # Do not let jump to go out of parent struct bounds
if target != rtarget and parent['type'] == 'and/or': if target != rtarget and parent["type"] == "and/or":
self.fixed_jumps[offset] = rtarget self.fixed_jumps[offset] = rtarget
return return
@@ -158,12 +175,15 @@ class Scanner30(Scanner3):
# rocky: if we have a conditional jump to the next instruction, then # rocky: if we have a conditional jump to the next instruction, then
# possibly I am "skipping over" a "pass" or null statement. # possibly I am "skipping over" a "pass" or null statement.
if ((code[prev_op[target]] in self.pop_jump_if_pop) and if (
(target > offset) and prev_op[target] != offset): (code[prev_op[target]] in self.pop_jump_if_pop)
and (target > offset)
and prev_op[target] != offset
):
self.fixed_jumps[offset] = prev_op[target] self.fixed_jumps[offset] = prev_op[target]
self.structs.append({'type': 'and/or', self.structs.append(
'start': start, {"type": "and/or", "start": start, "end": prev_op[target]}
'end': prev_op[target]}) )
return return
# The op offset just before the target jump offset is important # The op offset just before the target jump offset is important
@@ -176,35 +196,80 @@ class Scanner30(Scanner3):
# Search for another JUMP_IF_FALSE targetting the same op, # Search for another JUMP_IF_FALSE targetting the same op,
# in current statement, starting from current offset, and filter # in current statement, starting from current offset, and filter
# everything inside inner 'or' jumps and midline ifs # everything inside inner 'or' jumps and midline ifs
match = self.rem_or(start, self.next_stmt[offset], match = self.rem_or(
opc.JUMP_IF_FALSE, target) start, self.next_stmt[offset], opc.JUMP_IF_FALSE, target
)
# If we still have any offsets in set, start working on it # If we still have any offsets in set, start working on it
if match: if match:
is_jump_forward = self.is_jump_forward(pre_rtarget) is_jump_forward = self.is_jump_forward(pre_rtarget)
if (is_jump_forward and pre_rtarget not in self.stmts and if (
self.restrict_to_parent(self.get_target(pre_rtarget), parent) == rtarget): is_jump_forward
if (code[prev_op[pre_rtarget]] == self.opc.JUMP_ABSOLUTE and pre_rtarget not in self.stmts
and self.remove_mid_line_ifs([offset]) and and self.restrict_to_parent(
target == self.get_target(prev_op[pre_rtarget]) and self.get_target(pre_rtarget), parent
(prev_op[pre_rtarget] not in self.stmts or )
self.get_target(prev_op[pre_rtarget]) > prev_op[pre_rtarget]) and == rtarget
1 == len(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget], JUMP_TF, target)))): ):
if (
code[prev_op[pre_rtarget]] == self.opc.JUMP_ABSOLUTE
and self.remove_mid_line_ifs([offset])
and target == self.get_target(prev_op[pre_rtarget])
and (
prev_op[pre_rtarget] not in self.stmts
or self.get_target(prev_op[pre_rtarget])
> prev_op[pre_rtarget]
)
and 1
== len(
self.remove_mid_line_ifs(
self.rem_or(
start, prev_op[pre_rtarget], JUMP_TF, target
)
)
)
):
pass pass
elif (code[prev_op[pre_rtarget]] == self.opc.RETURN_VALUE elif (
and self.remove_mid_line_ifs([offset]) and code[prev_op[pre_rtarget]] == self.opc.RETURN_VALUE
1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget], and self.remove_mid_line_ifs([offset])
JUMP_TF, target))) | and 1
set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[pre_rtarget], == (
(opc.JUMP_IF_FALSE, len(
opc.JUMP_IF_TRUE, set(
opc.JUMP_ABSOLUTE), self.remove_mid_line_ifs(
pre_rtarget, True)))))): self.rem_or(
start,
prev_op[pre_rtarget],
JUMP_TF,
target,
)
)
)
| set(
self.remove_mid_line_ifs(
self.rem_or(
start,
prev_op[pre_rtarget],
(
opc.JUMP_IF_FALSE,
opc.JUMP_IF_TRUE,
opc.JUMP_ABSOLUTE,
),
pre_rtarget,
True,
)
)
)
)
)
):
pass pass
else: else:
fix = None fix = None
jump_ifs = self.inst_matches(start, self.next_stmt[offset], jump_ifs = self.inst_matches(
opc.JUMP_IF_FALSE) start, self.next_stmt[offset], opc.JUMP_IF_FALSE
)
last_jump_good = True last_jump_good = True
for j in jump_ifs: for j in jump_ifs:
if target == self.get_target(j): if target == self.get_target(j):
@@ -226,14 +291,19 @@ class Scanner30(Scanner3):
pass pass
elif self.is_jump_forward(next) and target == self.get_target(next): elif self.is_jump_forward(next) and target == self.get_target(next):
if code[prev_op[next]] == opc.JUMP_IF_FALSE: if code[prev_op[next]] == opc.JUMP_IF_FALSE:
if (code[next] == self.opc.JUMP_FORWARD if (
code[next] == self.opc.JUMP_FORWARD
or target != rtarget or target != rtarget
or code[prev_op[pre_rtarget]] not in or code[prev_op[pre_rtarget]]
(self.opc.JUMP_ABSOLUTE, self.opc.RETURN_VALUE)): not in (self.opc.JUMP_ABSOLUTE, self.opc.RETURN_VALUE)
):
self.fixed_jumps[offset] = prev_op[next] self.fixed_jumps[offset] = prev_op[next]
return return
elif (code[next] == self.opc.JUMP_ABSOLUTE and self.is_jump_forward(target) and elif (
self.get_target(target) == self.get_target(next)): code[next] == self.opc.JUMP_ABSOLUTE
and self.is_jump_forward(target)
and self.get_target(target) == self.get_target(next)
):
self.fixed_jumps[offset] = prev_op[next] self.fixed_jumps[offset] = prev_op[next]
return return
@@ -241,13 +311,17 @@ class Scanner30(Scanner3):
if offset in self.ignore_if: if offset in self.ignore_if:
return return
if (code[pre_rtarget] == self.opc.JUMP_ABSOLUTE and if (
pre_rtarget in self.stmts and code[pre_rtarget] == self.opc.JUMP_ABSOLUTE
pre_rtarget != offset and and pre_rtarget in self.stmts
prev_op[pre_rtarget] != offset and and pre_rtarget != offset
not (code[rtarget] == self.opc.JUMP_ABSOLUTE and and prev_op[pre_rtarget] != offset
code[rtarget+3] == self.opc.POP_BLOCK and and not (
code[prev_op[pre_rtarget]] != self.opc.JUMP_ABSOLUTE)): code[rtarget] == self.opc.JUMP_ABSOLUTE
and code[rtarget + 3] == self.opc.POP_BLOCK
and code[prev_op[pre_rtarget]] != self.opc.JUMP_ABSOLUTE
)
):
rtarget = pre_rtarget rtarget = pre_rtarget
# Does the "jump if" jump beyond a jump op? # Does the "jump if" jump beyond a jump op?
@@ -268,16 +342,17 @@ class Scanner30(Scanner3):
if_end = self.get_target(pre_rtarget, 0) if_end = self.get_target(pre_rtarget, 0)
# If the jump target is back, we are looping # If the jump target is back, we are looping
if (if_end < pre_rtarget and if if_end < pre_rtarget and (
(code[prev_op[if_end]] == self.opc.SETUP_LOOP)): code[prev_op[if_end]] == self.opc.SETUP_LOOP
if (if_end > start): ):
if if_end > start:
return return
end = self.restrict_to_parent(if_end, parent) end = self.restrict_to_parent(if_end, parent)
self.structs.append({'type': 'if-then', self.structs.append(
'start': start, {"type": "if-then", "start": start, "end": pre_rtarget}
'end': pre_rtarget}) )
self.not_continue.add(pre_rtarget) self.not_continue.add(pre_rtarget)
# if rtarget < end and ( # if rtarget < end and (
@@ -291,20 +366,17 @@ class Scanner30(Scanner3):
# self.else_start[rtarget] = end # self.else_start[rtarget] = end
elif self.is_jump_back(pre_rtarget, 0): elif self.is_jump_back(pre_rtarget, 0):
if_end = rtarget if_end = rtarget
self.structs.append({'type': 'if-then', self.structs.append(
'start': start, {"type": "if-then", "start": start, "end": pre_rtarget}
'end': pre_rtarget}) )
self.not_continue.add(pre_rtarget) self.not_continue.add(pre_rtarget)
elif code[pre_rtarget] in (self.opc.RETURN_VALUE, elif code[pre_rtarget] in (self.opc.RETURN_VALUE, self.opc.BREAK_LOOP):
self.opc.BREAK_LOOP): self.structs.append({"type": "if-then", "start": start, "end": rtarget})
self.structs.append({'type': 'if-then',
'start': start,
'end': rtarget})
# It is important to distingish if this return is inside some sort # It is important to distingish if this return is inside some sort
# except block return # except block return
jump_prev = prev_op[offset] jump_prev = prev_op[offset]
if self.is_pypy and code[jump_prev] == self.opc.COMPARE_OP: if self.is_pypy and code[jump_prev] == self.opc.COMPARE_OP:
if self.opc.cmp_op[code[jump_prev+1]] == 'exception-match': if self.opc.cmp_op[code[jump_prev + 1]] == "exception-match":
return return
if self.version >= 3.5: if self.version >= 3.5:
# Python 3.5 may remove as dead code a JUMP # Python 3.5 may remove as dead code a JUMP
@@ -332,7 +404,10 @@ class Scanner30(Scanner3):
if code[next_op] == self.opc.POP_TOP: if code[next_op] == self.opc.POP_TOP:
next_op = rtarget next_op = rtarget
for block in self.structs: for block in self.structs:
if block['type'] == 'while-loop' and block['end'] == next_op: if (
block["type"] == "while-loop"
and block["end"] == next_op
):
return return
next_op += instruction_size(self.code[next_op], self.opc) next_op += instruction_size(self.code[next_op], self.opc)
if code[next_op] == self.opc.POP_BLOCK: if code[next_op] == self.opc.POP_BLOCK:
@@ -342,20 +417,21 @@ class Scanner30(Scanner3):
self.fixed_jumps[offset] = rtarget self.fixed_jumps[offset] = rtarget
self.not_continue.add(pre_rtarget) self.not_continue.add(pre_rtarget)
elif op == self.opc.SETUP_EXCEPT: elif op == self.opc.SETUP_EXCEPT:
target = self.get_target(offset) target = self.get_target(offset)
end = self.restrict_to_parent(target, parent) end = self.restrict_to_parent(target, parent)
self.fixed_jumps[offset] = end self.fixed_jumps[offset] = end
elif op == self.opc.SETUP_FINALLY: elif op == self.opc.SETUP_FINALLY:
target = self.get_target(offset) target = self.get_target(offset)
end = self.restrict_to_parent(target, parent) end = self.restrict_to_parent(target, parent)
self.fixed_jumps[offset] = end self.fixed_jumps[offset] = end
elif op in self.jump_if_pop: elif op in self.jump_if_pop:
target = self.get_target(offset) target = self.get_target(offset)
if target > offset: if target > offset:
unop_target = self.last_instr(offset, target, self.opc.JUMP_FORWARD, target) unop_target = self.last_instr(
if unop_target and code[unop_target+3] != self.opc.ROT_TWO: offset, target, self.opc.JUMP_FORWARD, target
)
if unop_target and code[unop_target + 3] != self.opc.ROT_TWO:
self.fixed_jumps[offset] = unop_target self.fixed_jumps[offset] = unop_target
else: else:
self.fixed_jumps[offset] = self.restrict_to_parent(target, parent) self.fixed_jumps[offset] = self.restrict_to_parent(target, parent)
@@ -366,8 +442,11 @@ class Scanner30(Scanner3):
# misclassified as RETURN_END_IF. Handle that here. # misclassified as RETURN_END_IF. Handle that here.
# In RETURN_VALUE, JUMP_ABSOLUTE, RETURN_VALUE is never RETURN_END_IF # In RETURN_VALUE, JUMP_ABSOLUTE, RETURN_VALUE is never RETURN_END_IF
if op == self.opc.RETURN_VALUE: if op == self.opc.RETURN_VALUE:
if (offset+1 < len(code) and code[offset+1] == self.opc.JUMP_ABSOLUTE and if (
offset in self.return_end_ifs): offset + 1 < len(code)
and code[offset + 1] == self.opc.JUMP_ABSOLUTE
and offset in self.return_end_ifs
):
self.return_end_ifs.remove(offset) self.return_end_ifs.remove(offset)
pass pass
pass pass
@@ -377,8 +456,10 @@ class Scanner30(Scanner3):
# then RETURN_VALUE is not RETURN_END_IF # then RETURN_VALUE is not RETURN_END_IF
rtarget = self.get_target(offset) rtarget = self.get_target(offset)
rtarget_prev = self.prev[rtarget] rtarget_prev = self.prev[rtarget]
if (code[rtarget_prev] == self.opc.RETURN_VALUE and if (
rtarget_prev in self.return_end_ifs): code[rtarget_prev] == self.opc.RETURN_VALUE
and rtarget_prev in self.return_end_ifs
):
i = rtarget_prev i = rtarget_prev
while i != offset: while i != offset:
if code[i] in [opc.JUMP_FORWARD, opc.JUMP_ABSOLUTE]: if code[i] in [opc.JUMP_FORWARD, opc.JUMP_ABSOLUTE]:
@@ -388,15 +469,17 @@ class Scanner30(Scanner3):
pass pass
return return
if __name__ == "__main__": if __name__ == "__main__":
from uncompyle6 import PYTHON_VERSION from uncompyle6 import PYTHON_VERSION
if PYTHON_VERSION == 3.0: if PYTHON_VERSION == 3.0:
import inspect import inspect
co = inspect.currentframe().f_code co = inspect.currentframe().f_code
tokens, customize = Scanner30().ingest(co) tokens, customize = Scanner30().ingest(co)
for t in tokens: for t in tokens:
print(t) print(t)
pass pass
else: else:
print("Need to be Python 3.0 to demo; I am %s." % print("Need to be Python 3.0 to demo; I am %s." % PYTHON_VERSION)
PYTHON_VERSION)

View File

@@ -29,8 +29,8 @@ For example:
Finally we save token information. Finally we save token information.
""" """
from xdis import iscode from xdis import iscode, instruction_size, Instruction
from xdis.bytecode import instruction_size, _get_const_info, Instruction from xdis.bytecode import _get_const_info
from uncompyle6.scanner import Token from uncompyle6.scanner import Token
import xdis import xdis

View File

@@ -17,9 +17,8 @@
""" """
from uncompyle6.semantics.consts import TABLE_DIRECT from uncompyle6.semantics.consts import TABLE_DIRECT
from xdis.util import co_flags_is_async
from xdis import iscode from xdis import co_flags_is_async, iscode
from uncompyle6.scanner import Code from uncompyle6.scanner import Code
from uncompyle6.semantics.helper import ( from uncompyle6.semantics.helper import (
find_code_node, find_code_node,

View File

@@ -15,8 +15,7 @@
"""Isolate Python 3.5 version-specific semantic actions here. """Isolate Python 3.5 version-specific semantic actions here.
""" """
from xdis import iscode from xdis import co_flags_is_async, iscode
from xdis.util import co_flags_is_async
from uncompyle6.semantics.consts import ( from uncompyle6.semantics.consts import (
INDENT_PER_LEVEL, INDENT_PER_LEVEL,
PRECEDENCE, PRECEDENCE,

View File

@@ -135,8 +135,7 @@ import sys
IS_PYPY = "__pypy__" in sys.builtin_module_names IS_PYPY = "__pypy__" in sys.builtin_module_names
PYTHON3 = sys.version_info >= (3, 0) PYTHON3 = sys.version_info >= (3, 0)
from xdis import iscode from xdis import iscode, COMPILER_FLAG_BIT
from xdis.util import COMPILER_FLAG_BIT
from uncompyle6.parser import get_python_parser from uncompyle6.parser import get_python_parser
from uncompyle6.parsers.treenode import SyntaxTree from uncompyle6.parsers.treenode import SyntaxTree

View File

@@ -25,10 +25,9 @@ import xdis.std as dis
from subprocess import call from subprocess import call
import uncompyle6 import uncompyle6
from uncompyle6.scanner import (Token as ScannerToken, get_scanner) from uncompyle6.scanner import Token as ScannerToken, get_scanner
from uncompyle6 import PYTHON3 from uncompyle6 import PYTHON3
from xdis import iscode, load_file, load_module, pretty_code_flags from xdis import iscode, load_file, load_module, pretty_code_flags, PYTHON_MAGIC_INT
from xdis.magics import PYTHON_MAGIC_INT
# FIXME: DRY # FIXME: DRY
if PYTHON3: if PYTHON3:
@@ -41,63 +40,77 @@ else:
def code_equal(a, b): def code_equal(a, b):
return a.co_code == b.co_code return a.co_code == b.co_code
BIN_OP_FUNCS = { BIN_OP_FUNCS = {
'BINARY_POWER': operator.pow, "BINARY_POWER": operator.pow,
'BINARY_MULTIPLY': operator.mul, "BINARY_MULTIPLY": operator.mul,
'BINARY_DIVIDE': truediv, "BINARY_DIVIDE": truediv,
'BINARY_FLOOR_DIVIDE': operator.floordiv, "BINARY_FLOOR_DIVIDE": operator.floordiv,
'BINARY_TRUE_DIVIDE': operator.truediv, "BINARY_TRUE_DIVIDE": operator.truediv,
'BINARY_MODULO' : operator.mod, "BINARY_MODULO": operator.mod,
'BINARY_ADD': operator.add, "BINARY_ADD": operator.add,
'BINARY_SUBRACT': operator.sub, "BINARY_SUBRACT": operator.sub,
'BINARY_LSHIFT': operator.lshift, "BINARY_LSHIFT": operator.lshift,
'BINARY_RSHIFT': operator.rshift, "BINARY_RSHIFT": operator.rshift,
'BINARY_AND': operator.and_, "BINARY_AND": operator.and_,
'BINARY_XOR': operator.xor, "BINARY_XOR": operator.xor,
'BINARY_OR': operator.or_, "BINARY_OR": operator.or_,
} }
JUMP_OPS = None JUMP_OPS = None
# --- exceptions --- # --- exceptions ---
class VerifyCmpError(Exception): class VerifyCmpError(Exception):
pass pass
class CmpErrorConsts(VerifyCmpError): class CmpErrorConsts(VerifyCmpError):
"""Exception to be raised when consts differ.""" """Exception to be raised when consts differ."""
def __init__(self, name, index): def __init__(self, name, index):
self.name = name self.name = name
self.index = index self.index = index
def __str__(self): def __str__(self):
return 'Compare Error within Consts of %s at index %i' % \ return "Compare Error within Consts of %s at index %i" % (
(repr(self.name), self.index) repr(self.name),
self.index,
)
class CmpErrorConstsType(VerifyCmpError): class CmpErrorConstsType(VerifyCmpError):
"""Exception to be raised when consts differ.""" """Exception to be raised when consts differ."""
def __init__(self, name, index): def __init__(self, name, index):
self.name = name self.name = name
self.index = index self.index = index
def __str__(self): def __str__(self):
return 'Consts type differ in %s at index %i' % \ return "Consts type differ in %s at index %i" % (repr(self.name), self.index)
(repr(self.name), self.index)
class CmpErrorConstsLen(VerifyCmpError): class CmpErrorConstsLen(VerifyCmpError):
"""Exception to be raised when length of co_consts differs.""" """Exception to be raised when length of co_consts differs."""
def __init__(self, name, consts1, consts2): def __init__(self, name, consts1, consts2):
self.name = name self.name = name
self.consts = (consts1, consts2) self.consts = (consts1, consts2)
def __str__(self): def __str__(self):
return 'Consts length differs in %s:\n\n%i:\t%s\n\n%i:\t%s\n\n' % \ return "Consts length differs in %s:\n\n%i:\t%s\n\n%i:\t%s\n\n" % (
(repr(self.name), repr(self.name),
len(self.consts[0]), repr(self.consts[0]), len(self.consts[0]),
len(self.consts[1]), repr(self.consts[1])) repr(self.consts[0]),
len(self.consts[1]),
repr(self.consts[1]),
)
class CmpErrorCode(VerifyCmpError): class CmpErrorCode(VerifyCmpError):
"""Exception to be raised when code differs.""" """Exception to be raised when code differs."""
def __init__(self, name, index, token1, token2, tokens1, tokens2): def __init__(self, name, index, token1, token2, tokens1, tokens2):
self.name = name self.name = name
self.index = index self.index = index
@@ -106,57 +119,74 @@ class CmpErrorCode(VerifyCmpError):
self.tokens = [tokens1, tokens2] self.tokens = [tokens1, tokens2]
def __str__(self): def __str__(self):
s = reduce(lambda s, t: "%s%-37s\t%-37s\n" % (s, t[0], t[1]), s = reduce(
list(map(lambda a, b: (a, b), lambda s, t: "%s%-37s\t%-37s\n" % (s, t[0], t[1]),
self.tokens[0], list(map(lambda a, b: (a, b), self.tokens[0], self.tokens[1])),
self.tokens[1])), "Code differs in %s\n" % str(self.name),
'Code differs in %s\n' % str(self.name)) )
return ('Code differs in %s at offset %s [%s] != [%s]\n\n' % return (
(repr(self.name), self.index, "Code differs in %s at offset %s [%s] != [%s]\n\n"
repr(self.token1), repr(self.token2))) + s % (repr(self.name), self.index, repr(self.token1), repr(self.token2))
) + s
class CmpErrorCodeLen(VerifyCmpError): class CmpErrorCodeLen(VerifyCmpError):
"""Exception to be raised when code length differs.""" """Exception to be raised when code length differs."""
def __init__(self, name, tokens1, tokens2): def __init__(self, name, tokens1, tokens2):
self.name = name self.name = name
self.tokens = [tokens1, tokens2] self.tokens = [tokens1, tokens2]
def __str__(self): def __str__(self):
return reduce(lambda s, t: "%s%-37s\t%-37s\n" % (s, t[0], t[1]), return reduce(
list(map(lambda a, b: (a, b), lambda s, t: "%s%-37s\t%-37s\n" % (s, t[0], t[1]),
self.tokens[0], list(map(lambda a, b: (a, b), self.tokens[0], self.tokens[1])),
self.tokens[1])), "Code len differs in %s\n" % str(self.name),
'Code len differs in %s\n' % str(self.name)) )
class CmpErrorMember(VerifyCmpError): class CmpErrorMember(VerifyCmpError):
"""Exception to be raised when other members differ.""" """Exception to be raised when other members differ."""
def __init__(self, name, member, data1, data2): def __init__(self, name, member, data1, data2):
self.name = name self.name = name
self.member = member self.member = member
self.data = (data1, data2) self.data = (data1, data2)
def __str__(self): def __str__(self):
return 'Member %s differs in %s:\n\t%s\n\t%s\n' % \ return "Member %s differs in %s:\n\t%s\n\t%s\n" % (
(repr(self.member), repr(self.name), repr(self.member),
repr(self.data[0]), repr(self.data[1])) repr(self.name),
repr(self.data[0]),
repr(self.data[1]),
)
# --- compare --- # --- compare ---
# these members are ignored # these members are ignored
__IGNORE_CODE_MEMBERS__ = ['co_filename', 'co_firstlineno', 'co_lnotab', 'co_stacksize', 'co_names'] __IGNORE_CODE_MEMBERS__ = [
"co_filename",
"co_firstlineno",
"co_lnotab",
"co_stacksize",
"co_names",
]
def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
name=''): def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify, name=""):
""" """
Compare two code-objects. Compare two code-objects.
This is the main part of this module. This is the main part of this module.
""" """
# print code_obj1, type(code_obj2) # print code_obj1, type(code_obj2)
assert iscode(code_obj1), \ assert iscode(
"cmp_code_object first object type is %s, not code" % type(code_obj1) code_obj1
assert iscode(code_obj2), \ ), "cmp_code_object first object type is %s, not code" % type(code_obj1)
"cmp_code_object second object type is %s, not code" % type(code_obj2) assert iscode(
code_obj2
), "cmp_code_object second object type is %s, not code" % type(code_obj2)
# print dir(code_obj1) # print dir(code_obj1)
if isinstance(code_obj1, object): if isinstance(code_obj1, object):
# new style classes (Python 2.2) # new style classes (Python 2.2)
@@ -168,11 +198,12 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
assert dir(code_obj2) == code_obj2.__members__ assert dir(code_obj2) == code_obj2.__members__
assert code_obj1.__members__ == code_obj2.__members__ assert code_obj1.__members__ == code_obj2.__members__
if name == '__main__': if name == "__main__":
name = code_obj1.co_name name = code_obj1.co_name
else: else:
name = '%s.%s' % (name, code_obj1.co_name) name = "%s.%s" % (name, code_obj1.co_name)
if name == '.?': name = '__main__' if name == ".?":
name = "__main__"
if isinstance(code_obj1, object) and code_equal(code_obj1, code_obj2): if isinstance(code_obj1, object) and code_equal(code_obj1, code_obj2):
# use the new style code-classes' __cmp__ method, which # use the new style code-classes' __cmp__ method, which
@@ -184,22 +215,22 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
pass pass
if isinstance(code_obj1, object): if isinstance(code_obj1, object):
members = [x for x in dir(code_obj1) if x.startswith('co_')] members = [x for x in dir(code_obj1) if x.startswith("co_")]
else: else:
members = dir(code_obj1) members = dir(code_obj1)
members.sort() # ; members.reverse() members.sort() # ; members.reverse()
tokens1 = None tokens1 = None
for member in members: for member in members:
if member in __IGNORE_CODE_MEMBERS__ or verify != 'verify': if member in __IGNORE_CODE_MEMBERS__ or verify != "verify":
pass pass
elif member == 'co_code': elif member == "co_code":
if verify != 'strong': if verify != "strong":
continue continue
scanner = get_scanner(version, is_pypy, show_asm=False) scanner = get_scanner(version, is_pypy, show_asm=False)
global JUMP_OPS global JUMP_OPS
JUMP_OPS = list(scan.JUMP_OPS) + ['JUMP_BACK'] JUMP_OPS = list(scan.JUMP_OPS) + ["JUMP_BACK"]
# use changed Token class # use changed Token class
# We (re)set this here to save exception handling, # We (re)set this here to save exception handling,
@@ -208,25 +239,29 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
try: try:
# ingest both code-objects # ingest both code-objects
tokens1, customize = scanner.ingest(code_obj1) tokens1, customize = scanner.ingest(code_obj1)
del customize # save memory del customize # save memory
tokens2, customize = scanner.ingest(code_obj2) tokens2, customize = scanner.ingest(code_obj2)
del customize # save memory del customize # save memory
finally: finally:
scanner.resetTokenClass() # restore Token class scanner.resetTokenClass() # restore Token class
targets1 = dis.findlabels(code_obj1.co_code) targets1 = dis.findlabels(code_obj1.co_code)
tokens1 = [t for t in tokens1 if t.kind != 'COME_FROM'] tokens1 = [t for t in tokens1 if t.kind != "COME_FROM"]
tokens2 = [t for t in tokens2 if t.kind != 'COME_FROM'] tokens2 = [t for t in tokens2 if t.kind != "COME_FROM"]
i1 = 0; i2 = 0 i1 = 0
offset_map = {}; check_jumps = {} i2 = 0
offset_map = {}
check_jumps = {}
while i1 < len(tokens1): while i1 < len(tokens1):
if i2 >= len(tokens2): if i2 >= len(tokens2):
if len(tokens1) == len(tokens2) + 2 \ if (
and tokens1[-1].kind == 'RETURN_VALUE' \ len(tokens1) == len(tokens2) + 2
and tokens1[-2].kind == 'LOAD_CONST' \ and tokens1[-1].kind == "RETURN_VALUE"
and tokens1[-2].pattr is None \ and tokens1[-2].kind == "LOAD_CONST"
and tokens1[-3].kind == 'RETURN_VALUE': and tokens1[-2].pattr is None
and tokens1[-3].kind == "RETURN_VALUE"
):
break break
else: else:
raise CmpErrorCodeLen(name, tokens1, tokens2) raise CmpErrorCodeLen(name, tokens1, tokens2)
@@ -235,87 +270,144 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
for idx1, idx2, offset2 in check_jumps.get(tokens1[i1].offset, []): for idx1, idx2, offset2 in check_jumps.get(tokens1[i1].offset, []):
if offset2 != tokens2[i2].offset: if offset2 != tokens2[i2].offset:
raise CmpErrorCode(name, tokens1[idx1].offset, tokens1[idx1], raise CmpErrorCode(
tokens2[idx2], tokens1, tokens2) name,
tokens1[idx1].offset,
tokens1[idx1],
tokens2[idx2],
tokens1,
tokens2,
)
if tokens1[i1].kind != tokens2[i2].kind: if tokens1[i1].kind != tokens2[i2].kind:
if tokens1[i1].kind == 'LOAD_CONST' == tokens2[i2].kind: if tokens1[i1].kind == "LOAD_CONST" == tokens2[i2].kind:
i = 1 i = 1
while tokens1[i1+i].kind == 'LOAD_CONST': while tokens1[i1 + i].kind == "LOAD_CONST":
i += 1 i += 1
if tokens1[i1+i].kind.startswith(('BUILD_TUPLE', 'BUILD_LIST')) \ if tokens1[i1 + i].kind.startswith(
and i == int(tokens1[i1+i].kind.split('_')[-1]): ("BUILD_TUPLE", "BUILD_LIST")
t = tuple([ elem.pattr for elem in tokens1[i1:i1+i] ]) ) and i == int(tokens1[i1 + i].kind.split("_")[-1]):
t = tuple([elem.pattr for elem in tokens1[i1 : i1 + i]])
if t != tokens2[i2].pattr: if t != tokens2[i2].pattr:
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1], raise CmpErrorCode(
tokens2[i2], tokens1, tokens2) name,
tokens1[i1].offset,
tokens1[i1],
tokens2[i2],
tokens1,
tokens2,
)
i1 += i + 1 i1 += i + 1
i2 += 1 i2 += 1
continue continue
elif i == 2 and tokens1[i1+i].kind == 'ROT_TWO' and tokens2[i2+1].kind == 'UNPACK_SEQUENCE_2': elif (
i == 2
and tokens1[i1 + i].kind == "ROT_TWO"
and tokens2[i2 + 1].kind == "UNPACK_SEQUENCE_2"
):
i1 += 3 i1 += 3
i2 += 2 i2 += 2
continue continue
elif i == 2 and tokens1[i1+i].kind in BIN_OP_FUNCS: elif i == 2 and tokens1[i1 + i].kind in BIN_OP_FUNCS:
f = BIN_OP_FUNCS[tokens1[i1+i].kind] f = BIN_OP_FUNCS[tokens1[i1 + i].kind]
if f(tokens1[i1].pattr, tokens1[i1+1].pattr) == tokens2[i2].pattr: if (
f(tokens1[i1].pattr, tokens1[i1 + 1].pattr)
== tokens2[i2].pattr
):
i1 += 3 i1 += 3
i2 += 1 i2 += 1
continue continue
elif tokens1[i1].kind == 'UNARY_NOT': elif tokens1[i1].kind == "UNARY_NOT":
if tokens2[i2].kind == 'POP_JUMP_IF_TRUE': if tokens2[i2].kind == "POP_JUMP_IF_TRUE":
if tokens1[i1+1].kind == 'POP_JUMP_IF_FALSE': if tokens1[i1 + 1].kind == "POP_JUMP_IF_FALSE":
i1 += 2 i1 += 2
i2 += 1 i2 += 1
continue continue
elif tokens2[i2].kind == 'POP_JUMP_IF_FALSE': elif tokens2[i2].kind == "POP_JUMP_IF_FALSE":
if tokens1[i1+1].kind == 'POP_JUMP_IF_TRUE': if tokens1[i1 + 1].kind == "POP_JUMP_IF_TRUE":
i1 += 2 i1 += 2
i2 += 1 i2 += 1
continue continue
elif tokens1[i1].kind in ('JUMP_FORWARD', 'JUMP_BACK') \ elif (
and tokens1[i1-1].kind == 'RETURN_VALUE' \ tokens1[i1].kind in ("JUMP_FORWARD", "JUMP_BACK")
and tokens2[i2-1].kind in ('RETURN_VALUE', 'RETURN_END_IF') \ and tokens1[i1 - 1].kind == "RETURN_VALUE"
and int(tokens1[i1].offset) not in targets1: and tokens2[i2 - 1].kind in ("RETURN_VALUE", "RETURN_END_IF")
and int(tokens1[i1].offset) not in targets1
):
i1 += 1 i1 += 1
continue continue
elif tokens1[i1].kind == 'JUMP_BACK' and tokens2[i2].kind == 'CONTINUE': elif (
tokens1[i1].kind == "JUMP_BACK"
and tokens2[i2].kind == "CONTINUE"
):
# FIXME: should make sure that offset is inside loop, not outside of it # FIXME: should make sure that offset is inside loop, not outside of it
i1 += 2 i1 += 2
i2 += 2 i2 += 2
continue continue
elif tokens1[i1].kind == 'JUMP_FORWARD' and tokens2[i2].kind == 'JUMP_BACK' \ elif (
and tokens1[i1+1].kind == 'JUMP_BACK' and tokens2[i2+1].kind == 'JUMP_BACK' \ tokens1[i1].kind == "JUMP_FORWARD"
and int(tokens1[i1].pattr) == int(tokens1[i1].offset) + 3: and tokens2[i2].kind == "JUMP_BACK"
if int(tokens1[i1].pattr) == int(tokens1[i1+1].offset): and tokens1[i1 + 1].kind == "JUMP_BACK"
and tokens2[i2 + 1].kind == "JUMP_BACK"
and int(tokens1[i1].pattr) == int(tokens1[i1].offset) + 3
):
if int(tokens1[i1].pattr) == int(tokens1[i1 + 1].offset):
i1 += 2 i1 += 2
i2 += 2 i2 += 2
continue continue
elif tokens1[i1].kind == 'LOAD_NAME' and tokens2[i2].kind == 'LOAD_CONST' \ elif (
and tokens1[i1].pattr == 'None' and tokens2[i2].pattr is None: tokens1[i1].kind == "LOAD_NAME"
and tokens2[i2].kind == "LOAD_CONST"
and tokens1[i1].pattr == "None"
and tokens2[i2].pattr is None
):
pass pass
elif tokens1[i1].kind == 'LOAD_GLOBAL' and tokens2[i2].kind == 'LOAD_NAME' \ elif (
and tokens1[i1].pattr == tokens2[i2].pattr: tokens1[i1].kind == "LOAD_GLOBAL"
and tokens2[i2].kind == "LOAD_NAME"
and tokens1[i1].pattr == tokens2[i2].pattr
):
pass pass
elif tokens1[i1].kind == 'LOAD_ASSERT' and tokens2[i2].kind == 'LOAD_NAME' \ elif (
and tokens1[i1].pattr == tokens2[i2].pattr: tokens1[i1].kind == "LOAD_ASSERT"
and tokens2[i2].kind == "LOAD_NAME"
and tokens1[i1].pattr == tokens2[i2].pattr
):
pass pass
elif (tokens1[i1].kind == 'RETURN_VALUE' and elif (
tokens2[i2].kind == 'RETURN_END_IF'): tokens1[i1].kind == "RETURN_VALUE"
and tokens2[i2].kind == "RETURN_END_IF"
):
pass pass
elif (tokens1[i1].kind == 'BUILD_TUPLE_0' and elif (
tokens2[i2].pattr == ()): tokens1[i1].kind == "BUILD_TUPLE_0" and tokens2[i2].pattr == ()
):
pass pass
else: else:
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1], raise CmpErrorCode(
tokens2[i2], tokens1, tokens2) name,
elif tokens1[i1].kind in JUMP_OPS and tokens1[i1].pattr != tokens2[i2].pattr: tokens1[i1].offset,
if tokens1[i1].kind == 'JUMP_BACK': tokens1[i1],
tokens2[i2],
tokens1,
tokens2,
)
elif (
tokens1[i1].kind in JUMP_OPS
and tokens1[i1].pattr != tokens2[i2].pattr
):
if tokens1[i1].kind == "JUMP_BACK":
dest1 = int(tokens1[i1].pattr) dest1 = int(tokens1[i1].pattr)
dest2 = int(tokens2[i2].pattr) dest2 = int(tokens2[i2].pattr)
if offset_map[dest1] != dest2: if offset_map[dest1] != dest2:
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1], raise CmpErrorCode(
tokens2[i2], tokens1, tokens2) name,
tokens1[i1].offset,
tokens1[i1],
tokens2[i2],
tokens1,
tokens2,
)
else: else:
# import pdb; pdb.set_trace() # import pdb; pdb.set_trace()
try: try:
@@ -329,71 +421,84 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
i1 += 1 i1 += 1
i2 += 1 i2 += 1
del tokens1, tokens2 # save memory del tokens1, tokens2 # save memory
elif member == 'co_consts': elif member == "co_consts":
# partial optimization can make the co_consts look different, # partial optimization can make the co_consts look different,
# so we'll just compare the code consts # so we'll just compare the code consts
codes1 = ( c for c in code_obj1.co_consts if hasattr(c, 'co_consts') ) codes1 = (c for c in code_obj1.co_consts if hasattr(c, "co_consts"))
codes2 = ( c for c in code_obj2.co_consts if hasattr(c, 'co_consts') ) codes2 = (c for c in code_obj2.co_consts if hasattr(c, "co_consts"))
for c1, c2 in zip(codes1, codes2): for c1, c2 in zip(codes1, codes2):
cmp_code_objects(version, is_pypy, c1, c2, verify, cmp_code_objects(version, is_pypy, c1, c2, verify, name=name)
name=name) elif member == "co_flags":
elif member == 'co_flags':
flags1 = code_obj1.co_flags flags1 = code_obj1.co_flags
flags2 = code_obj2.co_flags flags2 = code_obj2.co_flags
if is_pypy: if is_pypy:
# For PYPY for now we don't care about PYPY_SOURCE_IS_UTF8: # For PYPY for now we don't care about PYPY_SOURCE_IS_UTF8:
flags2 &= ~0x0100 # PYPY_SOURCE_IS_UTF8 flags2 &= ~0x0100 # PYPY_SOURCE_IS_UTF8
# We also don't care about COROUTINE or GENERATOR for now # We also don't care about COROUTINE or GENERATOR for now
flags1 &= ~0x000000a0 flags1 &= ~0x000000A0
flags2 &= ~0x000000a0 flags2 &= ~0x000000A0
if flags1 != flags2: if flags1 != flags2:
raise CmpErrorMember(name, 'co_flags', raise CmpErrorMember(
pretty_code_flags(flags1), name,
pretty_code_flags(flags2)) "co_flags",
pretty_code_flags(flags1),
pretty_code_flags(flags2),
)
else: else:
# all other members must be equal # all other members must be equal
if getattr(code_obj1, member) != getattr(code_obj2, member): if getattr(code_obj1, member) != getattr(code_obj2, member):
raise CmpErrorMember(name, member, raise CmpErrorMember(
getattr(code_obj1, member), name, member, getattr(code_obj1, member), getattr(code_obj2, member)
getattr(code_obj2, member)) )
class Token(ScannerToken): class Token(ScannerToken):
"""Token class with changed semantics for 'cmp()'.""" """Token class with changed semantics for 'cmp()'."""
def __cmp__(self, o): def __cmp__(self, o):
t = self.kind # shortcut t = self.kind # shortcut
if t == 'BUILD_TUPLE_0' and o.kind == 'LOAD_CONST' and o.pattr == (): if t == "BUILD_TUPLE_0" and o.kind == "LOAD_CONST" and o.pattr == ():
return 0 return 0
if t == 'COME_FROM' == o.kind: if t == "COME_FROM" == o.kind:
return 0 return 0
if t == 'PRINT_ITEM_CONT' and o.kind == 'PRINT_ITEM': if t == "PRINT_ITEM_CONT" and o.kind == "PRINT_ITEM":
return 0 return 0
if t == 'RETURN_VALUE' and o.kind == 'RETURN_END_IF': if t == "RETURN_VALUE" and o.kind == "RETURN_END_IF":
return 0 return 0
if t == 'JUMP_IF_FALSE_OR_POP' and o.kind == 'POP_JUMP_IF_FALSE': if t == "JUMP_IF_FALSE_OR_POP" and o.kind == "POP_JUMP_IF_FALSE":
return 0 return 0
if JUMP_OPS and t in JUMP_OPS: if JUMP_OPS and t in JUMP_OPS:
# ignore offset # ignore offset
return t == o.kind return t == o.kind
return (t == o.kind) or self.pattr == o.pattr return (t == o.kind) or self.pattr == o.pattr
def __repr__(self): def __repr__(self):
return '%s %s (%s)' % (str(self.kind), str(self.attr), return "%s %s (%s)" % (str(self.kind), str(self.attr), repr(self.pattr))
repr(self.pattr))
def __str__(self): def __str__(self):
return '%s\t%-17s %r' % (self.offset, self.kind, self.pattr) return "%s\t%-17s %r" % (self.offset, self.kind, self.pattr)
def compare_code_with_srcfile(pyc_filename, src_filename, verify): def compare_code_with_srcfile(pyc_filename, src_filename, verify):
"""Compare a .pyc with a source code file. If everything is okay, None """Compare a .pyc with a source code file. If everything is okay, None
is returned. Otherwise a string message describing the mismatch is returned. is returned. Otherwise a string message describing the mismatch is returned.
""" """
(version, timestamp, magic_int, code_obj1, is_pypy, (
source_size, sip_hash) = load_module(pyc_filename) version,
timestamp,
magic_int,
code_obj1,
is_pypy,
source_size,
sip_hash,
) = load_module(pyc_filename)
if magic_int != PYTHON_MAGIC_INT: if magic_int != PYTHON_MAGIC_INT:
msg = ("Can't compare code - Python is running with magic %s, but code is magic %s " msg = (
% (PYTHON_MAGIC_INT, magic_int)) "Can't compare code - Python is running with magic %s, but code is magic %s "
% (PYTHON_MAGIC_INT, magic_int)
)
return msg return msg
try: try:
code_obj2 = load_file(src_filename) code_obj2 = load_file(src_filename)
@@ -401,7 +506,7 @@ def compare_code_with_srcfile(pyc_filename, src_filename, verify):
# src_filename can be the first of a group sometimes # src_filename can be the first of a group sometimes
return str(e).replace(src_filename, pyc_filename) return str(e).replace(src_filename, pyc_filename)
cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify) cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify)
if verify == 'verify-run': if verify == "verify-run":
try: try:
retcode = call("%s %s" % (sys.executable, src_filename), shell=True) retcode = call("%s %s" % (sys.executable, src_filename), shell=True)
if retcode != 0: if retcode != 0:
@@ -412,19 +517,35 @@ def compare_code_with_srcfile(pyc_filename, src_filename, verify):
pass pass
return None return None
def compare_files(pyc_filename1, pyc_filename2, verify): def compare_files(pyc_filename1, pyc_filename2, verify):
"""Compare two .pyc files.""" """Compare two .pyc files."""
(version1, timestamp, magic_int1, code_obj1, is_pypy, (
source_size, sip_hash) = uncompyle6.load_module(pyc_filename1) version1,
(version2, timestamp, magic_int2, code_obj2, is_pypy, timestamp,
source_size, sip_hash) = uncompyle6.load_module(pyc_filename2) magic_int1,
if (magic_int1 != magic_int2) and verify == 'verify': code_obj1,
verify = 'weak_verify' is_pypy,
source_size,
sip_hash,
) = uncompyle6.load_module(pyc_filename1)
(
version2,
timestamp,
magic_int2,
code_obj2,
is_pypy,
source_size,
sip_hash,
) = uncompyle6.load_module(pyc_filename2)
if (magic_int1 != magic_int2) and verify == "verify":
verify = "weak_verify"
cmp_code_objects(version1, is_pypy, code_obj1, code_obj2, verify) cmp_code_objects(version1, is_pypy, code_obj1, code_obj2, verify)
if __name__ == '__main__':
t1 = Token('LOAD_CONST', None, 'code_object _expandLang', 52) if __name__ == "__main__":
t2 = Token('LOAD_CONST', -421, 'code_object _expandLang', 55) t1 = Token("LOAD_CONST", None, "code_object _expandLang", 52)
t2 = Token("LOAD_CONST", -421, "code_object _expandLang", 55)
print(repr(t1)) print(repr(t1))
print(repr(t2)) print(repr(t2))
print(t1.kind == t2.kind, t1.attr == t2.attr) print(t1.kind == t2.kind, t1.attr == t2.attr)

View File

@@ -12,4 +12,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# This file is suitable for sourcing inside POSIX shell as # This file is suitable for sourcing inside POSIX shell as
# well as importing into Python # well as importing into Python
VERSION="3.6.7" # noqa VERSION="3.7.0" # noqa