# 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
        # 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)
    prop_prefix = "reachdef"


    def __init__(self, cfg, regs_only=True, inst_level=False):
        """If inst_level is True, perform instruction-level analysis, i.e.
        result will be as a set of (var, inst_addr) pairs. Otherwise, it
        will be (var, bblock_addr). inst_level=True is useful mostly for
        unittests/adhoc cases."""
        super().__init__(cfg)
        self.inst_level = inst_level
        self.regs_only = regs_only

    def init(self):
        """In and out sets of all nodes are initialized to empty sets, but
        entry's in set is initialized to a set of all defined locations with
        None address, representing non-initialized location."""
        entry = self.g.entry()
        if self.inst_level:
            all_defs = foreach_bblock(self.g, lambda b: b.def_addrs(self.regs_only), set.union)
        else:

# 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

        # 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)
    prop_prefix = "reachdef"


    def __init__(self, cfg, regs_only=True, inst_level=False):
        """If inst_level is True, perform instruction-level analysis, i.e.
        result will be as a set of (var, inst_addr) pairs. Otherwise, it
        will be (var, bblock_addr). inst_level=True is useful mostly for
        unittests/adhoc cases."""
        super().__init__(cfg)
        self.inst_level = inst_level
        self.regs_only = regs_only

    def init(self):
        """In and out sets of all nodes are initialized to empty sets, but
        entry's in set is initialized to a set of all defined locations with
        None address, representing non-initialized location."""
        entry = self.g.entry()
        if self.inst_level:
            all_defs = foreach_bblock(self.g, lambda b: b.def_addrs(self.regs_only), set.union)
        else:

            all_defs = foreach_bblock(self.g, lambda b: set((v, b.addr) for v in b.defs(self.regs_only)), set.union)