exception handling in PythonDataObjects.

Summary:
Python APIs nearly all can return an exception.   They do this
by returning NULL, or -1, or some such value and setting
the exception state with PyErr_Set*().   Exceptions must be
handled before further python API functions are called.   Failure
to do so will result in asserts on debug builds of python.
It will also sometimes, but not usually result in crashes of
release builds.

Nearly everything in PythonDataObjects.h needs to be updated
to account for this.   This patch doesn't fix everything,
but it does introduce some new methods using Expected<>
return types that are safe to use.

split off from https://reviews.llvm.org/D68188

Reviewers: JDevlieghere, jasonmolenda, labath, zturner

Reviewed By: labath

Subscribers: lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D68547

llvm-svn: 374094
This commit is contained in:
Lawrence D'Anna
2019-10-08 17:56:18 +00:00
parent 70d2e5427e
commit 085328eeee
2 changed files with 392 additions and 36 deletions

View File

@@ -18,6 +18,7 @@
#include "lldb/Host/File.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Interpreter/ScriptInterpreter.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Stream.h"
#include "llvm/ADT/StringSwitch.h"
@@ -28,6 +29,22 @@
using namespace lldb_private;
using namespace lldb;
using namespace lldb_private::python;
using llvm::Error;
using llvm::Expected;
template <> Expected<bool> python::As<bool>(Expected<PythonObject> &&obj) {
if (!obj)
return obj.takeError();
return obj.get().IsTrue();
}
template <>
Expected<long long> python::As<long long>(Expected<PythonObject> &&obj) {
if (!obj)
return obj.takeError();
return obj.get().AsLongLong();
}
void StructuredPythonObject::Serialize(llvm::json::OStream &s) const {
s.value(llvm::formatv("Python Obj: {0:X}", GetValue()).str());
@@ -167,12 +184,6 @@ PythonObject PythonObject::GetAttributeValue(llvm::StringRef attr) const {
PyObject_GetAttr(m_py_obj, py_attr.get()));
}
bool PythonObject::IsNone() const { return m_py_obj == Py_None; }
bool PythonObject::IsValid() const { return m_py_obj != nullptr; }
bool PythonObject::IsAllocated() const { return IsValid() && !IsNone(); }
StructuredData::ObjectSP PythonObject::CreateStructuredObject() const {
switch (GetObjectType()) {
case PyObjectType::Dictionary:
@@ -334,6 +345,17 @@ StructuredData::StringSP PythonByteArray::CreateStructuredString() const {
// PythonString
Expected<PythonString> PythonString::FromUTF8(llvm::StringRef string) {
#if PY_MAJOR_VERSION >= 3
PyObject *str = PyUnicode_FromStringAndSize(string.data(), string.size());
#else
PyObject *str = PyString_FromStringAndSize(string.data(), string.size());
#endif
if (!str)
return llvm::make_error<PythonException>();
return Take<PythonString>(str);
}
PythonString::PythonString(PyRefType type, PyObject *py_obj) : PythonObject() {
Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a string
}
@@ -342,10 +364,6 @@ PythonString::PythonString(llvm::StringRef string) : PythonObject() {
SetString(string);
}
PythonString::PythonString(const char *string) : PythonObject() {
SetString(llvm::StringRef(string));
}
PythonString::PythonString() : PythonObject() {}
PythonString::~PythonString() {}
@@ -376,8 +394,12 @@ void PythonString::Reset(PyRefType type, PyObject *py_obj) {
// In Python 2, Don't store PyUnicode objects directly, because we need
// access to their underlying character buffers which Python 2 doesn't
// provide.
if (PyUnicode_Check(py_obj))
result.Reset(PyRefType::Owned, PyUnicode_AsUTF8String(result.get()));
if (PyUnicode_Check(py_obj)) {
PyObject *s = PyUnicode_AsUTF8String(result.get());
if (s == NULL)
PyErr_Clear();
result.Reset(PyRefType::Owned, s);
}
#endif
// Calling PythonObject::Reset(const PythonObject&) will lead to stack
// overflow since it calls back into the virtual implementation.
@@ -385,8 +407,17 @@ void PythonString::Reset(PyRefType type, PyObject *py_obj) {
}
llvm::StringRef PythonString::GetString() const {
auto s = AsUTF8();
if (!s) {
llvm::consumeError(s.takeError());
return llvm::StringRef("");
}
return s.get();
}
Expected<llvm::StringRef> PythonString::AsUTF8() const {
if (!IsValid())
return llvm::StringRef();
return nullDeref();
Py_ssize_t size;
const char *data;
@@ -394,10 +425,16 @@ llvm::StringRef PythonString::GetString() const {
#if PY_MAJOR_VERSION >= 3
data = PyUnicode_AsUTF8AndSize(m_py_obj, &size);
#else
char *c;
PyString_AsStringAndSize(m_py_obj, &c, &size);
char *c = NULL;
int r = PyString_AsStringAndSize(m_py_obj, &c, &size);
if (r < 0)
c = NULL;
data = c;
#endif
if (!data)
return exception();
return llvm::StringRef(data, size);
}
@@ -413,13 +450,13 @@ size_t PythonString::GetSize() const {
}
void PythonString::SetString(llvm::StringRef string) {
#if PY_MAJOR_VERSION >= 3
PyObject *unicode = PyUnicode_FromStringAndSize(string.data(), string.size());
PythonObject::Reset(PyRefType::Owned, unicode);
#else
PyObject *str = PyString_FromStringAndSize(string.data(), string.size());
PythonObject::Reset(PyRefType::Owned, str);
#endif
auto s = FromUTF8(string);
if (!s) {
llvm::consumeError(s.takeError());
Reset();
} else {
PythonObject::Reset(std::move(s.get()));
}
}
StructuredData::StringSP PythonString::CreateStructuredString() const {
@@ -826,9 +863,23 @@ PythonModule PythonModule::AddModule(llvm::StringRef module) {
return PythonModule(PyRefType::Borrowed, PyImport_AddModule(str.c_str()));
}
PythonModule PythonModule::ImportModule(llvm::StringRef module) {
std::string str = module.str();
return PythonModule(PyRefType::Owned, PyImport_ImportModule(str.c_str()));
Expected<PythonModule> PythonModule::Import(const char *name) {
PyObject *mod = PyImport_ImportModule(name);
if (!mod)
return exception();
return Take<PythonModule>(mod);
}
Expected<PythonObject> PythonModule::Get(const char *name) {
if (!IsValid())
return nullDeref();
PyObject *dict = PyModule_GetDict(m_py_obj);
if (!dict)
return exception();
PyObject *item = PyDict_GetItemString(dict, name);
if (!item)
return exception();
return Retain<PythonObject>(item);
}
bool PythonModule::Check(PyObject *py_obj) {
@@ -1045,4 +1096,58 @@ FileUP PythonFile::GetUnderlyingFile() const {
return file;
}
const char *PythonException::toCString() const {
if (!m_repr_bytes)
return "unknown exception";
return PyBytes_AS_STRING(m_repr_bytes);
}
PythonException::PythonException(const char *caller) {
assert(PyErr_Occurred());
m_exception_type = m_exception = m_traceback = m_repr_bytes = NULL;
PyErr_Fetch(&m_exception_type, &m_exception, &m_traceback);
PyErr_NormalizeException(&m_exception_type, &m_exception, &m_traceback);
PyErr_Clear();
if (m_exception) {
PyObject *repr = PyObject_Repr(m_exception);
if (repr) {
m_repr_bytes = PyUnicode_AsEncodedString(repr, "utf-8", nullptr);
if (!m_repr_bytes) {
PyErr_Clear();
}
Py_XDECREF(repr);
} else {
PyErr_Clear();
}
}
Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT);
if (caller)
LLDB_LOGF(log, "%s failed with exception: %s", caller, toCString());
else
LLDB_LOGF(log, "python exception: %s", toCString());
}
void PythonException::Restore() {
if (m_exception_type && m_exception) {
PyErr_Restore(m_exception_type, m_exception, m_traceback);
} else {
PyErr_SetString(PyExc_Exception, toCString());
}
m_exception_type = m_exception = m_traceback = NULL;
}
PythonException::~PythonException() {
Py_XDECREF(m_exception_type);
Py_XDECREF(m_exception);
Py_XDECREF(m_traceback);
Py_XDECREF(m_repr_bytes);
}
void PythonException::log(llvm::raw_ostream &OS) const { OS << toCString(); }
std::error_code PythonException::convertToErrorCode() const {
return llvm::inconvertibleErrorCode();
}
char PythonException::ID = 0;
#endif