lawrence_danna updated this revision to Diff 225746.
lawrence_danna added a comment.

rebased


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69214/new/

https://reviews.llvm.org/D69214

Files:
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
  lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
  lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
  lldb/unittests/ScriptInterpreter/Python/CMakeLists.txt
  lldb/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
  lldb/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp

Index: lldb/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp
===================================================================
--- lldb/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-//===-- PythonExceptionStateTest.cpp ------------------------------*- C++
-//-*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "gtest/gtest.h"
-
-#include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h"
-#include "Plugins/ScriptInterpreter/Python/PythonExceptionState.h"
-#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
-#include "Plugins/ScriptInterpreter/Python/lldb-python.h"
-
-#include "PythonTestSuite.h"
-
-using namespace lldb_private;
-
-class PythonExceptionStateTest : public PythonTestSuite {
-public:
-protected:
-  void RaiseException() {
-    PyErr_SetString(PyExc_RuntimeError, "PythonExceptionStateTest test error");
-  }
-};
-
-TEST_F(PythonExceptionStateTest, TestExceptionStateChecking) {
-  PyErr_Clear();
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  RaiseException();
-  EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-}
-
-TEST_F(PythonExceptionStateTest, TestAcquisitionSemantics) {
-  PyErr_Clear();
-  PythonExceptionState no_error(false);
-  EXPECT_FALSE(no_error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-  RaiseException();
-  PythonExceptionState error(false);
-  EXPECT_TRUE(error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-  error.Discard();
-
-  PyErr_Clear();
-  RaiseException();
-  error.Acquire(false);
-  EXPECT_TRUE(error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-}
-
-TEST_F(PythonExceptionStateTest, TestDiscardSemantics) {
-  PyErr_Clear();
-
-  // Test that discarding an exception does not restore the exception
-  // state even when auto-restore==true is set
-  RaiseException();
-  PythonExceptionState error(true);
-  EXPECT_TRUE(error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  error.Discard();
-  EXPECT_FALSE(error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-}
-
-TEST_F(PythonExceptionStateTest, TestResetSemantics) {
-  PyErr_Clear();
-
-  // Resetting when auto-restore is true should restore.
-  RaiseException();
-  PythonExceptionState error(true);
-  EXPECT_TRUE(error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-  error.Reset();
-  EXPECT_FALSE(error.IsError());
-  EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-
-  // Resetting when auto-restore is false should discard.
-  RaiseException();
-  PythonExceptionState error2(false);
-  EXPECT_TRUE(error2.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-  error2.Reset();
-  EXPECT_FALSE(error2.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-}
-
-TEST_F(PythonExceptionStateTest, TestManualRestoreSemantics) {
-  PyErr_Clear();
-  RaiseException();
-  PythonExceptionState error(false);
-  EXPECT_TRUE(error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  error.Restore();
-  EXPECT_FALSE(error.IsError());
-  EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-}
-
-TEST_F(PythonExceptionStateTest, TestAutoRestoreSemantics) {
-  PyErr_Clear();
-  // Test that using the auto-restore flag correctly restores the exception
-  // state on destruction, and not using the auto-restore flag correctly
-  // does NOT restore the state on destruction.
-  {
-    RaiseException();
-    PythonExceptionState error(false);
-    EXPECT_TRUE(error.IsError());
-    EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-  }
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-  {
-    RaiseException();
-    PythonExceptionState error(true);
-    EXPECT_TRUE(error.IsError());
-    EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-  }
-  EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-}
-
-TEST_F(PythonExceptionStateTest, TestAutoRestoreChanged) {
-  // Test that if we re-acquire with different auto-restore semantics,
-  // that the new semantics are respected.
-  PyErr_Clear();
-
-  RaiseException();
-  PythonExceptionState error(false);
-  EXPECT_TRUE(error.IsError());
-
-  error.Reset();
-  EXPECT_FALSE(error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  RaiseException();
-  error.Acquire(true);
-  EXPECT_TRUE(error.IsError());
-  EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
-
-  error.Reset();
-  EXPECT_FALSE(error.IsError());
-  EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
-
-  PyErr_Clear();
-}
Index: lldb/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
===================================================================
--- lldb/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
+++ lldb/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
@@ -21,6 +21,7 @@
 
 using namespace lldb_private;
 using namespace lldb_private::python;
+using llvm::Expected;
 
 class PythonDataObjectsTest : public PythonTestSuite {
 public:
@@ -771,4 +772,32 @@
   }
 
 #endif
+}
+
+TEST_F(PythonDataObjectsTest, TestExceptions) {
+
+  static const char script[] = R"(
+def foo():
+  return bar()
+def bar():
+  return baz()
+def baz():
+  return 1 / 0
+)";
+
+  PythonScript foo(script, "foo");
+  Expected<PythonObject> r = foo();
+
+  bool failed = !r;
+  ASSERT_TRUE(failed);
+
+  std::string backtrace;
+  llvm::handleAllErrors(r.takeError(), [&](const PythonException &E) {
+    backtrace = E.ReadBacktrace();
+  });
+
+  EXPECT_NE(backtrace.find("line 3, in foo"), std::string::npos);
+  EXPECT_NE(backtrace.find("line 5, in bar"), std::string::npos);
+  EXPECT_NE(backtrace.find("line 7, in baz"), std::string::npos);
+  EXPECT_NE(backtrace.find("ZeroDivisionError"), std::string::npos);
 }
\ No newline at end of file
Index: lldb/unittests/ScriptInterpreter/Python/CMakeLists.txt
===================================================================
--- lldb/unittests/ScriptInterpreter/Python/CMakeLists.txt
+++ lldb/unittests/ScriptInterpreter/Python/CMakeLists.txt
@@ -1,6 +1,5 @@
 add_lldb_unittest(ScriptInterpreterPythonTests
   PythonDataObjectsTests.cpp
-  PythonExceptionStateTests.cpp
   PythonTestSuite.cpp
 
   LINK_LIBS
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -363,7 +363,7 @@
     eIOHandlerWatchpoint
   };
 
-  PythonObject &GetMainModule();
+  PythonModule &GetMainModule();
 
   PythonDictionary &GetSessionDictionary();
 
@@ -377,7 +377,7 @@
   PythonObject m_saved_stdin;
   PythonObject m_saved_stdout;
   PythonObject m_saved_stderr;
-  PythonObject m_main_module;
+  PythonModule m_main_module;
   PythonDictionary m_session_dict;
   PythonDictionary m_sys_module_dict;
   PythonObject m_run_one_line_function;
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -16,7 +16,6 @@
 #include "lldb-python.h"
 
 #include "PythonDataObjects.h"
-#include "PythonExceptionState.h"
 #include "ScriptInterpreterPythonImpl.h"
 
 #include "lldb/API/SBFrame.h"
@@ -56,6 +55,7 @@
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::python;
+using llvm::Expected;
 
 // Defined in the SWIG source file
 #if PY_MAJOR_VERSION >= 3
@@ -747,9 +747,9 @@
   return true;
 }
 
-PythonObject &ScriptInterpreterPythonImpl::GetMainModule() {
+PythonModule &ScriptInterpreterPythonImpl::GetMainModule() {
   if (!m_main_module.IsValid())
-    m_main_module.Reset(PyRefType::Borrowed, PyImport_AddModule("__main__"));
+    m_main_module = unwrapIgnoringErrors(PythonModule::Import("__main__"));
   return m_main_module;
 }
 
@@ -1030,6 +1030,7 @@
             "can't interrupt");
   return false;
 }
+
 bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn(
     llvm::StringRef in_string, ScriptInterpreter::ScriptReturnType return_type,
     void *ret_value, const ExecuteScriptOptions &options) {
@@ -1040,151 +1041,111 @@
                     Locker::NoSTDIN,
                 Locker::FreeAcquiredLock | Locker::TearDownSession);
 
-  PythonObject py_return;
-  PythonObject &main_module = GetMainModule();
-  PythonDictionary globals(PyRefType::Borrowed,
-                           PyModule_GetDict(main_module.get()));
-  PythonObject py_error;
-  bool ret_success = false;
-  int success;
+  PythonModule &main_module = GetMainModule();
+  PythonDictionary globals = main_module.GetDictionary();
 
   PythonDictionary locals = GetSessionDictionary();
-
-  if (!locals.IsValid()) {
+  if (!locals.IsValid())
     locals = unwrapIgnoringErrors(
         As<PythonDictionary>(globals.GetAttribute(m_dictionary_name)));
-  }
-
   if (!locals.IsValid())
     locals = globals;
 
-  py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
-  if (py_error.IsValid())
-    PyErr_Clear();
-
-  std::string as_string = in_string.str();
-  { // scope for PythonInputReaderManager
-    // PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL);
-    py_return.Reset(PyRefType::Owned,
-                    PyRun_String(as_string.c_str(), Py_eval_input,
-                                 globals.get(), locals.get()));
-    if (!py_return.IsValid()) {
-      py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
-      if (py_error.IsValid())
-        PyErr_Clear();
-
-      py_return.Reset(PyRefType::Owned,
-                      PyRun_String(as_string.c_str(), Py_single_input,
-                                   globals.get(), locals.get()));
-    }
+  Expected<PythonObject> maybe_py_return =
+      runStringOneLine(in_string, globals, locals);
+
+  if (!maybe_py_return) {
+    llvm::handleAllErrors(
+        maybe_py_return.takeError(),
+        [&](PythonException &E) {
+          E.Restore();
+          if (options.GetMaskoutErrors()) {
+            if (E.Matches(PyExc_SyntaxError)) {
+              PyErr_Print();
+            }
+            PyErr_Clear();
+          }
+        },
+        [](const llvm::ErrorInfoBase &E) {});
+    return false;
   }
 
-  if (py_return.IsValid()) {
-    switch (return_type) {
-    case eScriptReturnTypeCharPtr: // "char *"
-    {
-      const char format[3] = "s#";
-      success = PyArg_Parse(py_return.get(), format, (char **)ret_value);
-      break;
-    }
-    case eScriptReturnTypeCharStrOrNone: // char* or NULL if py_return ==
-                                         // Py_None
-    {
-      const char format[3] = "z";
-      success = PyArg_Parse(py_return.get(), format, (char **)ret_value);
-      break;
-    }
-    case eScriptReturnTypeBool: {
-      const char format[2] = "b";
-      success = PyArg_Parse(py_return.get(), format, (bool *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeShortInt: {
-      const char format[2] = "h";
-      success = PyArg_Parse(py_return.get(), format, (short *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeShortIntUnsigned: {
-      const char format[2] = "H";
-      success =
-          PyArg_Parse(py_return.get(), format, (unsigned short *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeInt: {
-      const char format[2] = "i";
-      success = PyArg_Parse(py_return.get(), format, (int *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeIntUnsigned: {
-      const char format[2] = "I";
-      success = PyArg_Parse(py_return.get(), format, (unsigned int *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeLongInt: {
-      const char format[2] = "l";
-      success = PyArg_Parse(py_return.get(), format, (long *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeLongIntUnsigned: {
-      const char format[2] = "k";
-      success =
-          PyArg_Parse(py_return.get(), format, (unsigned long *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeLongLong: {
-      const char format[2] = "L";
-      success = PyArg_Parse(py_return.get(), format, (long long *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeLongLongUnsigned: {
-      const char format[2] = "K";
-      success =
-          PyArg_Parse(py_return.get(), format, (unsigned long long *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeFloat: {
-      const char format[2] = "f";
-      success = PyArg_Parse(py_return.get(), format, (float *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeDouble: {
-      const char format[2] = "d";
-      success = PyArg_Parse(py_return.get(), format, (double *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeChar: {
-      const char format[2] = "c";
-      success = PyArg_Parse(py_return.get(), format, (char *)ret_value);
-      break;
-    }
-    case eScriptReturnTypeOpaqueObject: {
-      success = true;
-      PyObject *saved_value = py_return.get();
-      Py_XINCREF(saved_value);
-      *((PyObject **)ret_value) = saved_value;
-      break;
-    }
-    }
+  PythonObject py_return = std::move(maybe_py_return.get());
+  assert(py_return.IsValid());
 
-    ret_success = success;
+  switch (return_type) {
+  case eScriptReturnTypeCharPtr: // "char *"
+  {
+    const char format[3] = "s#";
+    return PyArg_Parse(py_return.get(), format, (char **)ret_value);
+  }
+  case eScriptReturnTypeCharStrOrNone: // char* or NULL if py_return ==
+                                       // Py_None
+  {
+    const char format[3] = "z";
+    return PyArg_Parse(py_return.get(), format, (char **)ret_value);
+  }
+  case eScriptReturnTypeBool: {
+    const char format[2] = "b";
+    return PyArg_Parse(py_return.get(), format, (bool *)ret_value);
+  }
+  case eScriptReturnTypeShortInt: {
+    const char format[2] = "h";
+    return PyArg_Parse(py_return.get(), format, (short *)ret_value);
+  }
+  case eScriptReturnTypeShortIntUnsigned: {
+    const char format[2] = "H";
+    return PyArg_Parse(py_return.get(), format, (unsigned short *)ret_value);
+  }
+  case eScriptReturnTypeInt: {
+    const char format[2] = "i";
+    return PyArg_Parse(py_return.get(), format, (int *)ret_value);
+  }
+  case eScriptReturnTypeIntUnsigned: {
+    const char format[2] = "I";
+    return PyArg_Parse(py_return.get(), format, (unsigned int *)ret_value);
+  }
+  case eScriptReturnTypeLongInt: {
+    const char format[2] = "l";
+    return PyArg_Parse(py_return.get(), format, (long *)ret_value);
+  }
+  case eScriptReturnTypeLongIntUnsigned: {
+    const char format[2] = "k";
+    return PyArg_Parse(py_return.get(), format, (unsigned long *)ret_value);
+  }
+  case eScriptReturnTypeLongLong: {
+    const char format[2] = "L";
+    return PyArg_Parse(py_return.get(), format, (long long *)ret_value);
+  }
+  case eScriptReturnTypeLongLongUnsigned: {
+    const char format[2] = "K";
+    return PyArg_Parse(py_return.get(), format,
+                       (unsigned long long *)ret_value);
+  }
+  case eScriptReturnTypeFloat: {
+    const char format[2] = "f";
+    return PyArg_Parse(py_return.get(), format, (float *)ret_value);
+  }
+  case eScriptReturnTypeDouble: {
+    const char format[2] = "d";
+    return PyArg_Parse(py_return.get(), format, (double *)ret_value);
+  }
+  case eScriptReturnTypeChar: {
+    const char format[2] = "c";
+    return PyArg_Parse(py_return.get(), format, (char *)ret_value);
+  }
+  case eScriptReturnTypeOpaqueObject: {
+    *((PyObject **)ret_value) = py_return.release();
+    return true;
   }
-
-  py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
-  if (py_error.IsValid()) {
-    ret_success = false;
-    if (options.GetMaskoutErrors()) {
-      if (PyErr_GivenExceptionMatches(py_error.get(), PyExc_SyntaxError))
-        PyErr_Print();
-      PyErr_Clear();
-    }
   }
-
-  return ret_success;
 }
 
 Status ScriptInterpreterPythonImpl::ExecuteMultipleLines(
     const char *in_string, const ExecuteScriptOptions &options) {
-  Status error;
+
+  if (in_string == nullptr)
+    return Status();
 
   Locker locker(this,
                 Locker::AcquireLock | Locker::InitSession |
@@ -1192,51 +1153,34 @@
                     Locker::NoSTDIN,
                 Locker::FreeAcquiredLock | Locker::TearDownSession);
 
-  PythonObject return_value;
-  PythonObject &main_module = GetMainModule();
-  PythonDictionary globals(PyRefType::Borrowed,
-                           PyModule_GetDict(main_module.get()));
-  PythonObject py_error;
+  PythonModule &main_module = GetMainModule();
+  PythonDictionary globals = main_module.GetDictionary();
 
   PythonDictionary locals = GetSessionDictionary();
-
   if (!locals.IsValid())
     locals = unwrapIgnoringErrors(
         As<PythonDictionary>(globals.GetAttribute(m_dictionary_name)));
-
   if (!locals.IsValid())
     locals = globals;
 
-  py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
-  if (py_error.IsValid())
-    PyErr_Clear();
-
-  if (in_string != nullptr) {
-    PythonObject code_object;
-    code_object.Reset(PyRefType::Owned,
-                      Py_CompileString(in_string, "temp.py", Py_file_input));
-
-    if (code_object.IsValid()) {
-// In Python 2.x, PyEval_EvalCode takes a PyCodeObject, but in Python 3.x, it
-// takes a PyObject.  They are convertible (hence the function
-// PyCode_Check(PyObject*), so we have to do the cast for Python 2.x
-#if PY_MAJOR_VERSION >= 3
-      PyObject *py_code_obj = code_object.get();
-#else
-      PyCodeObject *py_code_obj =
-          reinterpret_cast<PyCodeObject *>(code_object.get());
-#endif
-      return_value.Reset(
-          PyRefType::Owned,
-          PyEval_EvalCode(py_code_obj, globals.get(), locals.get()));
-    }
+  Expected<PythonObject> return_value =
+      runStringMultiLine(in_string, globals, locals);
+  if (!return_value) {
+    Status error;
+    llvm::handleAllErrors(
+        return_value.takeError(),
+        [&](PythonException &E) {
+          error.SetErrorString(E.ReadBacktrace());
+          if (!options.GetMaskoutErrors())
+            E.Restore();
+        },
+        [&](const llvm::ErrorInfoBase &E) {
+          error.SetErrorString(E.message());
+        });
+    return error;
   }
 
-  PythonExceptionState exception_state(!options.GetMaskoutErrors());
-  if (exception_state.IsError())
-    error.SetErrorString(exception_state.Format().c_str());
-
-  return error;
+  return Status();
 }
 
 void ScriptInterpreterPythonImpl::CollectDataForBreakpointCommandCallback(
@@ -2034,9 +1978,13 @@
   Locker py_lock(this,
                  Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
   TargetSP target_sp(target->shared_from_this());
-  reply_pyobj.Reset(PyRefType::Owned,
-                    (PyObject *)LLDBSWIGPython_GetDynamicSetting(
-                        generic->GetValue(), setting_name, target_sp));
+
+  auto setting = (PyObject *)LLDBSWIGPython_GetDynamicSetting(
+      generic->GetValue(), setting_name, target_sp);
+  if (setting)
+    reply_pyobj = Take<PythonObject>(setting);
+  else
+    reply_pyobj.Reset();
 
   PythonDictionary py_dict(PyRefType::Borrowed, reply_pyobj.get());
   return py_dict.CreateStructuredDictionary();
Index: lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h
+++ /dev/null
@@ -1,56 +0,0 @@
-//===-- PythonExceptionState.h ----------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONEXCEPTIONSTATE_H
-#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONEXCEPTIONSTATE_H
-
-#ifndef LLDB_DISABLE_PYTHON
-
-#include "PythonDataObjects.h"
-
-namespace lldb_private {
-
-class PythonExceptionState {
-public:
-  explicit PythonExceptionState(bool restore_on_exit);
-  ~PythonExceptionState();
-
-  void Acquire(bool restore_on_exit);
-
-  void Restore();
-
-  void Discard();
-
-  void Reset();
-
-  static bool HasErrorOccurred();
-
-  bool IsError() const;
-
-  PythonObject GetType() const;
-
-  PythonObject GetValue() const;
-
-  PythonObject GetTraceback() const;
-
-  std::string Format() const;
-
-private:
-  std::string ReadBacktrace() const;
-
-  bool m_restore_on_exit;
-
-  PythonObject m_type;
-  PythonObject m_value;
-  PythonObject m_traceback;
-};
-}
-
-#endif
-
-#endif
Index: lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-//===-- PythonExceptionState.cpp --------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#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
Index: lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
@@ -163,6 +163,20 @@
   operator const char *() { return str; }
 };
 
+inline llvm::Error nullDeref() {
+  return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                 "A NULL PyObject* was dereferenced");
+}
+
+inline llvm::Error exception(const char *s = nullptr) {
+  return llvm::make_error<PythonException>(s);
+}
+
+inline llvm::Error keyError() {
+  return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                 "key not in dict");
+}
+
 } // namespace python
 
 enum class PyInitialValue { Invalid, Empty };
@@ -179,6 +193,11 @@
   static auto get(long long value) { return value; }
 };
 
+template <> struct PythonFormat<PyObject *> {
+  static constexpr char format = 'O';
+  static auto get(PyObject *value) { return value; }
+};
+
 template <typename T>
 struct PythonFormat<
     T, typename std::enable_if<std::is_base_of<PythonObject, T>::value>::type> {
@@ -190,8 +209,14 @@
 public:
   PythonObject() : m_py_obj(nullptr) {}
 
-  PythonObject(PyRefType type, PyObject *py_obj) : m_py_obj(nullptr) {
-    Reset(type, py_obj);
+  PythonObject(PyRefType type, PyObject *py_obj) {
+    m_py_obj = py_obj;
+    // If this is a borrowed reference, we need to convert it to
+    // an owned reference by incrementing it.  If it is an owned
+    // reference (for example the caller allocated it with PyDict_New()
+    // then we must *not* increment it.
+    if (m_py_obj && Py_IsInitialized() && type == PyRefType::Borrowed)
+      Py_XINCREF(m_py_obj);
   }
 
   PythonObject(const PythonObject &rhs)
@@ -210,23 +235,6 @@
     m_py_obj = nullptr;
   }
 
-  void Reset(PyRefType type, PyObject *py_obj) {
-    if (py_obj == m_py_obj)
-      return;
-
-    if (Py_IsInitialized())
-      Py_XDECREF(m_py_obj);
-
-    m_py_obj = py_obj;
-
-    // If this is a borrowed reference, we need to convert it to
-    // an owned reference by incrementing it.  If it is an owned
-    // reference (for example the caller allocated it with PyDict_New()
-    // then we must *not* increment it.
-    if (m_py_obj && Py_IsInitialized() && type == PyRefType::Borrowed)
-      Py_XINCREF(m_py_obj);
-  }
-
   void Dump() const {
     if (m_py_obj)
       _PyObject_Dump(m_py_obj);
@@ -292,17 +300,6 @@
   StructuredData::ObjectSP CreateStructuredObject() const;
 
 protected:
-  static llvm::Error nullDeref() {
-    return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                   "A NULL PyObject* was dereferenced");
-  }
-  static llvm::Error exception(const char *s = nullptr) {
-    return llvm::make_error<PythonException>(s);
-  }
-  static llvm::Error keyError() {
-    return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                   "key not in dict");
-  }
 
 #if PY_MAJOR_VERSION < 3
   // The python 2 API declares some arguments as char* that should
@@ -316,6 +313,7 @@
   template <typename... T>
   llvm::Expected<PythonObject> CallMethod(const char *name,
                                           const T &... t) const {
+    using namespace python;
     const char format[] = {'(', PythonFormat<T>::format..., ')', 0};
     PyObject *obj =
         PyObject_CallMethod(m_py_obj, py2_const_cast(name),
@@ -327,6 +325,7 @@
 
   template <typename... T>
   llvm::Expected<PythonObject> Call(const T &... t) const {
+    using namespace python;
     const char format[] = {'(', PythonFormat<T>::format..., ')', 0};
     PyObject *obj = PyObject_CallFunction(m_py_obj, py2_const_cast(format),
                                           PythonFormat<T>::get(t)...);
@@ -346,6 +345,7 @@
   }
 
   llvm::Expected<bool> IsTrue() {
+    using namespace python;
     if (!m_py_obj)
       return nullDeref();
     int r = PyObject_IsTrue(m_py_obj);
@@ -355,6 +355,7 @@
   }
 
   llvm::Expected<long long> AsLongLong() {
+    using namespace python;
     if (!m_py_obj)
       return nullDeref();
     assert(!PyErr_Occurred());
@@ -365,6 +366,7 @@
   }
 
   llvm::Expected<bool> IsInstance(const PythonObject &cls) {
+    using namespace python;
     if (!m_py_obj || !cls.IsValid())
       return nullDeref();
     int r = PyObject_IsInstance(m_py_obj, cls.get());
@@ -405,16 +407,12 @@
   // This can be eliminated once we drop python 2 support.
   static void Convert(PyRefType &type, PyObject *&py_obj) {}
 
-  void Reset() { PythonObject::Reset(); }
-
-  void Reset(PyRefType type, PyObject *py_obj) = delete;
-
   TypedPythonObject(PyRefType type, PyObject *py_obj) {
     if (!py_obj)
       return;
     T::Convert(type, py_obj);
     if (T::Check(py_obj))
-      PythonObject::Reset(type, py_obj);
+      PythonObject::operator=(PythonObject(type, py_obj));
     else if (type == PyRefType::Owned)
       Py_DECREF(py_obj);
   }
@@ -686,6 +684,8 @@
   ~PythonException();
   void log(llvm::raw_ostream &OS) const override;
   std::error_code convertToErrorCode() const override;
+  bool Matches(PyObject *exc) const;
+  std::string ReadBacktrace() const;
 };
 
 // This extracts the underlying T out of an Expected<T> and returns it.
@@ -729,8 +729,55 @@
   llvm::consumeError(expected.takeError());
   return T();
 }
+
+llvm::Expected<PythonObject> runStringOneLine(const llvm::Twine &string,
+                                              const PythonDictionary &globals,
+                                              const PythonDictionary &locals);
+
+llvm::Expected<PythonObject> runStringMultiLine(const llvm::Twine &string,
+                                                const PythonDictionary &globals,
+                                                const PythonDictionary &locals);
+
 } // namespace python
 
+/* Sometimes the best way to interact with a python interpreter is
+ * to run some python code.   You construct a PythonScript with
+ * script string and a function name, and you get a C++ callable
+ * object that calls the python function.
+ *
+ * Example:
+ *
+ * const char script[] = R"(
+ * def foo(x, y):
+ *    ....
+ * )";
+ *
+ * Expected<PythonObject> cpp_foo_wrapper(PythonObject x, PythonObject y) {
+ *   // global is protected by the GIL
+ *   static PythonScript foo(script, "foo")
+ *   return  foo(x, y);
+ * }
+ */
+class PythonScript {
+  const char *script;
+  const char *function_name;
+  PythonCallable function;
+
+  llvm::Error Init();
+
+public:
+  PythonScript(const char *script, const char *name)
+      : script(script), function_name(name), function() {}
+
+  template <typename... Args>
+  llvm::Expected<PythonObject> operator()(Args &&... args) {
+    llvm::Error e = Init();
+    if (e)
+      return std::move(e);
+    return function.Call(std::forward<Args>(args)...);
+  }
+};
+
 } // namespace lldb_private
 
 #endif
Index: lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
@@ -261,8 +261,7 @@
 
 void PythonBytes::SetBytes(llvm::ArrayRef<uint8_t> bytes) {
   const char *data = reinterpret_cast<const char *>(bytes.data());
-  PyObject *py_bytes = PyBytes_FromStringAndSize(data, bytes.size());
-  PythonObject::Reset(PyRefType::Owned, py_bytes);
+  *this = Take<PythonBytes>(PyBytes_FromStringAndSize(data, bytes.size()));
 }
 
 StructuredData::StringSP PythonBytes::CreateStructuredString() const {
@@ -486,7 +485,7 @@
 }
 
 void PythonInteger::SetInteger(int64_t value) {
-  PythonObject::Reset(PyRefType::Owned, PyLong_FromLongLong(value));
+  *this = Take<PythonInteger>(PyLong_FromLongLong(value));
 }
 
 StructuredData::IntegerSP PythonInteger::CreateStructuredInteger() const {
@@ -510,7 +509,7 @@
 }
 
 void PythonBoolean::SetValue(bool value) {
-  PythonObject::Reset(PyRefType::Owned, PyBool_FromLong(value));
+  *this = Take<PythonBoolean>(PyBool_FromLong(value));
 }
 
 StructuredData::BooleanSP PythonBoolean::CreateStructuredBoolean() const {
@@ -853,27 +852,8 @@
 #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
 
   // this global is protected by the GIL
-  static PythonCallable get_arg_info;
-
-  if (!get_arg_info.IsValid()) {
-    PythonDictionary globals(PyInitialValue::Empty);
-
-    auto builtins = PythonModule::BuiltinsModule();
-    Error error = globals.SetItem("__builtins__", builtins);
-    if (error)
-      return std::move(error);
-    PyObject *o = PyRun_String(get_arg_info_script, Py_file_input,
-                               globals.get(), globals.get());
-    if (!o)
-      return exception();
-    Take<PythonObject>(o);
-    auto function = As<PythonCallable>(globals.GetItem("get_arg_info"));
-    if (!function)
-      return function.takeError();
-    get_arg_info = std::move(function.get());
-  }
-
-  Expected<PythonObject> pyarginfo = get_arg_info.Call(*this);
+  static PythonScript get_arg_info(get_arg_info_script, "get_arg_info");
+  Expected<PythonObject> pyarginfo = get_arg_info(*this);
   if (!pyarginfo)
     return pyarginfo.takeError();
   result.count = cantFail(As<long long>(pyarginfo.get().GetAttribute("count")));
@@ -1056,6 +1036,39 @@
   return llvm::inconvertibleErrorCode();
 }
 
+bool PythonException::Matches(PyObject *exc) const {
+  return PyErr_GivenExceptionMatches(m_exception_type, exc);
+}
+
+const char read_exception_script[] = R"(
+import sys
+from traceback import print_exception
+if sys.version_info.major < 3:
+  from StringIO import StringIO
+else:
+  from io import StringIO
+def read_exception(exc_type, exc_value, tb):
+  f = StringIO()
+  print_exception(exc_type, exc_value, tb, file=f)
+  return f.getvalue()
+)";
+
+std::string PythonException::ReadBacktrace() const {
+
+  // global is protected by the GIL
+  static PythonScript read_exception(read_exception_script, "read_exception");
+
+  Expected<std::string> backtrace = As<std::string>(
+      read_exception(m_exception_type, m_exception, m_traceback));
+
+  if (!backtrace) {
+    llvm::consumeError(backtrace.takeError());
+    return "backtrace unavailable";
+  }
+
+  return std::move(backtrace.get());
+}
+
 char PythonException::ID = 0;
 
 llvm::Expected<File::OpenOptions>
@@ -1496,4 +1509,69 @@
   return Take<PythonFile>(file_obj);
 }
 
+Error PythonScript::Init() {
+  if (!function.IsValid()) {
+    PythonDictionary globals(PyInitialValue::Empty);
+
+    auto builtins = PythonModule::BuiltinsModule();
+    Error error = globals.SetItem("__builtins__", builtins);
+    if (error)
+      return error;
+    PyObject *o =
+        PyRun_String(script, Py_file_input, globals.get(), globals.get());
+    if (!o)
+      return exception();
+    Take<PythonObject>(o);
+    auto f = As<PythonCallable>(globals.GetItem(function_name));
+    if (!f)
+      return f.takeError();
+    function = std::move(f.get());
+  }
+  return Error::success();
+}
+
+llvm::Expected<PythonObject>
+python::runStringOneLine(const llvm::Twine &string,
+                         const PythonDictionary &globals,
+                         const PythonDictionary &locals) {
+  if (!globals.IsValid() || !locals.IsValid())
+    return nullDeref();
+
+  PyObject *code =
+      Py_CompileString(NullTerminated(string), "<string>", Py_eval_input);
+  if (!code) {
+    PyErr_Clear();
+    code =
+        Py_CompileString(NullTerminated(string), "<string>", Py_single_input);
+  }
+  if (!code)
+    return exception();
+  auto code_ref = Take<PythonObject>(code);
+
+#if PY_MAJOR_VERSION < 3
+  PyObject *result =
+      PyEval_EvalCode((PyCodeObject *)code, globals.get(), locals.get());
+#else
+  PyObject *result = PyEval_EvalCode(code, globals.get(), locals.get());
+#endif
+
+  if (!result)
+    return exception();
+
+  return Take<PythonObject>(result);
+}
+
+llvm::Expected<PythonObject>
+python::runStringMultiLine(const llvm::Twine &string,
+                           const PythonDictionary &globals,
+                           const PythonDictionary &locals) {
+  if (!globals.IsValid() || !locals.IsValid())
+    return nullDeref();
+  PyObject *result = PyRun_String(NullTerminated(string), Py_file_input,
+                                  globals.get(), locals.get());
+  if (!result)
+    return exception();
+  return Take<PythonObject>(result);
+}
+
 #endif
Index: lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
+++ lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt
@@ -5,7 +5,6 @@
 
 add_lldb_library(lldbPluginScriptInterpreterPython PLUGIN
   PythonDataObjects.cpp
-  PythonExceptionState.cpp
   ScriptInterpreterPython.cpp
 
   LINK_LIBS
Index: lldb/include/lldb/Interpreter/ScriptInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -65,6 +65,9 @@
 
     bool GetSetLLDBGlobals() const { return m_set_lldb_globals; }
 
+    /* If this is true then any exceptions raised by the script will be
+     * cleared with PyErr_Clear().   If false then they will be left for
+     * the caller to clean up */
     bool GetMaskoutErrors() const { return m_maskout_errors; }
 
     ExecuteScriptOptions &SetEnableIO(bool enable) {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to