diff --git a/test/bytecode_2.4_run/05_long_literals24.pyc b/test/bytecode_2.4_run/05_long_literals24.pyc deleted file mode 100644 index 383aa905..00000000 Binary files a/test/bytecode_2.4_run/05_long_literals24.pyc and /dev/null differ diff --git a/test/bytecode_2.5_run/05_long_literals24.pyc b/test/bytecode_2.5_run/05_long_literals24.pyc new file mode 100644 index 00000000..ffae445b Binary files /dev/null and b/test/bytecode_2.5_run/05_long_literals24.pyc differ diff --git a/test/bytecode_2.7_run/07_for_if_else-continue.pyc b/test/bytecode_2.7_run/07_for_if_else-continue.pyc new file mode 100644 index 00000000..e4894c38 Binary files /dev/null and b/test/bytecode_2.7_run/07_for_if_else-continue.pyc differ diff --git a/test/simple_source/bug27+/07_for_if_else-continue.py b/test/simple_source/bug27+/07_for_if_else-continue.py new file mode 100644 index 00000000..3944232a --- /dev/null +++ b/test/simple_source/bug27+/07_for_if_else-continue.py @@ -0,0 +1,33 @@ +# Issue #413 on 2.7 +# Bug in handling CONTINUE in else block of if-then-else in a for loop + +"""This program is self-checking!""" +def test1(a, r = None): + for b in a: + if b: + r = b + else: + continue + raise AssertionError("CONTINUE not followed") + if b: + pass + return r + +def test2(a, r = None): + for b in a: + if b: + #pass # No payload + continue + raise AssertionError("CONTINUE not followed") + else: + continue + raise AssertionError("CONTINUE not followed") + if b: + r = b + raise AssertionError("CONTINUE not followed") + return r + +assert test1([True]) == True, "Incorrect flow" +assert test2([True]) is None, "Incorrect flow" +assert test1([False]) is None, "Incorrect flow" +assert test2([False]) is None, "Incorrect flow" diff --git a/test/stdlib/2.7-exclude.sh b/test/stdlib/2.7-exclude.sh index 629286a1..16a05547 100644 --- a/test/stdlib/2.7-exclude.sh +++ b/test/stdlib/2.7-exclude.sh @@ -1,34 +1,43 @@ SKIP_TESTS=( - [test_cgi.py]=1 # FIXME: Works on c90ff51 + # raise ValueError("str arguments must be keys in sys.modules") + # ValueError: str arguments must be keys in sys.modules + [test_collections.py]=1 + [test_asyncore.py]=1 + [test_bdb.py]=1 + [test_bisect.py]=1 [test_bsddb3.py]=1 # test takes too long to run: 110 seconds + [test_coercion.py]=1 # Code introspects on co_consts in a non-decompilable way [test_compile.py]=1 # Code introspects on co_consts in a non-decompilable way + [test_complex.py]=1 [test_curses.py]=1 # Possibly fails on its own but not detected [test_cmd_line.py]=1 # Takes too long, maybe hangs, or looking for interactive input? + [test_datetime.py]=1 + [test_decimal.py]=1 + [test_deque.py]=1 + [test_descr.py]=1 + [test_dictcomps.py]=1 [test_dis.py]=1 # We change line numbers - duh! [test_doctest.py]=1 # Fails on its own - [test_exceptions.py]=1 + [test_doctest2.py]=1 # Fails on its own + [test_format.py]=1 # Control flow "and" vs nested "if" + [test_float.py]=1 [test_grp.py]=1 # test takes to long, works interactively though [test_io.py]=1 # Test takes too long to run [test_ioctl.py]=1 # Test takes too long to run [test_lib2to3.py]=1 # test takes too long to run: 28 seconds - [test_math.py]=1 [test_memoryio.py]=1 # FIX - [test_modulefinder.py]=1 # FIX [test_multiprocessing.py]=1 # On uncompyle2, takes 24 secs [test_poll.py]=1 # test takes too long to run: 11 seconds [test_regrtest.py]=1 # [test_runpy.py]=1 # Long and fails on its own - [test_select.py]=1 # Runs okay but takes 11 seconds [test_socket.py]=1 # Runs ok but takes 22 seconds [test_ssl.py]=1 # [test_subprocess.py]=1 # Runs ok but takes 22 seconds [test_sys_settrace.py]=1 # Line numbers are expected to be different - [test_tokenize.py]=1 # test takes too long to run: 19 seconds [test_traceback.py]=1 # Line numbers change - duh. - [test_unicode.py]=1 # Too long to run 11 seconds [test_xpickle.py]=1 # Runs ok but takes 72 seconds [test_zipfile64.py]=1 # Runs ok but takes 204 seconds [test_zipimport.py]=1 # diff --git a/uncompyle6/parsers/parse27.py b/uncompyle6/parsers/parse27.py index b2765eef..86986d7d 100644 --- a/uncompyle6/parsers/parse27.py +++ b/uncompyle6/parsers/parse27.py @@ -142,6 +142,7 @@ class Python27Parser(Python2Parser): def p_stmt27(self, args): """ stmt ::= ifelsestmtr + stmt ::= ifelsestmtc # assert condition assert ::= assert_expr jmp_true LOAD_ASSERT RAISE_VARARGS_1 @@ -194,6 +195,7 @@ class Python27Parser(Python2Parser): ifstmt ::= testexpr return_if_stmts COME_FROM ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite come_froms ifelsestmtc ::= testexpr c_stmts_opt JUMP_ABSOLUTE else_suitec + ifelsestmtc ::= testexpr c_stmts_opt JUMP_FORWARD else_suite come_froms ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel ifelsestmtl ::= testexpr c_stmts_opt CONTINUE else_suitel @@ -242,6 +244,7 @@ class Python27Parser(Python2Parser): "except_handler": except_handler, "for_block": for_block_check.for_block_invalid, "ifelsestmt": ifelsestmt, + "ifelsestmtc": ifelsestmt, "or": or_check, "tryelsestmt": tryelsestmt, "tryelsestmtl": tryelsestmt, @@ -258,8 +261,9 @@ class Python27Parser(Python2Parser): self.check_reduce["or"] = "AST" self.check_reduce["raise_stmt1"] = "AST" - self.check_reduce["iflaststmtl"] = "AST" self.check_reduce["ifelsestmt"] = "AST" + self.check_reduce["ifelsestmtc"] = "AST" + self.check_reduce["iflaststmtl"] = "AST" self.check_reduce["list_if_not"] = "AST" self.check_reduce["list_if"] = "AST" self.check_reduce["comp_if"] = "AST" diff --git a/uncompyle6/parsers/reducecheck/ifelsestmt.py b/uncompyle6/parsers/reducecheck/ifelsestmt.py index 133df853..901b70fa 100644 --- a/uncompyle6/parsers/reducecheck/ifelsestmt.py +++ b/uncompyle6/parsers/reducecheck/ifelsestmt.py @@ -4,7 +4,7 @@ from uncompyle6.scanners.tok import Token IFELSE_STMT_RULES = frozenset( [ - ( + ( "ifelsestmt", ( "testexpr", @@ -52,6 +52,15 @@ IFELSE_STMT_RULES = frozenset( "else_suitec", ), ), + ( + "ifelsestmtc", + ( + "testexpr", + "c_stmts_opt", + "JUMP_ABSOLUTE", + "else_suitec", + ), + ), ( "ifelsestmt", ( @@ -74,7 +83,13 @@ IFELSE_STMT_RULES = frozenset( ), ( "ifelsestmt", - ("testexpr", "c_stmts", "come_froms", "else_suite", "come_froms",), + ( + "testexpr", + "c_stmts", + "come_froms", + "else_suite", + "come_froms", + ), ), ( "ifelsestmt", @@ -112,7 +127,8 @@ IFELSE_STMT_RULES = frozenset( "stmts", "jf_cfs", "\\e_else_suite_opt", - "\\e_opt_come_from_except") + "\\e_opt_come_from_except", + ), ), ( "ifelsestmt", @@ -121,9 +137,12 @@ IFELSE_STMT_RULES = frozenset( "stmts", "jf_cfs", "\\e_else_suite_opt", - "opt_come_from_except") + "opt_come_from_except", + ), ), - ]) + ] +) + def ifelsestmt(self, lhs, n, rule, tree, tokens, first, last): @@ -176,7 +195,11 @@ def ifelsestmt(self, lhs, n, rule, tree, tokens, first, last): else: last_token = tokens[last] if last_token == "COME_FROM" and first_offset > last_token.attr: - if self.version < (3, 0) and self.insts[self.offset2inst_index[last_token.attr]].opname != "SETUP_LOOP": + if ( + self.version < (3, 0) + and self.insts[self.offset2inst_index[last_token.attr]].opname + != "SETUP_LOOP" + ): return True testexpr = tree[0] @@ -198,7 +221,6 @@ def ifelsestmt(self, lhs, n, rule, tree, tokens, first, last): else: jmp_target = int(jmp[0].pattr) - # Below we check that jmp_target is jumping to a feasible # location. It should be to the transition after the "then" # block and to the beginning of the "else" block. @@ -240,6 +262,32 @@ def ifelsestmt(self, lhs, n, rule, tree, tokens, first, last): return True if first_offset > jmp_target: + # A backward or loop jump from the end of an "else" + # clause before the beginning of the "if" test is okay + # only if we are trying to match or reduce an "if" + # statement of the kind that can occur only inside a + # loop construct. + if lhs in ("ifelsestmtl", "ifelsestmtc"): + jump_false = jmp + if ( + tree[2].kind == "JUMP_FORWARD" + and jump_false == "jmp_false" + and len(else_suite) == 1 + ): + suite_stmts = else_suite[0] + continue_stmt = suite_stmts[0] + if ( + suite_stmts == "suite_stmts" + and len(suite_stmts) == 1 + and continue_stmt == "continue" + and jump_false[0].attr == continue_stmt[0].attr + ): + # for ...: + # if ...: + # ... + # else: + # continue + return False return True return (jmp_target > last_offset) and tokens[last] != "JUMP_FORWARD"