From bf58fb9cf285c3f0b963cdc8bf5a12b27b7bcead Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 25 Apr 2022 07:32:24 -0400 Subject: [PATCH 1/3] WIP - extend fast long-literals into older Python3 --- uncompyle6/parsers/parse3.py | 16 +++ uncompyle6/scanner.py | 74 -------------- uncompyle6/scanners/scanner3.py | 162 ++++++++++++++++++++++++++----- uncompyle6/scanners/scanner37.py | 83 +++++++++++++++- 4 files changed, 231 insertions(+), 104 deletions(-) diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index 975a19c5..039a51db 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -814,6 +814,22 @@ class Python3Parser(PythonParser): rule = "starred ::= %s %s" % ("expr " * v, opname) self.addRule(rule, nop_func) + elif opname in ("BUILD_CONST_LIST", "BUILD_CONST_DICT", "BUILD_CONST_SET"): + if opname == "BUILD_CONST_DICT": + rule = f""" + add_consts ::= ADD_VALUE* + const_list ::= COLLECTION_START add_consts {opname} + dict ::= const_list + expr ::= dict + """ + else: + rule = f""" + add_consts ::= ADD_VALUE* + const_list ::= COLLECTION_START add_consts {opname} + expr ::= const_list + """ + self.addRule(rule, nop_func) + elif opname_base in ( "BUILD_LIST", "BUILD_SET", diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index 30dcab2d..87b6fc3b 100644 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -125,80 +125,6 @@ class Scanner(object): # FIXME: This weird Python2 behavior is not Python3 self.resetTokenClass() - def bound_collection( - self, tokens: list, next_tokens: list, t: Token, i: int, collection_type: str - ): - count = t.attr - assert isinstance(count, int) - - assert count <= i - - if collection_type == "CONST_DICT": - # constant dictonaries work via BUILD_CONST_KEY_MAP and - # handle the values() like sets and lists. - # However the keys() are an LOAD_CONST of the keys. - # adjust offset to account for this - count += 1 - - # For small lists don't bother - if count < 5: - return next_tokens + [t] - - collection_start = i - count - - for j in range(collection_start, i): - if tokens[j].kind not in ( - "LOAD_CONST", - "LOAD_FAST", - "LOAD_GLOBAL", - "LOAD_NAME", - ): - return next_tokens + [t] - - collection_enum = CONST_COLLECTIONS.index(collection_type) - - # If we go there all instructions before tokens[i] are LOAD_CONST and we can replace - # add a boundary marker and change LOAD_CONST to something else - new_tokens = next_tokens[:-count] - start_offset = tokens[collection_start].offset - new_tokens.append( - Token( - opname="COLLECTION_START", - attr=collection_enum, - pattr=collection_type, - offset=f"{start_offset}_0", - has_arg=True, - opc=self.opc, - has_extended_arg=False, - ) - ) - for j in range(collection_start, i): - new_tokens.append( - Token( - opname="ADD_VALUE", - attr=tokens[j].attr, - pattr=tokens[j].pattr, - offset=tokens[j].offset, - has_arg=True, - linestart=tokens[j].linestart, - opc=self.opc, - has_extended_arg=False, - ) - ) - new_tokens.append( - Token( - opname=f"BUILD_{collection_type}", - attr=t.attr, - pattr=t.pattr, - offset=t.offset, - has_arg=t.has_arg, - linestart=t.linestart, - opc=t.opc, - has_extended_arg=False, - ) - ) - return new_tokens - def build_instructions(self, co): """ Create a list of instructions (a structured object rather than diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index 1e7396e5..325f82de 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -35,16 +35,19 @@ Finally we save token information. from __future__ import print_function -from xdis import iscode, instruction_size +from typing import Tuple + +from xdis import iscode, instruction_size, Instruction from xdis.bytecode import _get_const_info -from uncompyle6.scanner import Token, parse_fn_counts +from uncompyle6.scanners.tok import Token +from uncompyle6.scanner import parse_fn_counts import xdis # Get all the opcodes into globals import xdis.opcodes.opcode_33 as op3 -from uncompyle6.scanner import Scanner +from uncompyle6.scanner import Scanner, CONST_COLLECTIONS import sys @@ -204,17 +207,108 @@ class Scanner3(Scanner): # self.varargs_ops = frozenset(self.opc.hasvargs) return - def ingest(self, co, classname=None, code_objects={}, show_asm=None): + def bound_collection_from_inst( + self, insts: list, next_tokens: list, inst: Instruction, i: int, collection_type: str + ) -> list: + t = Token( + opname=inst.opname, + attr=inst.argval, + pattr=inst.argrepr, + offset=inst.offset, + linestart=inst.starts_line, + op=inst.opcode, + has_arg=inst.has_arg, + has_extended_arg=inst.has_extended_arg, + opc=self.opc, + ) + + count = t.attr + assert isinstance(count, int) + + assert count <= i + + if collection_type == "CONST_DICT": + # constant dictonaries work via BUILD_CONST_KEY_MAP and + # handle the values() like sets and lists. + # However the keys() are an LOAD_CONST of the keys. + # adjust offset to account for this + count += 1 + + # For small lists don't bother + if count < 5: + return next_tokens + [t] + + collection_start = i - count + + for j in range(collection_start, i): + if insts[j].opname not in ( + "LOAD_CONST", + "LOAD_FAST", + "LOAD_GLOBAL", + "LOAD_NAME", + ): + return next_tokens + [t] + + collection_enum = CONST_COLLECTIONS.index(collection_type) + + # If we get here, all instructions before tokens[i] are LOAD_CONST and we can replace + # add a boundary marker and change LOAD_CONST to something else + new_tokens = next_tokens[:-count] + start_offset = insts[collection_start].offset + new_tokens.append( + Token( + opname="COLLECTION_START", + attr=collection_enum, + pattr=collection_type, + offset=f"{start_offset}_0", + linestart=False, + has_arg=True, + has_extended_arg=False, + opc=self.opc, + ) + ) + for j in range(collection_start, i): + new_tokens.append( + Token( + opname="ADD_VALUE", + attr=insts[j].argval, + pattr=insts[j].argrepr, + offset=insts[j].offset, + linestart=insts[j].starts_line, + has_arg=True, + has_extended_arg=False, + opc=self.opc, + ) + ) + new_tokens.append( + Token( + opname=f"BUILD_{collection_type}", + attr=t.attr, + pattr=t.pattr, + offset=t.offset, + linestart=t.linestart, + has_arg=t.has_arg, + has_extended_arg=False, + opc=t.opc, + ) + ) + return new_tokens + + def ingest(self, co, classname=None, code_objects={}, show_asm=None + ) -> Tuple[list, dict]: """ - Pick out tokens from an uncompyle6 code object, and transform them, + Create "tokens" the bytecode of an Python code object. Largely these + are the opcode name, but in some cases that has been modified to make parsing + easier. returning a list of uncompyle6 Token's. - The transformations are made to assist the deparsing grammar. - Specificially: + Some transformations are made to assist the deparsing grammar: - various types of LOAD_CONST's are categorized in terms of what they load - COME_FROM instructions are added to assist parsing control structures - - MAKE_FUNCTION and FUNCTION_CALLS append the number of positional arguments - - some EXTENDED_ARGS instructions are removed + - operands with stack argument counts or flag masks are appended to the opcode name, e.g.: + * BUILD_LIST, BUILD_SET + * MAKE_FUNCTION and FUNCTION_CALLS append the number of positional arguments + - EXTENDED_ARGS instructions are removed Also, when we encounter certain tokens, we add them to a set which will cause custom grammar rules. Specifically, variable arg tokens like MAKE_FUNCTION or BUILD_LIST @@ -231,9 +325,6 @@ class Scanner3(Scanner): for instr in bytecode.get_instructions(co): print(instr.disassemble()) - # list of tokens/instructions - tokens = [] - # "customize" is in the process of going away here customize = {} @@ -248,6 +339,7 @@ class Scanner3(Scanner): n = len(self.insts) for i, inst in enumerate(self.insts): + opname = inst.opname # We need to detect the difference between: # raise AssertionError # and @@ -258,7 +350,7 @@ class Scanner3(Scanner): if self.version[:2] == (3, 0): # Like 2.6, 3.0 doesn't have POP_JUMP_IF... so we have # to go through more machinations - assert_can_follow = inst.opname == "POP_TOP" and i + 1 < n + assert_can_follow = opname == "POP_TOP" and i + 1 < n if assert_can_follow: prev_inst = self.insts[i - 1] assert_can_follow = ( @@ -267,7 +359,7 @@ class Scanner3(Scanner): jump_if_inst = prev_inst else: assert_can_follow = ( - inst.opname in ("POP_JUMP_IF_TRUE", "POP_JUMP_IF_FALSE") + opname in ("POP_JUMP_IF_TRUE", "POP_JUMP_IF_FALSE") and i + 1 < n ) jump_if_inst = inst @@ -291,13 +383,32 @@ class Scanner3(Scanner): # print("XXX2", jump_targets) last_op_was_break = False + new_tokens = [] for i, inst in enumerate(self.insts): + opname = inst.opname + + # things that smash new_tokens like BUILD_LIST have to come first. + if opname in ( + "BUILD_CONST_KEY_MAP", + "BUILD_LIST", + "BUILD_SET", + ): + collection_type = ( + "DICT" + if opname.startswith("BUILD_CONST_KEY_MAP") + else opname.split("_")[1] + ) + new_tokens = self.bound_collection_from_inst( + self.insts, new_tokens, inst, i, f"CONST_{collection_type}" + ) + continue + argval = inst.argval op = inst.opcode - if inst.opname == "EXTENDED_ARG": + if opname == "EXTENDED_ARG": # FIXME: The EXTENDED_ARG is used to signal annotation # parameters if i + 1 < n and self.insts[i + 1].opcode != self.opc.MAKE_FUNCTION: @@ -324,7 +435,7 @@ class Scanner3(Scanner): pass elif inst.offset in self.except_targets: come_from_name = "COME_FROM_EXCEPT_CLAUSE" - tokens.append( + new_tokens.append( Token( come_from_name, jump_offset, @@ -339,7 +450,7 @@ class Scanner3(Scanner): pass elif inst.offset in self.else_start: end_offset = self.else_start[inst.offset] - tokens.append( + new_tokens.append( Token( "ELSE", None, @@ -353,7 +464,6 @@ class Scanner3(Scanner): pass pattr = inst.argrepr - opname = inst.opname if op in self.opc.CONST_OPS: const = argval @@ -422,7 +532,7 @@ class Scanner3(Scanner): pass opname = "%s_%d" % (opname, pos_args) attr = (pos_args, name_pair_args, annotate_args) - tokens.append( + new_tokens.append( Token( opname=opname, attr=attr, @@ -508,12 +618,12 @@ class Scanner3(Scanner): # the "continue" is not on a new line. # There are other situations where we don't catch # CONTINUE as well. - if tokens[-1].kind == "JUMP_BACK" and tokens[-1].attr <= argval: - if tokens[-2].kind == "BREAK_LOOP": - del tokens[-1] + if new_tokens[-1].kind == "JUMP_BACK" and new_tokens[-1].attr <= argval: + if new_tokens[-2].kind == "BREAK_LOOP": + del new_tokens[-1] else: # intern is used because we are changing the *previous* token - tokens[-1].kind = intern("CONTINUE") + new_tokens[-1].kind = intern("CONTINUE") if last_op_was_break and opname == "CONTINUE": last_op_was_break = False continue @@ -527,7 +637,7 @@ class Scanner3(Scanner): opname = "LOAD_ASSERT" last_op_was_break = opname == "BREAK_LOOP" - tokens.append( + new_tokens.append( Token( opname=opname, attr=argval, @@ -542,10 +652,10 @@ class Scanner3(Scanner): pass if show_asm in ("both", "after"): - for t in tokens: + for t in new_tokens: print(t.format(line_prefix="")) print() - return tokens, customize + return new_tokens, customize def find_jump_targets(self, debug): """ diff --git a/uncompyle6/scanners/scanner37.py b/uncompyle6/scanners/scanner37.py index 7ccfc91c..28b280ff 100644 --- a/uncompyle6/scanners/scanner37.py +++ b/uncompyle6/scanners/scanner37.py @@ -23,6 +23,9 @@ scanner routine for Python 3. """ from typing import Tuple + +from uncompyle6.scanner import CONST_COLLECTIONS +from uncompyle6.scanners.tok import Token from uncompyle6.scanners.scanner37base import Scanner37Base # bytecode verification, verify(), uses JUMP_OPs from here @@ -31,9 +34,6 @@ from xdis.opcodes import opcode_37 as opc # bytecode verification, verify(), uses JUMP_OPS from here JUMP_OPs = opc.JUMP_OPS -CONST_COLLECTIONS = ("CONST_LIST", "CONST_SET", "CONST_DICT") - - class Scanner37(Scanner37Base): def __init__(self, show_asm=None, is_pypy: bool=False): Scanner37Base.__init__(self, (3, 7), show_asm) @@ -42,6 +42,81 @@ class Scanner37(Scanner37Base): pass + def bound_collection_from_tokens( + self, tokens: list, next_tokens: list, t: Token, i: int, collection_type: str + ) -> list: + count = t.attr + assert isinstance(count, int) + + assert count <= i + + if collection_type == "CONST_DICT": + # constant dictonaries work via BUILD_CONST_KEY_MAP and + # handle the values() like sets and lists. + # However the keys() are an LOAD_CONST of the keys. + # adjust offset to account for this + count += 1 + + # For small lists don't bother + if count < 5: + return next_tokens + [t] + + collection_start = i - count + + for j in range(collection_start, i): + if tokens[j].kind not in ( + "LOAD_CONST", + "LOAD_FAST", + "LOAD_GLOBAL", + "LOAD_NAME", + ): + return next_tokens + [t] + + collection_enum = CONST_COLLECTIONS.index(collection_type) + + # If we go there all instructions before tokens[i] are LOAD_CONST and we can replace + # add a boundary marker and change LOAD_CONST to something else + new_tokens = next_tokens[:-count] + start_offset = tokens[collection_start].offset + new_tokens.append( + Token( + opname="COLLECTION_START", + attr=collection_enum, + pattr=collection_type, + offset=f"{start_offset}_0", + linestart=False, + has_arg=True, + has_extended_arg=False, + opc=self.opc, + ) + ) + for j in range(collection_start, i): + new_tokens.append( + Token( + opname="ADD_VALUE", + attr=tokens[j].attr, + pattr=tokens[j].pattr, + offset=tokens[j].offset, + linestart=tokens[j].linestart, + has_arg=True, + has_extended_arg=False, + opc=self.opc, + ) + ) + new_tokens.append( + Token( + opname=f"BUILD_{collection_type}", + attr=t.attr, + pattr=t.pattr, + offset=t.offset, + linestart=t.linestart, + has_arg=t.has_arg, + has_extended_arg=False, + opc=t.opc, + ) + ) + return new_tokens + def ingest( self, co, classname=None, code_objects={}, show_asm=None ) -> Tuple[list, dict]: @@ -77,7 +152,7 @@ class Scanner37(Scanner37Base): if t.kind.startswith("BUILD_CONST_KEY_MAP") else t.kind.split("_")[1] ) - new_tokens = self.bound_collection( + new_tokens = self.bound_collection_from_tokens( tokens, new_tokens, t, i, f"CONST_{collection_type}" ) continue From 4879a60eccf0be3d63461fbfaf29c51a9e706c37 Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 25 Apr 2022 16:39:53 -0400 Subject: [PATCH 2/3] Some bugs creating token stream .. from instructions. --- uncompyle6/scanners/scanner3.py | 62 ++++++++++++++----------------- uncompyle6/semantics/n_actions.py | 4 +- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index 325f82de..cb540823 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -35,7 +35,7 @@ Finally we save token information. from __future__ import print_function -from typing import Tuple +from typing import Optional, Tuple from xdis import iscode, instruction_size, Instruction from xdis.bytecode import _get_const_info @@ -208,20 +208,8 @@ class Scanner3(Scanner): return def bound_collection_from_inst( - self, insts: list, next_tokens: list, inst: Instruction, i: int, collection_type: str - ) -> list: - t = Token( - opname=inst.opname, - attr=inst.argval, - pattr=inst.argrepr, - offset=inst.offset, - linestart=inst.starts_line, - op=inst.opcode, - has_arg=inst.has_arg, - has_extended_arg=inst.has_extended_arg, - opc=self.opc, - ) - + self, insts: list, next_tokens: list, inst: Instruction, t: Token, i: int, collection_type: str + ) -> Optional[list]: count = t.attr assert isinstance(count, int) @@ -236,7 +224,7 @@ class Scanner3(Scanner): # For small lists don't bother if count < 5: - return next_tokens + [t] + return None collection_start = i - count @@ -247,7 +235,7 @@ class Scanner3(Scanner): "LOAD_GLOBAL", "LOAD_NAME", ): - return next_tokens + [t] + return None collection_enum = CONST_COLLECTIONS.index(collection_type) @@ -389,6 +377,18 @@ class Scanner3(Scanner): opname = inst.opname + t = Token( + opname=opname, + attr=inst.argval, + pattr=inst.argrepr, + offset=inst.offset, + linestart=inst.starts_line, + op=inst.opcode, + has_arg=inst.has_arg, + has_extended_arg=inst.has_extended_arg, + opc=self.opc, + ) + # things that smash new_tokens like BUILD_LIST have to come first. if opname in ( "BUILD_CONST_KEY_MAP", @@ -400,10 +400,12 @@ class Scanner3(Scanner): if opname.startswith("BUILD_CONST_KEY_MAP") else opname.split("_")[1] ) - new_tokens = self.bound_collection_from_inst( - self.insts, new_tokens, inst, i, f"CONST_{collection_type}" + try_tokens = self.bound_collection_from_inst( + self.insts, new_tokens, inst, t, i, f"CONST_{collection_type}" ) - continue + if try_tokens is not None: + new_tokens = try_tokens + continue argval = inst.argval op = inst.opcode @@ -424,10 +426,10 @@ class Scanner3(Scanner): # "loop" tag last so the grammar rule matches that properly. for jump_offset in sorted(jump_targets[inst.offset], reverse=True): come_from_name = "COME_FROM" - opname = self.opname_for_offset(jump_offset) - if opname == "EXTENDED_ARG": + come_from_opname = self.opname_for_offset(jump_offset) + if come_from_opname == "EXTENDED_ARG": j = xdis.next_offset(op, self.opc, jump_offset) - opname = self.opname_for_offset(j) + come_from_opname = self.opname_for_offset(j) if opname.startswith("SETUP_"): come_from_type = opname[len("SETUP_") :] @@ -637,18 +639,8 @@ class Scanner3(Scanner): opname = "LOAD_ASSERT" last_op_was_break = opname == "BREAK_LOOP" - new_tokens.append( - Token( - opname=opname, - attr=argval, - pattr=pattr, - offset=inst.offset, - linestart=inst.starts_line, - op=op, - has_arg=inst.has_arg, - opc=self.opc, - ) - ) + t.kind = opname + new_tokens.append(t) pass if show_asm in ("both", "after"): diff --git a/uncompyle6/semantics/n_actions.py b/uncompyle6/semantics/n_actions.py index d108b742..b6a9b822 100644 --- a/uncompyle6/semantics/n_actions.py +++ b/uncompyle6/semantics/n_actions.py @@ -721,8 +721,8 @@ class NonterminalActions: def n_import_from(self, node): relative_path_index = 0 if self.version >= (2, 5): - if node[relative_path_index].pattr > 0: - node[2].pattr = ("." * node[relative_path_index].pattr) + node[2].pattr + if node[relative_path_index].attr > 0: + node[2].pattr = ("." * node[relative_path_index].attr) + node[2].pattr if self.version > (2, 7): if isinstance(node[1].pattr, tuple): imports = node[1].pattr From 2d6f31df9753e40bd048c8130df61693f73ef954 Mon Sep 17 00:00:00 2001 From: rocky Date: Tue, 26 Apr 2022 02:34:00 -0400 Subject: [PATCH 3/3] Use attr insead of pattrr for non-strings --- test/bytecode_3.6_run/05_long_literals.pyc | Bin 0 -> 15723 bytes uncompyle6/scanners/scanner3.py | 14 ++++++++------ uncompyle6/semantics/n_actions.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 test/bytecode_3.6_run/05_long_literals.pyc diff --git a/test/bytecode_3.6_run/05_long_literals.pyc b/test/bytecode_3.6_run/05_long_literals.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e73e3a0f4426315e51741977f063c686bec74413 GIT binary patch literal 15723 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 J_PIpTe*j|O2WbER literal 0 HcmV?d00001 diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index cb540823..047a380e 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -376,11 +376,13 @@ class Scanner3(Scanner): for i, inst in enumerate(self.insts): opname = inst.opname + argval = inst.argval + pattr = inst.argrepr t = Token( opname=opname, - attr=inst.argval, - pattr=inst.argrepr, + attr=argval, + pattr=pattr, offset=inst.offset, linestart=inst.starts_line, op=inst.opcode, @@ -431,8 +433,8 @@ class Scanner3(Scanner): j = xdis.next_offset(op, self.opc, jump_offset) come_from_opname = self.opname_for_offset(j) - if opname.startswith("SETUP_"): - come_from_type = opname[len("SETUP_") :] + if come_from_opname.startswith("SETUP_"): + come_from_type = come_from_opname[len("SETUP_") :] come_from_name = "COME_FROM_%s" % come_from_type pass elif inst.offset in self.except_targets: @@ -465,8 +467,6 @@ class Scanner3(Scanner): pass - pattr = inst.argrepr - if op in self.opc.CONST_OPS: const = argval if iscode(const): @@ -640,6 +640,8 @@ class Scanner3(Scanner): last_op_was_break = opname == "BREAK_LOOP" t.kind = opname + t.attr = argval + t.pattr = pattr new_tokens.append(t) pass diff --git a/uncompyle6/semantics/n_actions.py b/uncompyle6/semantics/n_actions.py index b6a9b822..30cb0fe0 100644 --- a/uncompyle6/semantics/n_actions.py +++ b/uncompyle6/semantics/n_actions.py @@ -227,7 +227,7 @@ class NonterminalActions: self.indent_more(INDENT_PER_LEVEL) sep = "" if is_dict: - keys = flat_elems[-1].pattr + keys = flat_elems[-1].attr assert isinstance(keys, tuple) assert len(keys) == len(flat_elems) - 1 for i, elem in enumerate(flat_elems[:-1]):