From 8d90e3383259c8a22de52fc8828a184424e62936 Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 31 Dec 2015 10:56:12 -0500 Subject: [PATCH] Handle Python 3.3 > dotted class names --- test/bytecode_3.4/05_class.pyc | Bin 0 -> 311 bytes test/simple_source/def/05_class.py | 13 +++++++++++ uncompyle6/parsers/parse3.py | 35 ++++++++++++++++++----------- uncompyle6/semantics/fragments.py | 14 +++++++----- uncompyle6/semantics/pysource.py | 15 ++++++++----- 5 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 test/bytecode_3.4/05_class.pyc create mode 100644 test/simple_source/def/05_class.py diff --git a/test/bytecode_3.4/05_class.pyc b/test/bytecode_3.4/05_class.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7788e50c5e9e498b445da5bd93150bc97a59a32f GIT binary patch literal 311 zcmaFI!^?Hvzcs9$k%8ec0}@~XvK@f9SQAL301+cY4g*6J1A}`C6GI9!Lkm!dnIV;d zA&ZfrnVBI9D9FeVtjY2csGPws8AQSW&}fKJ0zd{(M-(H-K$s4uTkK9zMsAroseYQw zw>aYC^AdAYMQlKWz{D@v;>_Fvpqk?R(xT*4 z{gl)+eFM|@= 3.3: - buildclass ::= LOAD_BUILD_CLASS mkfunc LOAD_CONST LOAD_CLASSNAME CALL_FUNCTION_3 + buildclass ::= LOAD_BUILD_CLASS mkfunc + LOAD_CLASSNAME expr CALL_FUNCTION_3 + or + buildclass ::= LOAD_BUILD_CLASS mkfunc + LOAD_CONST CALL_FUNCTION_3 Python < 3.3 buildclass ::= LOAD_BUILD_CLASS LOAD_CONST MAKE_FUNCTION_0 LOAD_CONST CALL_FUNCTION_n @@ -693,28 +697,29 @@ class Python3Parser(PythonParser): break pass assert i < len(tokens) - if self.version >= 3.3: - assert tokens[i+1].type == 'LOAD_CONST' - load_check = 'LOAD_NAME' - else: - load_check = 'LOAD_CONST' + assert tokens[i+1].type == 'LOAD_CONST' # find load names have_loadname = False for i in range(i+1, len(tokens)): - if tokens[i].type == load_check: - tokens[i].type = 'LOAD_CLASSNAME' + if tokens[i].type == 'LOAD_NAME': + if tokens[i+1].type != 'LOAD_ATTR': + tokens[i].type = 'LOAD_CLASSNAME' have_loadname = True break if tokens[i].type in 'CALL_FUNCTION': break pass assert i < len(tokens) + have_load_attr = False if have_loadname: j = 1 for i in range(i+1, len(tokens)): - if tokens[i].type in 'CALL_FUNCTION': + if tokens[i].type in ['CALL_FUNCTION', 'LOAD_ATTR']: + if tokens[i].type == 'LOAD_ATTR': + have_load_attr = True break - assert tokens[i].type == 'LOAD_NAME' + assert tokens[i].type == 'LOAD_NAME', \ + 'Expecting LOAD_NAME after CALL_FUNCTION' tokens[i].type = 'LOAD_CLASSNAME' j += 1 pass @@ -724,9 +729,13 @@ class Python3Parser(PythonParser): load_names = '' # customize CALL_FUNCTION if self.version >= 3.3: - call_function = 'CALL_FUNCTION_%d' % (j + 2) - rule = ("buildclass ::= LOAD_BUILD_CLASS mkfunc LOAD_CONST %s%s" % - (load_names, call_function)) + if not have_load_attr: + call_function = 'CALL_FUNCTION_%d' % (j + 2) + rule = ("buildclass ::= LOAD_BUILD_CLASS mkfunc LOAD_CONST " + "%s%s" % (load_names, call_function)) + else: + rule = ("buildclass ::= LOAD_BUILD_CLASS mkfunc LOAD_CONST expr " + "CALL_FUNCTION_3") else: call_function = 'CALL_FUNCTION_%d' % (j + 1) rule = ("buildclass ::= LOAD_BUILD_CLASS mkfunc %s%s" % diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index de7595a5..b06c2179 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -588,21 +588,23 @@ class FragmentsWalker(pysource.SourceWalker, object): cclass = self.currentclass if self.version > 3.0: - buildclass = node[1] - build_list = node[0] - subclass = build_list[1][0].attr + currentclass = node[1][0].pattr + buildclass = node[0] + subclass = buildclass[1][0].attr + subclass_info = node[0] else: buildclass = node[0] build_list = buildclass[1][0] subclass = buildclass[-3][0].attr + currentclass = buildclass[0].pattr self.write('\n\n') - self.currentclass = str(buildclass[0].pattr) + self.currentclass = str(currentclass) start = len(self.f.getvalue()) self.write(self.indent, 'class ', self.currentclass) if self.version > 3.0: - self.print_super_classes3(build_list) + self.print_super_classes3(subclass_info) else: self.print_super_classes(build_list) self.print_(':') @@ -888,7 +890,7 @@ class FragmentsWalker(pysource.SourceWalker, object): n = len(node)-1 assert node[n].type.startswith('CALL_FUNCTION') for i in range(n-1, 0, -1): - if node[i].type != 'LOAD_CLASSNAME': + if not node[i].type in ['expr', 'LOAD_CLASSNAME']: break pass diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index f81c31f4..3c624c29 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -1072,20 +1072,22 @@ class SourceWalker(GenericASTTraversal, object): cclass = self.currentclass if self.version > 3.0: - buildclass = node[1] - build_list = node[0] - subclass = build_list[1][0].attr + currentclass = node[1][0].pattr + buildclass = node[0] + subclass = buildclass[1][0].attr + subclass_info = node[0] else: buildclass = node[0] build_list = buildclass[1][0] subclass = buildclass[-3][0].attr + currentclass = buildclass[0].pattr self.write('\n\n') - self.currentclass = str(buildclass[0].pattr) + self.currentclass = str(currentclass) self.write(self.indent, 'class ', self.currentclass) if self.version > 3.0: - self.print_super_classes3(build_list) + self.print_super_classes3(subclass_info) else: self.print_super_classes(build_list) self.print_(':') @@ -1125,8 +1127,9 @@ class SourceWalker(GenericASTTraversal, object): # as a custom rule n = len(node)-1 assert node[n].type.startswith('CALL_FUNCTION') + for i in range(n-1, 0, -1): - if node[i].type != 'LOAD_CLASSNAME': + if not node[i].type in ['expr', 'LOAD_CLASSNAME']: break pass