//===-- ThreadPlanCallOnFunctionExit.cpp ------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Target/ThreadPlanCallOnFunctionExit.h"

using namespace lldb;
using namespace lldb_private;

ThreadPlanCallOnFunctionExit::ThreadPlanCallOnFunctionExit(
    Thread &thread, const Callback &callback)
    : ThreadPlan(ThreadPlanKind::eKindGeneric, "CallOnFunctionExit", thread,
                 eVoteNoOpinion, eVoteNoOpinion // TODO check with Jim on these
                 ),
      m_callback(callback) {
  // We are not a user-generated plan.
  SetIsMasterPlan(false);
}

void ThreadPlanCallOnFunctionExit::DidPush() {
  // We now want to queue the "step out" thread plan so it executes
  // and completes.

  // Set stop vote to eVoteNo.
  m_step_out_threadplan_sp = GetThread().QueueThreadPlanForStepOut(
      false,             // abort other plans
      nullptr,           // addr_context
      true,              // first instruction
      true,              // stop other threads
      eVoteNo,           // do not say "we're stopping"
      eVoteNoOpinion,    // don't care about
                         // run state broadcasting
      0,                 // frame_idx
      eLazyBoolCalculate // avoid code w/o debinfo
      );
}

// -------------------------------------------------------------------------
// ThreadPlan API
// -------------------------------------------------------------------------

void ThreadPlanCallOnFunctionExit::GetDescription(
    Stream *s, lldb::DescriptionLevel level) {
  if (!s)
    return;
  s->Printf("Running until completion of current function, then making "
            "callback.");
}

bool ThreadPlanCallOnFunctionExit::ValidatePlan(Stream *error) {
  // We'll say we're always good since I don't know what would make this
  // invalid.
  return true;
}

bool ThreadPlanCallOnFunctionExit::ShouldStop(Event *event_ptr) {
  // If this is where we find out that an internal stop came in, then:
  // Check if the step-out plan completed.  If it did, then we want to
  // run the callback here (our reason for living...)
  if (m_step_out_threadplan_sp && m_step_out_threadplan_sp->IsPlanComplete()) {
    m_callback();

    // We no longer need the pointer to the step-out thread plan.
    m_step_out_threadplan_sp.reset();

    // Indicate that this plan is done and can be discarded.
    SetPlanComplete();

    // We're done now, but we want to return false so that we
    // don't cause the thread to really stop.
  }

  return false;
}

bool ThreadPlanCallOnFunctionExit::WillStop() {
  // The code looks like the return value is ignored via ThreadList::
  // ShouldStop().
  // This is called when we really are going to stop.  We don't care
  // and don't need to do anything here.
  return false;
}

bool ThreadPlanCallOnFunctionExit::DoPlanExplainsStop(Event *event_ptr) {
  // We don't ever explain a stop.  The only stop that is relevant
  // to us directly is the step_out plan we added to do the heavy lifting
  // of getting us past the current method.
  return false;
}

lldb::StateType ThreadPlanCallOnFunctionExit::GetPlanRunState() {
  // This value doesn't matter - we'll never be the top thread plan, so
  // nobody will ask us this question.
  return eStateRunning;
}
