def _handle_successor(self, job, successor, successors):
        """
        Returns a new CFGJob instance for further analysis, or None if there is no immediate state to perform the
        analysis on.

        :param CFGJob job: The current job.
        """

        state = successor
        all_successor_states = successors
        addr = job.addr

        # The PathWrapper instance to return
        pw = None

        job.successor_status[state] = ""

        new_state = state.copy()
        suc_jumpkind = state.history.jumpkind
        suc_exit_stmt_idx = state.scratch.exit_stmt_idx
        suc_exit_ins_addr = state.scratch.exit_ins_addr

        if suc_jumpkind in {'Ijk_EmWarn', 'Ijk_NoDecode', 'Ijk_MapFail', 'Ijk_NoRedir',
                            'Ijk_SigTRAP', 'Ijk_SigSEGV', 'Ijk_ClientReq'}:
            # Ignore SimExits that are of these jumpkinds
            job.successor_status[state] = "Skipped"
            return [ ]

        call_target = job.extra_info['call_target']
        if suc_jumpkind == "Ijk_FakeRet" and call_target is not None:
            # if the call points to a SimProcedure that doesn't return, we don't follow the fakeret anymore
            if self.project.is_hooked(call_target):
                sim_proc = self.project._sim_procedures[call_target]
                if sim_proc.NO_RET:
                    return [ ]

        # Get target address
        try:
            target_addr = state.solver.eval_one(state.ip)
        except (SimValueError, SimSolverModeError):
            # It cannot be concretized currently. Maybe we can handle it later, maybe it just cannot be concretized
            target_addr = None
            if suc_jumpkind == "Ijk_Ret":
                target_addr = job.call_stack.current_return_target
                if target_addr is not None:
                    new_state.ip = new_state.solver.BVV(target_addr, new_state.arch.bits)

        if target_addr is None:
            # Unlucky...
            return [ ]

        if state.thumb:
            # Make sure addresses are always odd. It is important to encode this information in the address for the
            # time being.
            target_addr |= 1

        # see if the target successor is in our whitelist
        if self._address_whitelist is not None:
            if target_addr not in self._address_whitelist:
                l.debug("Successor %#x is not in the address whitelist. Skip.", target_addr)
                return [ ]

        # see if this edge is in the base graph
        if self._base_graph is not None:
            # TODO: make it more efficient. the current implementation is half-assed and extremely slow
            for src_, dst_ in self._base_graph.edges():
                if src_.addr == addr and dst_.addr == target_addr:
                    break
            else:
                # not found
                l.debug("Edge (%#x -> %#x) is not found in the base graph. Skip.", addr, target_addr)
                return [ ]

        # Fix target_addr for syscalls
        if suc_jumpkind.startswith("Ijk_Sys"):
            syscall_proc = self.project.simos.syscall(new_state)
            if syscall_proc is not None:
                target_addr = syscall_proc.addr

        self._pre_handle_successor_state(job.extra_info, suc_jumpkind, target_addr)

        if suc_jumpkind == "Ijk_FakeRet":
            if target_addr == job.extra_info['last_call_exit_target']:
                l.debug("... skipping a fake return exit that has the same target with its call exit.")
                job.successor_status[state] = "Skipped"
                return [ ]

            if job.extra_info['skip_fakeret']:
                l.debug('... skipping a fake return exit since the function it\'s calling doesn\'t return')
                job.successor_status[state] = "Skipped - non-returning function 0x%x" % job.extra_info['call_target']
                return [ ]

        # TODO: Make it optional
        if (suc_jumpkind == 'Ijk_Ret' and
                    self._call_depth is not None and
                    len(job.call_stack) <= 1
            ):
            # We cannot continue anymore since this is the end of the function where we started tracing
            l.debug('... reaching the end of the starting function, skip.')
            job.successor_status[state] = "Skipped - reaching the end of the starting function"
            return [ ]

        # Create the new call stack of target block
        new_call_stack = self._create_new_call_stack(addr, all_successor_states, job, target_addr,
                                                     suc_jumpkind)
        # Create the callstack suffix
        new_call_stack_suffix = new_call_stack.stack_suffix(self._context_sensitivity_level)
        # Tuple that will be used to index this exit
        new_tpl = self._generate_block_id(new_call_stack_suffix, target_addr, suc_jumpkind.startswith('Ijk_Sys'))

        # We might have changed the mode for this basic block
        # before. Make sure it is still running in 'fastpath' mode
        self._reset_state_mode(new_state, 'fastpath')

        pw = CFGJob(target_addr,
                    new_state,
                    self._context_sensitivity_level,
                    src_block_id=job.block_id,
                    src_exit_stmt_idx=suc_exit_stmt_idx,
                    src_ins_addr=suc_exit_ins_addr,
                    call_stack=new_call_stack,
                    jumpkind=suc_jumpkind,
                    )
        # Special case: If the binary has symbols and the target address is a function, but for some reason (e.g.,
        # a tail-call optimization) the CallStack's function address is still the old function address, we will have to
        # overwrite it here.
        if not self._is_call_jumpkind(pw.jumpkind):
            target_symbol = self.project.loader.find_symbol(target_addr)
            if target_symbol and target_symbol.is_function:
                # Force update the function address
                pw.func_addr = target_addr

        # Generate new exits
        if suc_jumpkind == "Ijk_Ret":
            # This is the real return exit
            job.successor_status[state] = "Appended"

        elif suc_jumpkind == "Ijk_FakeRet":
            # This is the default "fake" retn that generated at each
            # call. Save them first, but don't process them right
            # away
            # st = self.project._simos.prepare_call_state(new_state, initial_state=saved_state)
            st = new_state
            self._reset_state_mode(st, 'fastpath')

            pw = None # clear the job
            pe = PendingJob(job.func_addr,
                            job.extra_info['call_target'],
                            st,
                            job.block_id,
                            suc_exit_stmt_idx,
                            suc_exit_ins_addr,
                            new_call_stack
                            )
            self._pending_jobs[new_tpl] = pe
            self._register_analysis_job(pe.caller_func_addr, pe)
            job.successor_status[state] = "Pended"

        elif self._traced_addrs[new_call_stack_suffix][target_addr] >= 1 and suc_jumpkind == "Ijk_Ret":
            # This is a corner case for the f****** ARM instruction
            # like
            # BLEQ <address>
            # If we have analyzed the boring exit before returning from that called address, we will lose the link
            # between the last block of the function being called and the basic block it returns to. We cannot
            # reanalyze the basic block as we are not flow-sensitive, but we can still record the connection and make
            # for it afterwards.
            pass

        else:
            job.successor_status[state] = "Appended"

        if job.extra_info['is_call_jump'] and job.extra_info['call_target'] in self._non_returning_functions:
            job.extra_info['skip_fakeret'] = True

        if not pw:
            return [ ]

        if self._base_graph is not None:
            # remove all existing jobs that has the same block ID
            if next((en for en in self.jobs if en.block_id == pw.block_id), None):
                # TODO: this is very hackish. Reimplement this logic later
                self._job_info_queue = [entry for entry in self._job_info_queue if entry.job.block_id != pw.block_id]

        # register the job
        self._register_analysis_job(pw.func_addr, pw)

        return [ pw ]
Returns a new CFGJob instance for further analysis, or None if there is no immediate state to perform the
        analysis on.

        :param CFGJob job: The current job.