Process each successor generated by the job, and return a new list of succeeding jobs.

        :param VFGJob job: The VFGJob instance.
        :param SimState successor: The succeeding state.
        :param list all_successors:  A list of all successors.
        :return: A list of newly created jobs from the successor.
        :rtype: list

def _handle_successor(self, job, successor, all_successors):
        """
        Process each successor generated by the job, and return a new list of succeeding jobs.

        :param VFGJob job: The VFGJob instance.
        :param SimState successor: The succeeding state.
        :param list all_successors:  A list of all successors.
        :return: A list of newly created jobs from the successor.
        :rtype: list
        """

        # Initialize parameters
        addr = job.addr
        jumpkind = successor.history.jumpkind

        #
        # Get instruction pointer
        #

        if job.is_return_jump:
            ret_target = job.call_stack.current_return_target
            if ret_target is None:
                # We have no where to go according to our call stack. However, the callstack might be corrupted
                l.debug("According to the call stack, we have nowhere to return to.")
                return [ ]

            successor.ip = ret_target

        # this try-except block is to handle cases where the instruction pointer is symbolic
        try:
            successor_addrs = successor.solver.eval_upto(successor.ip, 2)
        except SimValueError:
            # TODO: Should fall back to reading targets from CFG
            # It cannot be concretized currently. Maybe we could handle
            # it later, maybe it just cannot be concretized
            return [ ]

        if len(successor_addrs) > 1:
            # multiple concrete targets
            if job.is_return_jump:
                # It might be caused by state merging
                # We may retrieve the correct ip from call stack
                successor.ip = job.call_stack.current_return_target

            else:
                return self._handle_successor_multitargets(job, successor, all_successors)

        # Now there should be one single target for the successor
        successor_addr = successor.solver.eval_one(successor.ip)

        # Get the fake ret successor
        fakeret_successor = None
        if self._is_call_jumpkind(jumpkind):
            fakeret_successor = all_successors[-1]

            # If the function we're calling into doesn't return, we should discard it
            if self._cfg is not None:
                func = self.kb.functions.function(addr=job.call_target)
                if func is not None and func.returning is False and len(all_successors) == 2:
                    del all_successors[-1]
                    fakeret_successor = None

        if self._is_call_jumpkind(jumpkind):
            # Create a new call stack for the successor
            new_call_stack = self._create_callstack(job, successor_addr, jumpkind, fakeret_successor)
            if new_call_stack is None:
                l.debug("Cannot create a new callstack for address %#x", successor_addr)
                job.dbg_exit_status[successor] = ""
                return [ ]
            new_call_stack_suffix = new_call_stack.stack_suffix(self._context_sensitivity_level)

            new_function_key = FunctionKey.new(successor_addr, new_call_stack_suffix)
            # Save the initial state for the function
            self._save_function_initial_state(new_function_key, successor_addr, successor.copy())

            # bail out if we hit the interfunction_level cap
            if len(job.call_stack) >= self._interfunction_level:
                l.debug('We are not tracing into a new function %#08x as we hit interfunction_level limit', successor_addr)

                # mark it as skipped
                job.dbg_exit_status[successor] = "Skipped"

                job.call_skipped = True
                job.call_function_key = new_function_key

                job.call_task.skipped = True

                return [ ]

        elif jumpkind == 'Ijk_Ret':
            # Pop the current function out from the call stack
            new_call_stack = self._create_callstack(job, successor_addr, jumpkind, fakeret_successor)
            if new_call_stack is None:
                l.debug("Cannot create a new callstack for address %#x", successor_addr)
                job.dbg_exit_status[successor] = ""
                return [ ]
            new_call_stack_suffix = new_call_stack.stack_suffix(self._context_sensitivity_level)

        else:
            new_call_stack = job.call_stack
            new_call_stack_suffix = job.call_stack_suffix

        # Generate the new block ID
        new_block_id = BlockID.new(successor_addr, new_call_stack_suffix, jumpkind)

        #
        # Generate new VFG jobs
        #

        if jumpkind == "Ijk_Ret":
            assert not job.is_call_jump

            # Record this return
            self._return_target_sources[successor_addr].append(job.call_stack_suffix + (addr,))

            # Check if this return is inside our pending returns list
            if new_block_id in self._pending_returns:
                del self._pending_returns[new_block_id]

        # Check if we have reached a fix-point
        if jumpkind != 'Ijk_FakeRet' and \
                new_block_id in self._nodes:
            last_state = self._nodes[new_block_id].state

            _, _, merged = last_state.merge(successor, plugin_whitelist=self._mergeable_plugins)

            if merged:
                l.debug("%s didn't reach a fix-point", new_block_id)
            else:
                l.debug("%s reaches a fix-point.", new_block_id)
                job.dbg_exit_status[successor] = "Merged due to reaching a fix-point"
                return [ ]

        new_jobs = self._create_new_jobs(job, successor, new_block_id, new_call_stack)

        return new_jobs