diff --git a/pylingual/control_flow_reconstruction/templates/Exception.py b/pylingual/control_flow_reconstruction/templates/Exception.py index d30ed3e..cb6e225 100644 --- a/pylingual/control_flow_reconstruction/templates/Exception.py +++ b/pylingual/control_flow_reconstruction/templates/Exception.py @@ -14,6 +14,7 @@ from ..utils import ( ending_instructions, exact_instructions, no_back_edges, + without_top_level_instructions, has_incoming_edge_of_categories, revert_on_fail, starting_instructions, @@ -37,22 +38,22 @@ class Except3_11(ControlFlowTemplate): return x -class Except3_10(ControlFlowTemplate): +class Except3_9(ControlFlowTemplate): @classmethod @override def try_match(cls, cfg, node) -> ControlFlowTemplate | None: if [x.opname for x in node.get_instructions()] == ["RERAISE"]: return node - if x := ExceptExc3_10.try_match(cfg, node): + if x := ExceptExc3_9.try_match(cfg, node): return x - if x := BareExcept3_10.try_match(cfg, node): + if x := BareExcept3_9.try_match(cfg, node): return x - if isinstance(node, Except3_10): + if isinstance(node, Except3_9): return node @register_template(0, 0, *versions_from(3, 11)) -class Try3_12(ControlFlowTemplate): +class Try3_11(ControlFlowTemplate): template = T( try_header=N("try_body"), try_body=N("tail.", None, "except_body"), @@ -84,7 +85,7 @@ class Try3_12(ControlFlowTemplate): @register_template(0, 0, *versions_from(3, 11)) -class TryElse3_12(ControlFlowTemplate): +class TryElse3_11(ControlFlowTemplate): template = T( try_header=N("try_body"), try_body=N("try_else.", None, "except_body"), @@ -119,223 +120,6 @@ class TryElse3_12(ControlFlowTemplate): """ -@register_template(0, 1, (3, 9), (3, 10)) -class Try3_10(ControlFlowTemplate): - template = T( - try_header=~N("try_body"), - try_body=N("try_footer.", None, "except_body"), - try_footer=~N("tail."), - except_body=~N("tail.").of_subtemplate(Except3_10), - tail=N.tail(), - ) - - try_match = revert_on_fail( - make_try_match( - { - EdgeKind.Fall: "tail", - }, - "try_header", - "try_body", - "except_body", - "try_footer", - ) - ) - - @to_indented_source - def to_indented_source(): - """ - {try_header} - try: - {try_body} - {except_body} - """ - - -@register_template(0, 0, (3, 9), (3, 10)) -class TryElse3_10(ControlFlowTemplate): - template = T( - try_header=N("try_body"), - try_body=N("try_footer.", None, "except_body"), - try_footer=N("else_body").with_in_deg(1), - except_body=N("tail.").with_in_deg(1).of_subtemplate(Except3_10), - else_body=~N("tail.").with_in_deg(1), - tail=N.tail(), - ) - - try_match = revert_on_fail( - make_try_match( - { - EdgeKind.Fall: "tail", - }, - "try_header", - "try_body", - "try_footer", - "except_body", - "else_body", - ) - ) - - @to_indented_source - def to_indented_source(): - """ - {try_header} - try: - {try_body} - {except_body} - else: - {else_body} - """ - - -class ExcBody3_10(ControlFlowTemplate): - @classmethod - @override - def try_match(cls, cfg, node) -> ControlFlowTemplate | None: - if x := NamedExc3_10.try_match(cfg, node): - return x - return node - - -class NamedExc3_10(ExcBody3_10): - template = T( - header=N("body", None).with_cond(with_instructions("POP_TOP", "STORE_FAST")), - body=N("normal_cleanup", None, "exception_cleanup"), - normal_cleanup=N("tail.").with_cond(with_instructions("STORE_FAST", "DELETE_FAST")), - exception_cleanup=N.tail().with_cond(with_instructions("STORE_FAST", "DELETE_FAST")), - tail=N.tail(), - ) - - try_match = make_try_match({EdgeKind.Fall: "tail"}, "exception_cleanup", "header", "body", "normal_cleanup") - - to_indented_source = defer_source_to("body") - - -class BareExcept3_10(Except3_10): - template = T( - except_body=~N("tail.", None).with_cond(with_instructions("POP_EXCEPT")).with_cond(has_incoming_edge_of_categories("exception", "false_jump")), - tail=N.tail(), - ) - - try_match = revert_on_fail( - make_try_match( - { - EdgeKind.Fall: "tail", - }, - "except_body", - ) - ) - - @to_indented_source - def to_indented_source(): - """ - except: - {except_body} - """ - - -class ExceptExc3_10(Except3_10): - template = T( - except_header=N("body", "falsejump"), - body=~N("tail.").of_subtemplate(ExcBody3_10), - falsejump=~N("tail.").of_subtemplate(Except3_10), - tail=N.tail(), - ) - - try_match = revert_on_fail( - make_try_match( - { - EdgeKind.Fall: "tail", - }, - "body", - "except_header", - "falsejump", - ) - ) - - @to_indented_source - def to_indented_source(): - """ - {except_header} - {body} - {falsejump} - """ - - -@register_template(2, 50, (3, 9), (3, 10)) -class TryFinally3_10(ControlFlowTemplate): - template = T( - try_header=N("try_body"), - try_body=N("finally_body", None, "fail_body"), - finally_body=~N("tail.").with_in_deg(1).with_cond(no_back_edges), - fail_body=N("tail.").with_cond(ending_instructions("POP_TOP", "RERAISE")), - tail=N.tail(), - ) - template2 = T( - try_except=N("finally_tail", None, "fail_body").of_type(TryElse3_10, Try3_10), - finally_tail=N("finally_body", None, "fail_body"), - finally_body=~N("tail.").with_in_deg(1).with_cond(no_back_edges), - fail_body=N("tail.").with_cond(ending_instructions("POP_TOP", "RERAISE")), - tail=N.tail(), - ) - - @staticmethod - def find_finally_cutoff(mapping): - f = mapping["finally_body"] - g = mapping["fail_body"] - if any(x.starts_line is not None for x in g.get_instructions()): - return None - if not isinstance(f, BlockTemplate): - f = BlockTemplate([f]) - if not isinstance(g, BlockTemplate): - g = BlockTemplate([g]) - if isinstance(g.members[-1], InstTemplate) and g.members[-1].inst.opname == "RERAISE": - g.members.pop() - x = None - for x, y in zip(f.members, g.members): - if all(type(a) in [IfThen, IfElse] for a in (x, y)): - continue - if type(x) is not type(y): - return None - return x and f.members.index(x) - - cutoff: int - - @classmethod - @override - def try_match(cls, cfg, node) -> ControlFlowTemplate | None: - mapping = cls.template.try_match(cfg, node) - if mapping is None: - mapping = cls.template2.try_match(cfg, node) - if mapping is None: - return None - mapping["try_header"] = mapping.pop("try_except") - - cutoff = cls.find_finally_cutoff(mapping) - if cutoff is None: - if cfg.run == 2: - cutoff = 9999 - else: - return None - - template = condense_mapping(cls, cfg, mapping, "try_header", "try_body", "finally_body", "fail_body") - template.cutoff = cutoff - return template - - def to_indented_source(self, source: SourceContext) -> list[SourceLine]: - header = source[self.try_header] - body = source[self.try_body, 1] - - if isinstance(self.finally_body, BlockTemplate): - i = self.cutoff + 1 - in_finally = source[BlockTemplate(self.finally_body.members[:i]), 1] if i > 0 else [] - after = source[BlockTemplate(self.finally_body.members[i:])] if i < len(self.finally_body.members) else [] - else: - in_finally = source[self.finally_body, 1] - after = [] - - return list(chain(header, self.line("try:"), body, self.line("finally:"), in_finally, after)) - - class BareExcept3_11(Except3_11): template = T( except_body=N("except_footer", None, "reraise"), @@ -444,7 +228,7 @@ class ExceptExc3_11(Except3_11): @register_template(0, 50) @register_template(2, 50) -class TryFinally3_12(ControlFlowTemplate): +class TryFinally3_11(ControlFlowTemplate): template = T( try_header=N("try_body"), try_body=N("finally_body", None, "fail_body"), @@ -454,7 +238,7 @@ class TryFinally3_12(ControlFlowTemplate): tail=N.tail(), ) template2 = T( - try_except=N("finally_body", None, "fail_body").of_type(Try3_12, TryElse3_12), + try_except=N("finally_body", None, "fail_body").of_type(Try3_11, TryElse3_11), finally_body=~N("tail.").with_in_deg(1).with_cond(no_back_edges), fail_body=N(E.exc("reraise")), reraise=reraise, @@ -519,3 +303,375 @@ class TryFinally3_12(ControlFlowTemplate): after = [] return list(chain(header, self.line("try:"), body, self.line("finally:"), in_finally, after)) + + +@register_template(0, 1, (3, 9), (3, 10)) +class Try3_9(ControlFlowTemplate): + template = T( + try_header=~N("try_body"), + try_body=N("try_footer.", None, "except_body"), + try_footer=~N("tail."), + except_body=~N("tail.").of_subtemplate(Except3_9), + tail=N.tail(), + ) + + try_match = revert_on_fail( + make_try_match( + { + EdgeKind.Fall: "tail", + }, + "try_header", + "try_body", + "except_body", + "try_footer", + ) + ) + + @to_indented_source + def to_indented_source(): + """ + {try_header} + try: + {try_body} + {except_body} + """ + + +@register_template(0, 0, (3, 9), (3, 10)) +class TryElse3_9(ControlFlowTemplate): + template = T( + try_header=N("try_body"), + try_body=N("try_footer.", None, "except_body"), + try_footer=N("else_body").with_in_deg(1), + except_body=N("tail.").with_in_deg(1).of_subtemplate(Except3_9), + else_body=~N("tail.").with_in_deg(1), + tail=N.tail(), + ) + + try_match = revert_on_fail( + make_try_match( + { + EdgeKind.Fall: "tail", + }, + "try_header", + "try_body", + "try_footer", + "except_body", + "else_body", + ) + ) + + @to_indented_source + def to_indented_source(): + """ + {try_header} + try: + {try_body} + {except_body} + else: + {else_body} + """ + + +class ExcBody3_9(ControlFlowTemplate): + @classmethod + @override + def try_match(cls, cfg, node) -> ControlFlowTemplate | None: + if x := NamedExc3_9.try_match(cfg, node): + return x + return node + + +class NamedExc3_9(ExcBody3_9): + template = T( + header=N("body", None).with_cond(with_instructions("POP_TOP", "STORE_FAST")), + body=N("normal_cleanup", None, "exception_cleanup"), + normal_cleanup=N("tail.").with_cond(with_instructions("STORE_FAST", "DELETE_FAST")), + exception_cleanup=N.tail().with_cond(with_instructions("STORE_FAST", "DELETE_FAST")), + tail=N.tail(), + ) + + try_match = make_try_match({EdgeKind.Fall: "tail"}, "exception_cleanup", "header", "body", "normal_cleanup") + + to_indented_source = defer_source_to("body") + + +class BareExcept3_9(Except3_9): + template = T( + except_body=~N("tail.", None).with_cond(with_instructions("POP_EXCEPT")).with_cond(has_incoming_edge_of_categories("exception", "false_jump")), + tail=N.tail(), + ) + + try_match = revert_on_fail( + make_try_match( + { + EdgeKind.Fall: "tail", + }, + "except_body", + ) + ) + + @to_indented_source + def to_indented_source(): + """ + except: + {except_body} + """ + + +class ExceptExc3_9(Except3_9): + template = T( + except_header=N("body", "falsejump"), + body=~N("tail.").of_subtemplate(ExcBody3_9), + falsejump=~N("tail.").of_subtemplate(Except3_9), + tail=N.tail(), + ) + + try_match = revert_on_fail( + make_try_match( + { + EdgeKind.Fall: "tail", + }, + "body", + "except_header", + "falsejump", + ) + ) + + @to_indented_source + def to_indented_source(): + """ + {except_header} + {body} + {falsejump} + """ + + +@register_template(2, 50, (3, 9), (3, 10)) +class TryFinally3_9(ControlFlowTemplate): + template = T( + try_header=N("try_body"), + try_body=N("finally_body", None, "fail_body"), + finally_body=~N("tail.").with_in_deg(1).with_cond(no_back_edges), + fail_body=N("tail.").with_cond(ending_instructions("POP_TOP", "RERAISE")), + tail=N.tail(), + ) + template2 = T( + try_except=N("finally_tail", None, "fail_body").of_type(TryElse3_9, Try3_9), + finally_tail=N("finally_body", None, "fail_body"), + finally_body=~N("tail.").with_in_deg(1).with_cond(no_back_edges), + fail_body=N("tail.").with_cond(ending_instructions("POP_TOP", "RERAISE")), + tail=N.tail(), + ) + + @staticmethod + def find_finally_cutoff(mapping): + f = mapping["finally_body"] + g = mapping["fail_body"] + if any(x.starts_line is not None for x in g.get_instructions()): + return None + if not isinstance(f, BlockTemplate): + f = BlockTemplate([f]) + if not isinstance(g, BlockTemplate): + g = BlockTemplate([g]) + if isinstance(g.members[-1], InstTemplate) and g.members[-1].inst.opname == "RERAISE": + g.members.pop() + x = None + for x, y in zip(f.members, g.members): + if all(type(a) in [IfThen, IfElse] for a in (x, y)): + continue + if type(x) is not type(y): + return None + return x and f.members.index(x) + + cutoff: int + + @classmethod + @override + def try_match(cls, cfg, node) -> ControlFlowTemplate | None: + mapping = cls.template.try_match(cfg, node) + if mapping is None: + mapping = cls.template2.try_match(cfg, node) + if mapping is None: + return None + mapping["try_header"] = mapping.pop("try_except") + + cutoff = cls.find_finally_cutoff(mapping) + if cutoff is None: + if cfg.run == 2: + cutoff = 9999 + else: + return None + + template = condense_mapping(cls, cfg, mapping, "try_header", "try_body", "finally_body", "fail_body") + template.cutoff = cutoff + return template + + def to_indented_source(self, source: SourceContext) -> list[SourceLine]: + header = source[self.try_header] + body = source[self.try_body, 1] + + if isinstance(self.finally_body, BlockTemplate): + i = self.cutoff + 1 + in_finally = source[BlockTemplate(self.finally_body.members[:i]), 1] if i > 0 else [] + after = source[BlockTemplate(self.finally_body.members[i:])] if i < len(self.finally_body.members) else [] + else: + in_finally = source[self.finally_body, 1] + after = [] + + return list(chain(header, self.line("try:"), body, self.line("finally:"), in_finally, after)) + + +class Except3_6(ControlFlowTemplate): + @classmethod + @override + def try_match(cls, cfg, node) -> ControlFlowTemplate | None: + if [x.opname for x in node.get_instructions()] == ["END_FINALLY"]: + return node + if x := ExceptExc3_6.try_match(cfg, node): + return x + if x := BareExcept3_6.try_match(cfg, node): + return x + return None + + +@register_template(0, 0, (3, 6), (3, 7), (3, 8)) +class Try3_6(ControlFlowTemplate): + template = T( + try_header=N("try_body").with_cond(without_top_level_instructions("SETUP_WITH")), + try_body=N("try_footer", None, "except_body"), + try_footer=N("tail."), + except_body=N("tail.").with_in_deg(1).of_subtemplate(Except3_6), + tail=N.tail(), + ) + + try_match = revert_on_fail( + make_try_match( + { + EdgeKind.Fall: "tail", + }, + "try_header", + "try_body", + "try_footer", + "except_body", + ) + ) + + @to_indented_source + def to_indented_source(): + """ + {try_header} + try: + {try_body} + {except_body} + """ + + +class ExcBody3_6(ControlFlowTemplate): + @classmethod + @override + def try_match(cls, cfg, node) -> ControlFlowTemplate | None: + if x := NamedExc3_6.try_match(cfg, node): + return x + return node + + +class NamedExc3_6(ExcBody3_6): + template = T( + header=N("body", None).with_cond(starting_instructions("POP_TOP", "STORE_FAST")), + body=N("normal_cleanup", None, "exception_cleanup"), + normal_cleanup=N("exception_cleanup."), + exception_cleanup=N.tail().with_cond(with_instructions("LOAD_CONST", "STORE_FAST")), + ) + + try_match = make_try_match({EdgeKind.Fall: "tail"}, "exception_cleanup", "header", "body", "normal_cleanup") + + to_indented_source = defer_source_to("body") + + +class ExceptExc3_6(Except3_6): + template = T( + except_header=N("except_body", "no_match").with_cond( + ending_instructions("COMPARE_OP", "POP_JUMP_IF_FALSE"), + ending_instructions("COMPARE_OP", "POP_JUMP_FORWARD_IF_FALSE") + ), + except_body=N("tail.", None).of_subtemplate(ExcBody3_6).with_in_deg(1), + no_match=N("tail?", None).of_subtemplate(Except3_6), + tail=N.tail(), + ) + + try_match = revert_on_fail( + make_try_match( + { + EdgeKind.Fall: "tail", + }, + "except_header", + "except_body", + "no_match", + ) + ) + + @to_indented_source + def to_indented_source(): + """ + {except_header} + {except_body} + {no_match} + """ + + +@register_template(0, 0, (3, 6), (3, 7), (3, 8)) +class TryElse3_6(ControlFlowTemplate): + template = T( + try_header=N("try_body"), + try_body=N("try_footer.", None, "except_body"), + try_footer=N("else_body").with_in_deg(1), + except_body=N("tail").with_in_deg(1).of_subtemplate(Except3_6), + else_body=~N("tail").with_in_deg(1), + tail=N.tail(), + ) + + try_match = revert_on_fail( + make_try_match( + { + EdgeKind.Fall: "tail", + }, + "try_header", + "try_body", + "try_footer", + "except_body", + "else_body", + ) + ) + + @to_indented_source + def to_indented_source(): + """ + {try_header} + try: + {try_body} + {except_body} + else: + {else_body} + """ + + +class BareExcept3_6(Except3_6): + template = T( + except_body=N("tail."), + tail=N.tail(), + ) + + try_match = make_try_match( + { + EdgeKind.Fall: "tail", + }, + "except_body", + ) + + @to_indented_source + def to_indented_source(): + """ + except: + {except_body} + """ \ No newline at end of file diff --git a/pylingual/control_flow_reconstruction/templates/With.py b/pylingual/control_flow_reconstruction/templates/With.py index 1d7c9f2..59ab79d 100644 --- a/pylingual/control_flow_reconstruction/templates/With.py +++ b/pylingual/control_flow_reconstruction/templates/With.py @@ -1,5 +1,5 @@ from ..cft import ControlFlowTemplate, EdgeKind, register_template -from ..utils import T, N, exact_instructions, starting_instructions, to_indented_source, make_try_match, versions_from +from ..utils import T, N, exact_instructions, starting_instructions, without_instructions, to_indented_source, make_try_match, versions_from class WithCleanup3_11(ControlFlowTemplate): @@ -82,7 +82,7 @@ class With3_9(ControlFlowTemplate): @register_template(0, 10, (3, 6), (3, 7), (3, 8)) class With3_6(ControlFlowTemplate): template = T( - setup_with=~N("with_body", None), + setup_with=~N("with_body", None).with_cond(without_instructions("SETUP_FINALLY")), with_body=N("buffer_block.", None, "normal_cleanup").with_in_deg(1), buffer_block=~N("normal_cleanup.", None).with_in_deg(1), normal_cleanup=~N.tail(),