diff --git a/test/bytecode_3.6/01_call_function.pyc b/test/bytecode_3.6/01_call_function.pyc new file mode 100644 index 00000000..c18dbcbb Binary files /dev/null and b/test/bytecode_3.6/01_call_function.pyc differ diff --git a/test/bytecode_3.6/01_extended_arg.pyc b/test/bytecode_3.6/01_extended_arg.pyc-notyet similarity index 100% rename from test/bytecode_3.6/01_extended_arg.pyc rename to test/bytecode_3.6/01_extended_arg.pyc-notyet diff --git a/test/bytecode_3.6/02_build_map_unpack_with_call.pyc b/test/bytecode_3.6/02_build_map_unpack_with_call.pyc-notyet similarity index 100% rename from test/bytecode_3.6/02_build_map_unpack_with_call.pyc rename to test/bytecode_3.6/02_build_map_unpack_with_call.pyc-notyet diff --git a/test/simple_source/bug36/01_call_function.py b/test/simple_source/bug36/01_call_function.py new file mode 100644 index 00000000..13870f52 --- /dev/null +++ b/test/simple_source/bug36/01_call_function.py @@ -0,0 +1,5 @@ +# Python 3.6's changes for calling functions. +# See https://github.com/rocky/python-uncompyle6/issues/58 +# CALL_FUNCTION_EX takes 2 to 3 arguments on the stack: the function, the tuple of positional arguments, +# and optionally the dict of keyword arguments if bit 0 of oparg is 1. +a(*[]) diff --git a/uncompyle6/parsers/parse36.py b/uncompyle6/parsers/parse36.py index b1a38e05..f93a86a8 100644 --- a/uncompyle6/parsers/parse36.py +++ b/uncompyle6/parsers/parse36.py @@ -26,12 +26,16 @@ class Python36Parser(Python35Parser): withstmt ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK LOAD_CONST WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY + + call_function ::= expr expr CALL_FUNCTION_EX + call_function ::= expr expr expr CALL_FUNCTION_EX_KW """ def add_custom_rules(self, tokens, customize): super(Python36Parser, self).add_custom_rules(tokens, customize) for i, token in enumerate(tokens): opname = token.type + if opname == 'FORMAT_VALUE': rules_str = """ expr ::= fstring_single diff --git a/uncompyle6/scanners/scanner36.py b/uncompyle6/scanners/scanner36.py index b491033b..65ca0ba5 100644 --- a/uncompyle6/scanners/scanner36.py +++ b/uncompyle6/scanners/scanner36.py @@ -1,8 +1,8 @@ # Copyright (c) 2016 by Rocky Bernstein """ -Python 3.5 bytecode scanner/deparser +Python 3.6 bytecode scanner/deparser -This sets up opcodes Python's 3.5 and calls a generalized +This sets up opcodes Python's 3.6 and calls a generalized scanner routine for Python 3. """ @@ -19,6 +19,18 @@ class Scanner36(Scanner3): def __init__(self, show_asm=None): Scanner3.__init__(self, 3.6, show_asm) return + + def ingest(self, co, classname=None, code_objects={}, show_asm=None): + tokens, customize = Scanner3.ingest(self, co, classname, code_objects, show_asm) + for t in tokens: + # The lowest bit of flags indicates whether the + # var-keyword argument is placed at the top of the stack + if t.op == self.opc.CALL_FUNCTION_EX and t.attr & 1: + t.type = 'CALL_FUNCTION_EX_KW' + pass + pass + return tokens, customize + pass if __name__ == "__main__": diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index 117cba13..0e29de45 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -393,7 +393,11 @@ class SourceWalker(GenericASTTraversal, object): 'fstring_multi': ( "f'%c'", 0), 'func_args36': ( "%c(**", 0), }) - + TABLE_R.update({ + 'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)), + # Not quite right + 'CALL_FUNCTION_EX_KW': ('%c%c *%c', 0, 1, 2) + }) FSTRING_CONVERSION_MAP = {1: '!s', 2: '!r', 3: '!a'} def f_conversion(node): @@ -1698,14 +1702,20 @@ class SourceWalker(GenericASTTraversal, object): self.prec = p arg += 1 elif typ == 'C': - low, high, sep = entry[arg] - remaining = len(node[low:high]) - for subnode in node[low:high]: - self.preorder(subnode) - remaining -= 1 - if remaining > 0: - self.write(sep) - arg += 1 + try: + low, high, sep = entry[arg] + remaining = len(node[low:high]) + for subnode in node[low:high]: + self.preorder(subnode) + remaining -= 1 + if remaining > 0: + self.write(sep) + pass + pass + arg += 1 + except: + from trepan.api import debug; debug() + pass elif typ == 'D': low, high, sep = entry[arg] remaining = len(node[low:high])