# Path: core.py
# def is_expr(e):
#     return isinstance(e, EXPR)
# 
# def is_mem(e):
#     return isinstance(e, MEM)
# 
# Path: utils.py
# def set_union(*sets):
#     # Python's set.union is unfortunately not a class method,
#     # but a normal method, [and if used as unbound method],
#     # requires at least one argument (set).
#     if sets:
#         return set.union(*sets)
#     return set()
# 
# def set_intersection(*sets):
#     # Python's set.intersection is unfortunately not a class method,
#     # but a normal method, [and if used as unbound method],
#     # requires at least one argument (set).
#     if sets:
#         return set.intersection(*sets)
#     return set()
# 
# Path: cfgutils.py
# def foreach_bblock(cfg, func, join_func=lambda a, b: a or b, **kwargs):
#     """Apply basic-block level transformation to each block in CFG.
#     Return cumulative status (OR of each block's status).
#     """
#     res = Ellipsis
#     for addr, info in cfg.iter_sorted_nodes():
#         bblock = info["val"]
#         r = func(bblock, **kwargs)
#         if res is Ellipsis:
#             res = r
#         else:
#             res = join_func(res, r)
#     return res


import logging
import core
                    import progdb
from core import is_expr, is_mem
from utils import set_union, set_intersection
from cfgutils import foreach_bblock
                info[self.node_prop_in] = all_nodes

    def join(self, node, source_nodes):
        state = set_intersection(*(self.g.get_node_attr(x, self.node_prop_in) for x in source_nodes))
        return state | {node}


class GenKillAnalysis(AnalysisBase):

    # Should be staticmethod(set_union) or staticmethod(set_intersection)
    # staticmethod() is required to work around Python's magic handling
    # of functions references within classes.
    join_op = None

    def transfer(self, node, src_state):
        return (src_state - self.g.get_node_attr(node, self.node_prop_kill)) | self.g.get_node_attr(node, self.node_prop_gen)

    def join(self, node, source_nodes):
        if node == "_DEADEND_":
            log.debug("%s: joining from %s to _DEADEND_" % (self.__class__.__name__, source_nodes))
            return set()
        # node_prop_dst is named from the point of view of intra-node transfer function.
        # inter-node join function takes source nodes dst set to compute current node
        # src set
        return self.join_op(*(self.g.get_node_attr(x, self.node_prop_dst) for x in source_nodes))


class ReachDefAnalysis(GenKillAnalysis):
    "Encapsulation of dataflow analysis for reaching definitions."
    forward = True

# Path: core.py
# def is_expr(e):
#     return isinstance(e, EXPR)
# 
# def is_mem(e):
#     return isinstance(e, MEM)
# 
# Path: utils.py
# def set_union(*sets):
#     # Python's set.union is unfortunately not a class method,
#     # but a normal method, [and if used as unbound method],
#     # requires at least one argument (set).
#     if sets:
#         return set.union(*sets)
#     return set()
# 
# def set_intersection(*sets):
#     # Python's set.intersection is unfortunately not a class method,
#     # but a normal method, [and if used as unbound method],
#     # requires at least one argument (set).
#     if sets:
#         return set.intersection(*sets)
#     return set()
# 
# Path: cfgutils.py
# def foreach_bblock(cfg, func, join_func=lambda a, b: a or b, **kwargs):
#     """Apply basic-block level transformation to each block in CFG.
#     Return cumulative status (OR of each block's status).
#     """
#     res = Ellipsis
#     for addr, info in cfg.iter_sorted_nodes():
#         bblock = info["val"]
#         r = func(bblock, **kwargs)
#         if res is Ellipsis:
#             res = r
#         else:
#             res = join_func(res, r)
#     return res


# Path: dataflow.py
import logging
import core
                    import progdb
from core import is_expr, is_mem
from utils import set_union, set_intersection
from cfgutils import foreach_bblock

                info[self.node_prop_in] = all_nodes

    def join(self, node, source_nodes):
        state = set_intersection(*(self.g.get_node_attr(x, self.node_prop_in) for x in source_nodes))
        return state | {node}


class GenKillAnalysis(AnalysisBase):

    # Should be staticmethod(set_union) or staticmethod(set_intersection)
    # staticmethod() is required to work around Python's magic handling
    # of functions references within classes.
    join_op = None

    def transfer(self, node, src_state):
        return (src_state - self.g.get_node_attr(node, self.node_prop_kill)) | self.g.get_node_attr(node, self.node_prop_gen)

    def join(self, node, source_nodes):
        if node == "_DEADEND_":
            log.debug("%s: joining from %s to _DEADEND_" % (self.__class__.__name__, source_nodes))
            return set()
        # node_prop_dst is named from the point of view of intra-node transfer function.
        # inter-node join function takes source nodes dst set to compute current node
        # src set
        return self.join_op(*(self.g.get_node_attr(x, self.node_prop_dst) for x in source_nodes))


class ReachDefAnalysis(GenKillAnalysis):
    "Encapsulation of dataflow analysis for reaching definitions."
    forward = True

    join_op = staticmethod(set_union)