(self, others: Tuple['VariableRecoveryFastState'],
              successor=None)
return state, merge_occurred
Merge two abstract states.

        For any node A whose dominance frontier that the current node (at the current program location) belongs to, we
        create a phi variable V' for each variable V that is defined in A, and then replace all existence of V with V'
        in the merged abstract state.

        :param others: Other abstract states to merge.
        :return:       The merged abstract state.
Merge two abstract states.
Merge two abstract states .
def merge(self, others: Tuple['VariableRecoveryFastState'],
              successor=None) -> Tuple['VariableRecoveryFastState',bool]:
        """
        Merge two abstract states.

        For any node A whose dominance frontier that the current node (at the current program location) belongs to, we
        create a phi variable V' for each variable V that is defined in A, and then replace all existence of V with V'
        in the merged abstract state.

        :param others: Other abstract states to merge.
        :return:       The merged abstract state.
        """

        self.phi_variables = {}  # A mapping from original variable and its corresponding phi variable
        self.successor_block_addr = successor

        merged_stack_region = self.stack_region.copy()
        merged_stack_region.set_state(self)
        merge_occurred = merged_stack_region.merge([other.stack_region for other in others], None)

        merged_register_region = self.register_region.copy()
        merged_register_region.set_state(self)
        merge_occurred |= merged_register_region.merge([other.register_region for other in others], None)

        merged_global_region = self.global_region.copy()
        merged_global_region.set_state(self)
        merge_occurred |= merged_global_region.merge([other.global_region for other in others], None)

        merged_typevars = self.typevars
        merged_typeconstraints = self.type_constraints.copy()
        delayed_typeconstraints = self.delayed_type_constraints.copy().clean()
        for other in others:
            merged_typevars = merged_typevars.merge(other.typevars)
            merged_typeconstraints |= other.type_constraints
            for v, cons in other.delayed_type_constraints.items():
                delayed_typeconstraints[v] |= cons

        merge_occurred |= self.typevars != merged_typevars
        merge_occurred |= self.type_constraints != merged_typeconstraints
        merge_occurred |= self.delayed_type_constraints != delayed_typeconstraints

        # add subtype constraints for all replacements
        for v0, v1 in self.phi_variables.items():
            # v0 will be replaced by v1
            if not merged_typevars.has_type_variable_for(v1, None):
                merged_typevars.add_type_variable(v1, None, TypeVariable())
            if not merged_typevars.has_type_variable_for(v0, None):
                merged_typevars.add_type_variable(v0, None, TypeVariable())
            # Assuming v2 = phi(v0, v1), then we know that v0_typevar == v1_typevar == v2_typevar
            # However, it's possible that neither v0 nor v1 will ever be used in future blocks, which not only makes
            # this phi function useless, but also leads to the incorrect assumption that v1_typevar == v2_typevar.
            # Hence, we delay the addition of the equivalence relationship into the type constraints. It is only added
            # when v1 (the new variable that will end up in the state) is ever used in the future.

            # create an equivalence relationship
            equivalence = Equivalence(merged_typevars.get_type_variable(v1, None),
                                      merged_typevars.get_type_variable(v0, None)
                                      )
            delayed_typeconstraints[v1].add(equivalence)

        stack_offset_typevars = {}
        all_stack_addr_typevar_offsets = set(self.stack_offset_typevars)
        for other in others:
            all_stack_addr_typevar_offsets.update(other.stack_offset_typevars)
        for offset in all_stack_addr_typevar_offsets:
            all_typevars = set()
            for state in (self,) + others:
                typevar = state.stack_offset_typevars.get(offset, None)
                if typevar is not None:
                    all_typevars.add(typevar)

            if len(all_typevars) == 1:
                typevar = all_typevars.pop()
            else:
                typevar = TypeVariable()
                for orig_typevar in all_typevars:
                    merged_typeconstraints.add(Subtype(orig_typevar, typevar))
            stack_offset_typevars[offset] = typevar

        # clean up
        self.phi_variables = {}
        self.successor_block_addr = None

        state = VariableRecoveryFastState(
            successor,
            self._analysis,
            self.arch,
            self.function,
            stack_region=merged_stack_region,
            register_region=merged_register_region,
            global_region=merged_global_region,
            typevars=merged_typevars,
            type_constraints=merged_typeconstraints,
            delayed_type_constraints=delayed_typeconstraints,
            stack_offset_typevars=stack_offset_typevars,
            project=self.project,
        )

        return state, merge_occurred
def merge ( self , others : Tuple [ 'VariableRecoveryFastState' ] , successor = None ) -> Tuple [ 'VariableRecoveryFastState' , bool ] : self . phi_variables = { } # A mapping from original variable and its corresponding phi variable self . successor_block_addr = successor merged_stack_region = self . stack_region . copy ( ) merged_stack_region . set_state ( self ) merge_occurred = merged_stack_region . merge ( [ other . stack_region for other in others ] , None ) merged_register_region = self . register_region . copy ( ) merged_register_region . set_state ( self ) merge_occurred |= merged_register_region . merge ( [ other . register_region for other in others ] , None ) merged_global_region = self . global_region . copy ( ) merged_global_region . set_state ( self ) merge_occurred |= merged_global_region . merge ( [ other . global_region for other in others ] , None ) merged_typevars = self . typevars merged_typeconstraints = self . type_constraints . copy ( ) delayed_typeconstraints = self . delayed_type_constraints . copy ( ) . clean ( ) for other in others : merged_typevars = merged_typevars . merge ( other . typevars ) merged_typeconstraints |= other . type_constraints for v , cons in other . delayed_type_constraints . items ( ) : delayed_typeconstraints [ v ] |= cons merge_occurred |= self . typevars != merged_typevars merge_occurred |= self . type_constraints != merged_typeconstraints merge_occurred |= self . delayed_type_constraints != delayed_typeconstraints # add subtype constraints for all replacements for v0 , v1 in self . phi_variables . items ( ) : # v0 will be replaced by v1 if not merged_typevars . has_type_variable_for ( v1 , None ) : merged_typevars . add_type_variable ( v1 , None , TypeVariable ( ) ) if not merged_typevars . has_type_variable_for ( v0 , None ) : merged_typevars . add_type_variable ( v0 , None , TypeVariable ( ) ) # Assuming v2 = phi(v0, v1), then we know that v0_typevar == v1_typevar == v2_typevar # However, it's possible that neither v0 nor v1 will ever be used in future blocks, which not only makes # this phi function useless, but also leads to the incorrect assumption that v1_typevar == v2_typevar. # Hence, we delay the addition of the equivalence relationship into the type constraints. It is only added # when v1 (the new variable that will end up in the state) is ever used in the future. # create an equivalence relationship equivalence = Equivalence ( merged_typevars . get_type_variable ( v1 , None ) , merged_typevars . get_type_variable ( v0 , None ) ) delayed_typeconstraints [ v1 ] . add ( equivalence ) stack_offset_typevars = { } all_stack_addr_typevar_offsets = set ( self . stack_offset_typevars ) for other in others : all_stack_addr_typevar_offsets . update ( other . stack_offset_typevars ) for offset in all_stack_addr_typevar_offsets : all_typevars = set ( ) for state in ( self , ) + others : typevar = state . stack_offset_typevars . get ( offset , None ) if typevar is not None : all_typevars . add ( typevar ) if len ( all_typevars ) == 1 : typevar = all_typevars . pop ( ) else : typevar = TypeVariable ( ) for orig_typevar in all_typevars : merged_typeconstraints . add ( Subtype ( orig_typevar , typevar ) ) stack_offset_typevars [ offset ] = typevar # clean up self . phi_variables = { } self . successor_block_addr = None state = VariableRecoveryFastState ( successor , self . _analysis , self . arch , self . function , stack_region = merged_stack_region , register_region = merged_register_region , global_region = merged_global_region , typevars = merged_typevars , type_constraints = merged_typeconstraints , delayed_type_constraints = delayed_typeconstraints , stack_offset_typevars = stack_offset_typevars , project = self . project , ) return state , merge_occurred