docs: def _pre_job_handling(self, job):  # pylint:disable=arguments-differ
          """
          Before processing a CFGJob.
          Right now each block is traced at most once. If it is traced more than once, we will mark it as "should_skip"
          before tracing it.
          An AngrForwardAnalysisSkipJob exception is raised in order to skip analyzing the job.
  
          :param CFGJob job: The CFG job object.
          :param dict _locals: A bunch of local variables that will be kept around when handling this job and its
                          corresponding successors.
          :return: None
          """
  
          # Extract initial info the CFGJob
          job.call_stack_suffix = job.get_call_stack_suffix()
          job.current_function = self.kb.functions.function(job.func_addr, create=True,
                                                            syscall=job.jumpkind.startswith("Ijk_Sys"))
          src_block_id = job.src_block_id
          src_exit_stmt_idx = job.src_exit_stmt_idx
          src_ins_addr = job.src_ins_addr
          addr = job.addr
  
          # Log this address
          if l.level == logging.DEBUG:
              self._analyzed_addrs.add(addr)
  
          if addr == job.func_addr:
              # Store the input state of this function
              self._function_input_states[job.func_addr] = job.state
  
          # deregister this job
          self._deregister_analysis_job(job.func_addr, job)
  
          # Generate a unique key for this job
          block_id = job.block_id
  
          # Should we skip tracing this block?
          should_skip = False
          if self._traced_addrs[job.call_stack_suffix][addr] >= self._max_iterations:
              should_skip = True
          elif self._is_call_jumpkind(job.jumpkind) and \
               self._call_depth is not None and \
               len(job.call_stack) > self._call_depth and \
               (self._call_tracing_filter is None or self._call_tracing_filter(job.state, job.jumpkind)):
              should_skip = True
  
          # SimInspect breakpoints support
          job.state._inspect('cfg_handle_job', BP_BEFORE)
  
          # Get a SimSuccessors out of current job
          sim_successors, exception_info, _ = self._get_simsuccessors(addr, job, current_function_addr=job.func_addr)
  
          # determine the depth of this basic block
          if self._max_steps is None:
              # it's unnecessary to track depth when we are not limiting max_steps
              depth = None
          else:
              if src_block_id is None:
                  # oh this is the very first basic block on this path
                  depth = 0
              else:
                  src_cfgnode = self._nodes[src_block_id]
                  depth = src_cfgnode.depth + 1
                  # the depth will not be updated later on even if this block has a greater depth on another path.
                  # consequently, the `max_steps` limit is not veyr precise - I didn't see a need to make it precise
                  # though.
  
          if block_id not in self._nodes:
              # Create the CFGNode object
              cfg_node = self._create_cfgnode(sim_successors, job.call_stack, job.func_addr, block_id=block_id,
                                              depth=depth
                                              )
  
              self._nodes[block_id] = cfg_node
              self._nodes_by_addr[cfg_node.addr].append(cfg_node)
  
          else:
              # each block_id should only correspond to one CFGNode
              # use the existing CFGNode object
              # note that since we reuse existing CFGNodes, we may miss the following cases:
              #
              # mov eax, label_0       mov ebx, label_1
              #     jmp xxx                 jmp xxx
              #         |                      |
              #         |                      |
              #         *----------------------*
              #                     |
              #                 call eax
              #
              # Since the basic block "call eax" will only be traced once, either label_0 or label_1 will be missed in
              # this case. Indirect jump resolution might be able to get it, but that's another story. Ideally,
              # "call eax" should be traced twice with *different* sim_successors keys, which requires block ID being flow
              # sensitive, but it is way too expensive.
  
              cfg_node = self._nodes[block_id]
  
          # Increment tracing count for this block
          self._traced_addrs[job.call_stack_suffix][addr] += 1
  
          if self._keep_state:
              # TODO: if we are reusing an existing CFGNode, we will be overwriting the original input state here. we
              # TODO: should save them all, which, unfortunately, requires some redesigning :-(
              cfg_node.input_state = sim_successors.initial_state
  
          # See if this job cancels another FakeRet
          # This should be done regardless of whether this job should be skipped or not, otherwise edges will go missing
          # in the CFG or function transiton graphs.
          if job.jumpkind == 'Ijk_FakeRet' or \
                  (job.jumpkind == 'Ijk_Ret' and block_id in self._pending_jobs):
              # The fake ret is confirmed (since we are returning from the function it calls). Create an edge for it
              # in the graph.
  
              if block_id in self._pending_jobs:
                  the_job = self._pending_jobs.pop(block_id)  # type: PendingJob
                  self._deregister_analysis_job(the_job.caller_func_addr, the_job)
              else:
                  the_job = job
  
              self._graph_add_edge(the_job.src_block_id, block_id,
                                   jumpkind='Ijk_FakeRet',
                                   stmt_idx=the_job.src_exit_stmt_idx,
                                   ins_addr=src_ins_addr
                                   )
              self._update_function_transition_graph(the_job.src_block_id, block_id,
                                                     jumpkind='Ijk_FakeRet',
                                                     ins_addr=src_ins_addr,
                                                     stmt_idx=the_job.src_exit_stmt_idx,
                                                     confirmed=True
                                                     )
  
          if sim_successors is None or should_skip:
              # We cannot retrieve the block, or we should skip the analysis of this node
              # But we create the edge anyway. If the sim_successors does not exist, it will be an edge from the previous
              # node to a PathTerminator
              self._graph_add_edge(src_block_id, block_id,
                                   jumpkind=job.jumpkind,
                                   stmt_idx=src_exit_stmt_idx,
                                   ins_addr=src_ins_addr
                                   )
              self._update_function_transition_graph(src_block_id, block_id,
                                                     jumpkind=job.jumpkind,
                                                     ins_addr=src_ins_addr,
                                                     stmt_idx=src_exit_stmt_idx)
  
              # We are good. Raise the exception and leave
              raise AngrSkipJobNotice()
  
          self._update_thumb_addrs(sim_successors, job.state)
  
          # We store the function hints first. Function hints will be checked at the end of the analysis to avoid
          # any duplication with existing jumping targets
          if self._enable_function_hints:
              if sim_successors.sort == 'IRSB' and sim_successors.all_successors:
                  function_hints = self._search_for_function_hints(sim_successors.all_successors[0])
                  for f in function_hints:
                      self._pending_function_hints.add(f)
  
          self._graph_add_edge(src_block_id, block_id, jumpkind=job.jumpkind, stmt_idx=src_exit_stmt_idx,
                               ins_addr=src_ins_addr)
          self._update_function_transition_graph(src_block_id, block_id, jumpkind=job.jumpkind,
                                                 ins_addr=src_ins_addr, stmt_idx=src_exit_stmt_idx)
  
          if block_id in self._pending_edges:
              # there are some edges waiting to be created. do it here.
              for src_key, dst_key, data in self._pending_edges[block_id]:
                  self._graph_add_edge(src_key, dst_key, **data)
              del self._pending_edges[block_id]
  
          block_info = {reg: sim_successors.initial_state.registers.load(reg) for reg in self.project.arch.persistent_regs}
          self._block_artifacts[addr] = block_info
  
          job.cfg_node = cfg_node
          job.sim_successors = sim_successors
          job.exception_info = exception_info
  
          # For debugging purposes!
          job.successor_status = {}