Merge branch 'master' into python-2.4

This commit is contained in:
rocky
2020-05-19 01:24:08 -04:00
13 changed files with 480 additions and 274 deletions

View File

@@ -1,3 +1,12 @@
3.7.0: 2020-5-19 Primidi 1st Prairial - Alfalfa - HF
====================================================
The main impetus for this release is to pull in the recent changes from xdis.
We simplify imports using xdis 4.6.0.
There were some bugfixes to Python 3.4-3.8. See the ChangeLog for details
3.6.7: 2020-4-27 xdis again 3.6.7: 2020-4-27 xdis again
=========================== ===========================

View File

@@ -2,7 +2,7 @@
from uncompyle6 import uncompyle from uncompyle6 import uncompyle
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

@@ -32,8 +32,7 @@ want to run on earlier Python versions.
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

@@ -15,8 +15,7 @@
import datetime, os, subprocess, sys import datetime, os, subprocess, sys
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
@@ -27,9 +27,7 @@ import sys
from uncompyle6 import PYTHON3, IS_PYPY, PYTHON_VERSION from uncompyle6 import PYTHON3, IS_PYPY, PYTHON_VERSION
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
if PYTHON_VERSION < 2.6: if PYTHON_VERSION < 2.6:
from xdis.namedtuple24 import namedtuple from xdis.namedtuple24 import namedtuple

View File

@@ -40,8 +40,8 @@ if PYTHON_VERSION < 2.6:
else: else:
from collections import namedtuple from collections import namedtuple
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
@@ -896,7 +896,7 @@ class Scanner3(Scanner):
start, self.next_stmt[offset], self.opc.POP_JUMP_IF_FALSE, target start, self.next_stmt[offset], self.opc.POP_JUMP_IF_FALSE, target
) )
# FIXME: Remoeve this whole "if" block # FIXME: Remove this whole "if" block
# 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)

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
@@ -8,17 +8,19 @@ scanner routine for Python 3.
# 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):
@@ -33,15 +35,16 @@ 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 start = current_start
end = current_end end = current_end
parent = struct parent = struct
@@ -61,21 +64,28 @@ class Scanner30(Scanner3):
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
@@ -90,15 +100,17 @@ 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)
@@ -115,13 +127,13 @@ class Scanner30(Scanner3):
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)
@@ -129,17 +141,22 @@ class Scanner30(Scanner3):
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)
@@ -147,7 +164,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
@@ -156,12 +173,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
@@ -174,35 +194,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(
set(
self.remove_mid_line_ifs(
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_IF_TRUE,
opc.JUMP_ABSOLUTE), opc.JUMP_ABSOLUTE,
pre_rtarget, True)))))): ),
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):
@@ -224,14 +289,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
@@ -239,13 +309,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?
@@ -266,16 +340,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 (
@@ -289,20 +364,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
@@ -330,7 +402,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:
@@ -340,7 +415,6 @@ 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)
@@ -352,7 +426,9 @@ class Scanner30(Scanner3):
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(
offset, target, self.opc.JUMP_FORWARD, target
)
if unop_target and code[unop_target + 3] != self.opc.ROT_TWO: 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:
@@ -364,8 +440,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
@@ -375,8 +454,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]:
@@ -386,15 +467,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

@@ -23,10 +23,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:
@@ -39,63 +38,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
@@ -104,57 +117,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)
@@ -166,11 +196,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
@@ -182,22 +213,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,
@@ -213,18 +244,22 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
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)
@@ -233,87 +268,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")
) and i == int(tokens1[i1 + i].kind.split("_")[-1]):
t = tuple([elem.pattr for elem in tokens1[i1 : i1 + i]]) 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"
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): 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:
@@ -331,12 +423,11 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
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
@@ -346,32 +437,37 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify,
# where or why # where or why
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(
name,
"co_flags",
pretty_code_flags(flags1), pretty_code_flags(flags1),
pretty_code_flags(flags2)) 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
@@ -379,21 +475,30 @@ class Token(ScannerToken):
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)
@@ -403,7 +508,7 @@ def compare_code_with_srcfile(pyc_filename, src_filename, verify):
print(pyc_filename) print(pyc_filename)
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:
@@ -414,19 +519,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