text: //===-- PythonExceptionState.cpp --------------------------------*- C++ -*-===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  
  #ifndef LLDB_DISABLE_PYTHON
  
  // LLDB Python header must be included first
  #include "lldb-python.h"
  
  #include "PythonExceptionState.h"
  
  #include "llvm/ADT/StringRef.h"
  #include "llvm/Support/raw_ostream.h"
  
  using namespace lldb_private;
  
  PythonExceptionState::PythonExceptionState(bool restore_on_exit)
      : m_restore_on_exit(restore_on_exit) {
    Acquire(restore_on_exit);
  }
  
  PythonExceptionState::~PythonExceptionState() {
    if (m_restore_on_exit)
      Restore();
  }
  
  void PythonExceptionState::Acquire(bool restore_on_exit) {
    // If a state is already acquired, the user needs to decide whether they want
    // to discard or restore it.  Don't allow the potential silent loss of a
    // valid state.
    assert(!IsError());
  
    if (!HasErrorOccurred())
      return;
  
    PyObject *py_type = nullptr;
    PyObject *py_value = nullptr;
    PyObject *py_traceback = nullptr;
    PyErr_Fetch(&py_type, &py_value, &py_traceback);
    // PyErr_Fetch clears the error flag.
    assert(!HasErrorOccurred());
  
    // Ownership of the objects returned by `PyErr_Fetch` is transferred to us.
    m_type.Reset(PyRefType::Owned, py_type);
    m_value.Reset(PyRefType::Owned, py_value);
    m_traceback.Reset(PyRefType::Owned, py_traceback);
    m_restore_on_exit = restore_on_exit;
  }
  
  void PythonExceptionState::Restore() {
    if (m_type.IsValid()) {
      // The documentation for PyErr_Restore says "Do not pass a null type and
      // non-null value or traceback.  So only restore if type was non-null to
      // begin with.  In this case we're passing ownership back to Python so
      // release them all.
      PyErr_Restore(m_type.release(), m_value.release(), m_traceback.release());
    }
  
    // After we restore, we should not hold onto the exception state.  Demand
    // that it be re-acquired.
    Discard();
  }
  
  void PythonExceptionState::Discard() {
    m_type.Reset();
    m_value.Reset();
    m_traceback.Reset();
  }
  
  void PythonExceptionState::Reset() {
    if (m_restore_on_exit)
      Restore();
    else
      Discard();
  }
  
  bool PythonExceptionState::HasErrorOccurred() { return PyErr_Occurred(); }
  
  bool PythonExceptionState::IsError() const {
    return m_type.IsValid() || m_value.IsValid() || m_traceback.IsValid();
  }
  
  PythonObject PythonExceptionState::GetType() const { return m_type; }
  
  PythonObject PythonExceptionState::GetValue() const { return m_value; }
  
  PythonObject PythonExceptionState::GetTraceback() const { return m_traceback; }
  
  std::string PythonExceptionState::Format() const {
    // Don't allow this function to modify the error state.
    PythonExceptionState state(true);
  
    std::string backtrace = ReadBacktrace();
    if (!IsError())
      return std::string();
  
    // It's possible that ReadPythonBacktrace generated another exception. If
    // this happens we have to clear the exception, because otherwise
    // PyObject_Str() will assert below.  That's why we needed to do the save /
    // restore at the beginning of this function.
    PythonExceptionState bt_error_state(false);
  
    std::string error_string;
    llvm::raw_string_ostream error_stream(error_string);
    error_stream << m_value.Str().GetString() << "\n";
  
    if (!bt_error_state.IsError()) {
      // If we were able to read the backtrace, just append it.
      error_stream << backtrace << "\n";
    } else {
      // Otherwise, append some information about why we were unable to obtain
      // the backtrace.
      PythonString bt_error = bt_error_state.GetValue().Str();
      error_stream << "An error occurred while retrieving the backtrace: "
                   << bt_error.GetString() << "\n";
    }
    return error_stream.str();
  }
  
  std::string PythonExceptionState::ReadBacktrace() const {
    std::string retval("backtrace unavailable");
  
    auto traceback_module = PythonModule::ImportModule("traceback");
  #if PY_MAJOR_VERSION >= 3
    auto stringIO_module = PythonModule::ImportModule("io");
  #else
    auto stringIO_module = PythonModule::ImportModule("StringIO");
  #endif
    if (!m_traceback.IsAllocated())
      return retval;
  
    if (!traceback_module.IsAllocated() || !stringIO_module.IsAllocated())
      return retval;
  
    auto stringIO_builder =
        stringIO_module.ResolveName<PythonCallable>("StringIO");
    if (!stringIO_builder.IsAllocated())
      return retval;
  
    auto stringIO_buffer = stringIO_builder();
    if (!stringIO_buffer.IsAllocated())
      return retval;
  
    auto printTB = traceback_module.ResolveName<PythonCallable>("print_tb");
    if (!printTB.IsAllocated())
      return retval;
  
    auto printTB_result =
        printTB(m_traceback.get(), Py_None, stringIO_buffer.get());
    auto stringIO_getvalue =
        stringIO_buffer.ResolveName<PythonCallable>("getvalue");
    if (!stringIO_getvalue.IsAllocated())
      return retval;
  
    auto printTB_string = stringIO_getvalue().AsType<PythonString>();
    if (!printTB_string.IsAllocated())
      return retval;
  
    llvm::StringRef string_data(printTB_string.GetString());
    retval.assign(string_data.data(), string_data.size());
  
    return retval;
  }
  
  #endif
  