diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index f6d4a5d9..fcb91d59 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -201,6 +201,8 @@ class Python3Parser(PythonParser): del_stmt ::= expr DELETE_ATTR kwarg ::= LOAD_CONST expr + kwargs ::= kwargs kwarg + kwargs ::= classdef ::= build_class designator # Python3 introduced LOAD_BUILD_CLASS @@ -507,21 +509,17 @@ class Python3Parser(PythonParser): elif opname_base == 'UNPACK_LIST': rule = 'unpack_list ::= ' + opname + ' designator' * token.attr elif opname_base.startswith('MAKE_FUNCTION'): - self.addRule('mklambda ::= %s LOAD_LAMBDA %s' % - ('expr ' * token.attr, opname), nop_func) - rule = 'mkfunc ::= %sLOAD_CONST %s' % ('expr ' * token.attr, opname) - self.add_unique_rule(rule, opname, token.attr, customize) - if opname.startswith('MAKE_FUNCTION_N'): - args_pos = token.attr & 0xff - args_kw = (token.attr >> 8) & 0xff - rule = ('mkfunc ::= %s %s %s %s' % + args_pos, args_kw, annotate_args = token.attr + self.addRule('mklambda ::= %sLOAD_LAMBDA %s' % + ('pos_arg ' * args_pos, opname), nop_func) + if self.version > 3.2: + rule = ('mkfunc ::= %skwargs %s %s' % ('pos_arg ' * args_pos, - 'expr ' * args_kw, - 'LOAD_CONST ' * 3, + 'LOAD_CONST ' * 2, opname)) else: - rule = 'mkfunc ::= %sLOAD_CONST LOAD_CONST %s' % ('pos_arg ' * token.attr, opname) - pass + rule = ('mkfunc ::= %sLOAD_CONST %s' % + ('pos_arg ' * args_pos, opname)) self.add_unique_rule(rule, opname, token.attr, customize) elif opname.startswith('MAKE_CLOSURE'): self.add_unique_rule('mklambda ::= %sload_closure LOAD_LAMBDA %s' % diff --git a/uncompyle6/scanners/dis35.py b/uncompyle6/scanners/dis35.py index bb4d3c7f..30ba5a35 100644 --- a/uncompyle6/scanners/dis35.py +++ b/uncompyle6/scanners/dis35.py @@ -306,7 +306,8 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None, elif op in hasfree: argval, argrepr = _get_name_info(arg, cells) elif op in hasnargs: - argrepr = "%d positional, %d keyword pair" % (code[i-2], code[i-1]) + argrepr = ("%d positional, %d keyword pair, %d annotated" % + (code[i-2], code[i-1], code[i])) yield Instruction(opname[op_num], op, arg, argval, argrepr, offset, starts_line, is_jump_target) diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index d21e56e8..042c21b5 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -167,7 +167,29 @@ class Scanner3(scan.Scanner): elif op in op3.hasfree: pattr = free[oparg] - if op_name in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET', 'BUILD_SLICE', + if op_name == 'MAKE_FUNCTION': + argc = oparg + attr = ((argc & 0xFF), (argc >> 8) & 0xFF, (argc >> 16) & 0x7FFF) + pos_args, name_pair_args, annotate_args = attr + if name_pair_args > 0: + op_name = 'MAKE_FUNCTION_N%d' % name_pair_args + pass + if annotate_args > 0: + op_name = '%s_A_%d' % [op_name, annotate_args] + pass + op_name = '%s_%d' % (op_name, pos_args) + pattr = ("%d positional, %d keyword pair, %d annotated" % + (pos_args, name_pair_args, annotate_args)) + tokens.append( + Token( + type_ = op_name, + attr = (pos_args, name_pair_args, annotate_args), + pattr = pattr, + offset = offset, + linestart = linestart) + ) + continue + elif op_name in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET', 'BUILD_SLICE', 'UNPACK_SEQUENCE', 'MAKE_FUNCTION', 'CALL_FUNCTION', 'MAKE_CLOSURE', 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW', diff --git a/uncompyle6/scanners/scanner35.py b/uncompyle6/scanners/scanner35.py index 469c31cb..e0b5c9d3 100644 --- a/uncompyle6/scanners/scanner35.py +++ b/uncompyle6/scanners/scanner35.py @@ -30,6 +30,8 @@ class Scanner35(scan3.Scanner3): # we do post-processing like we do here. def disassemble(self, co, classname=None, code_objects={}): + + dis.disassemble(co) # DEBUG # Container for tokens tokens = [] customize = {} @@ -104,28 +106,37 @@ class Scanner35(scan3.Scanner3): else: pattr = const pass + elif opname == 'MAKE_FUNCTION': + argc = inst.argval + attr = ((argc & 0xFF), (argc >> 8) & 0xFF, (argc >> 16) & 0x7FFF) + pos_args, name_pair_args, annotate_args = attr + if name_pair_args > 0: + opname = 'MAKE_FUNCTION_N%d' % name_pair_args + pass + if annotate_args > 0: + opname = '%s_A_%d' % [op_name, annotate_args] + pass + opname = '%s_%d' % (opname, pos_args) + pattr = ("%d positional, %d keyword pair, %d annotated" % + (pos_args, name_pair_args, annotate_args)) + tokens.append( + Token( + type_ = opname, + attr = (pos_args, name_pair_args, annotate_args), + pattr = pattr, + offset = inst.offset, + linestart = inst.starts_line) + ) + continue elif opname in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET', 'BUILD_SLICE', - 'BUILD_MAP', - 'UNPACK_SEQUENCE', - 'MAKE_FUNCTION', 'MAKE_CLOSURE', - 'DUP_TOPX', 'RAISE_VARARGS' + 'BUILD_MAP', 'UNPACK_SEQUENCE', 'MAKE_CLOSURE', + 'RAISE_VARARGS' ): pos_args = inst.argval - if inst.opname == 'MAKE_FUNCTION': - argc = inst.argval - pos_args = (argc & 0xFF) - name_pair_args = (argc >> 8) & 0xFF - if name_pair_args > 0: - opname = 'MAKE_FUNCTION_N%d' % name_pair_args - pass - annotate_args = (argc >> 16) & 0x7FFF - if annotate_args > 0: - opname = '%s_A_%d' % [op_name, annotate_args] - pass - elif inst.opname != 'BUILD_SLICE': + if inst.opname != 'BUILD_SLICE': customize[opname] = pos_args + pass opname = '%s_%d' % (opname, pos_args) - elif opname == 'JUMP_ABSOLUTE': pattr = inst.argval target = self.get_target(inst.offset) diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index 479e5635..5a0c403e 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -261,6 +261,7 @@ TABLE_DIRECT = { 'classdefdeco': ( '%c', 0), 'classdefdeco1': ( '\n\n%|@%c%c', 0, 1), 'kwarg': ( '%[0]{pattr}=%c', 1), + 'kwargs': ( '%C', (0, maxint, ', ') ), 'importlist2': ( '%C', (0, maxint, ', ') ), 'assert': ( '%|assert %c\n' , 0 ), @@ -951,7 +952,11 @@ class SourceWalker(GenericASTTraversal, object): self.write(func_name) self.indentMore() - self.make_function(node, isLambda=False, code_index=code_index) + if self.version > 3.2: + self.make_function33(node, isLambda=False, code_index=code_index) + else: + self.make_function2(node, isLambda=False, code_index=code_index) + if len(self.param_stack) > 1: self.write('\n\n') else: @@ -1521,7 +1526,7 @@ class SourceWalker(GenericASTTraversal, object): # return self.traverse(node[1]) raise Exception("Can't find tuple parameter " + name) - def make_function(self, node, isLambda, nested=1, code_index=-2): + def make_function2(self, node, isLambda, nested=1, code_index=-2): """Dump function defintion, doc string, and function body.""" def build_param(ast, name, default): @@ -1539,7 +1544,6 @@ class SourceWalker(GenericASTTraversal, object): if default: if self.showast: - print() print('--', name) print(default) print('--') @@ -1549,9 +1553,13 @@ class SourceWalker(GenericASTTraversal, object): return result else: return name - # node[-1] == MAKE_xxx_n - defparams = node[:node[-1].attr] + # node[-1] == MAKE_FUNCTION_n + + if isinstance(node[-1].attr, tuple): + defparams = node[:node[-1].attr[0]] + else: + defparams = node[:node[-1].attr] code = node[code_index].attr assert iscode(code) @@ -1575,6 +1583,7 @@ class SourceWalker(GenericASTTraversal, object): return # build parameters + params = [build_param(ast, name, default) for name, default in zip_longest(paramnames, defparams, fillvalue=None)] # params = [ build_param(ast, name, default) for @@ -1601,6 +1610,76 @@ class SourceWalker(GenericASTTraversal, object): self.print_("(", ", ".join(params), "):") # self.print_(indent, '#flags:\t', int(code.co_flags)) + self.make_function_tail(indent, code, ast, isLambda) + + def make_function33(self, node, isLambda, nested=1, code_index=-2): + """Dump function defintion, doc string, and function body.""" + + def build_param(ast, name, default): + """build parameters: + - handle defaults + - handle format tuple parameters + """ + if default: + if self.showast: + print() + print('--', name) + print(default) + print('--') + result = '%s = %s' % (name, self.traverse(default, indent='') ) + if result[-2:] == '= ': # default was 'LOAD_CONST None' + result += 'None' + return result + else: + return name + + # node[-1] == MAKE_FUNCTION_xxx + + args_node = node[-1] + if isinstance(args_node.attr, tuple): + pos_args, kw_args, annotate_args = args_node.attr + else: + pos_args = args_node.attr + + code = node[code_index].attr + + assert iscode(code) + code = Code(code, self.scanner, self.currentclass) + + try: + ast = self.build_ast(code._tokens, + code._customize, + isLambda = isLambda, + noneInNames = ('None' in code.co_names)) + except ParserError as p: + self.write( str(p)) + self.ERROR = p + return + + # build positional parameters + argc = code.co_argcount + params = list(code.co_varnames[:argc]) + + params.reverse() # back to correct order + + if 4 & code.co_flags: # flag 2 -> variable number of args + params.append('*%s' % code.co_varnames[argc]) + argc += 1 + if 8 & code.co_flags: # flag 3 -> keyword args + params.append('**%s' % code.co_varnames[argc]) + argc += 1 + + # dump parameter list (with default values) + indent = self.indent + if isLambda: + self.write("lambda ", ", ".join(params), ": ") + else: + self.print_("(", ", ".join(params), "):") + # self.print_(indent, '#flags:\t', int(code.co_flags)) + + self.make_function_tail(indent, code, ast, isLambda) + + def make_function_tail(self, indent, code, ast, isLambda): if len(code.co_consts)>0 and code.co_consts[0] is not None and not isLambda: # ugly # docstring exists, dump it self.print_docstring(indent, code.co_consts[0])