diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-10-23 17:53:01 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-10-23 17:53:01 +0000 |
commit | ead246455adf1a215ec2715dad6533073a6beb4e (patch) | |
tree | f3f97a47d77053bf96fe74cdbd6fae74380e8a92 /source/Plugins/ScriptInterpreter | |
parent | fdb00c4408990a0a63ef7f496d809ce59f263bc5 (diff) | |
download | src-vendor/lldb.tar.gz src-vendor/lldb.zip |
Vendor import of stripped lldb trunk r375505, the last commit before thevendor/lldb/lldb-trunk-r375505vendor/lldb
upstream Subversion repository was made read-only, and the LLVM project
migrated to GitHub:
https://llvm.org/svn/llvm-project/lldb/trunk@375505
Notes
Notes:
svn path=/vendor/lldb/dist/; revision=353952
svn path=/vendor/lldb/lldb-r375505/; revision=353953; tag=vendor/lldb/lldb-trunk-r375505
Diffstat (limited to 'source/Plugins/ScriptInterpreter')
8 files changed, 1617 insertions, 1101 deletions
diff --git a/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp b/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp index 7b9598379553..3517f831970d 100644 --- a/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp +++ b/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp @@ -28,13 +28,13 @@ ScriptInterpreterNone::~ScriptInterpreterNone() {} bool ScriptInterpreterNone::ExecuteOneLine(llvm::StringRef command, CommandReturnObject *, const ExecuteScriptOptions &) { - m_debugger.GetErrorFile()->PutCString( + m_debugger.GetErrorStream().PutCString( "error: there is no embedded script interpreter in this mode.\n"); return false; } void ScriptInterpreterNone::ExecuteInterpreterLoop() { - m_debugger.GetErrorFile()->PutCString( + m_debugger.GetErrorStream().PutCString( "error: there is no embedded script interpreter in this mode.\n"); } diff --git a/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index 29dd037efd86..70d93424fdec 100644 --- a/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -18,9 +18,11 @@ #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" +#include "llvm/Support/Casting.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errno.h" @@ -28,9 +30,41 @@ using namespace lldb_private; using namespace lldb; +using namespace lldb_private::python; +using llvm::cantFail; +using llvm::Error; +using llvm::Expected; +using llvm::Twine; -void StructuredPythonObject::Dump(Stream &s, bool pretty_print) const { - s << "Python Obj: 0x" << GetValue(); +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(); +} + +template <> +Expected<std::string> python::As<std::string>(Expected<PythonObject> &&obj) { + if (!obj) + return obj.takeError(); + PyObject *str_obj = PyObject_Str(obj.get().get()); + if (!obj) + return llvm::make_error<PythonException>(); + auto str = Take<PythonString>(str_obj); + auto utf8 = str.AsUTF8(); + if (!utf8) + return utf8.takeError(); + return utf8.get(); +} + +void StructuredPythonObject::Serialize(llvm::json::OStream &s) const { + s.value(llvm::formatv("Python Obj: {0:X}", GetValue()).str()); } // PythonObject @@ -167,12 +201,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: @@ -201,43 +229,19 @@ StructuredData::ObjectSP PythonObject::CreateStructuredObject() const { } // PythonString -PythonBytes::PythonBytes() : PythonObject() {} -PythonBytes::PythonBytes(llvm::ArrayRef<uint8_t> bytes) : PythonObject() { - SetBytes(bytes); -} +PythonBytes::PythonBytes(llvm::ArrayRef<uint8_t> bytes) { SetBytes(bytes); } -PythonBytes::PythonBytes(const uint8_t *bytes, size_t length) : PythonObject() { +PythonBytes::PythonBytes(const uint8_t *bytes, size_t length) { SetBytes(llvm::ArrayRef<uint8_t>(bytes, length)); } -PythonBytes::PythonBytes(PyRefType type, PyObject *py_obj) : PythonObject() { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a string -} - -PythonBytes::~PythonBytes() {} - bool PythonBytes::Check(PyObject *py_obj) { if (!py_obj) return false; return PyBytes_Check(py_obj); } -void PythonBytes::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonBytes::Check(py_obj)) { - PythonObject::Reset(); - return; - } - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); -} - llvm::ArrayRef<uint8_t> PythonBytes::GetBytes() const { if (!IsValid()) return llvm::ArrayRef<uint8_t>(); @@ -257,8 +261,7 @@ size_t PythonBytes::GetSize() const { 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 { @@ -275,39 +278,15 @@ PythonByteArray::PythonByteArray(llvm::ArrayRef<uint8_t> bytes) PythonByteArray::PythonByteArray(const uint8_t *bytes, size_t length) { const char *str = reinterpret_cast<const char *>(bytes); - Reset(PyRefType::Owned, PyByteArray_FromStringAndSize(str, length)); -} - -PythonByteArray::PythonByteArray(PyRefType type, PyObject *o) { - Reset(type, o); + *this = Take<PythonByteArray>(PyByteArray_FromStringAndSize(str, length)); } -PythonByteArray::PythonByteArray(const PythonBytes &object) - : PythonObject(object) {} - -PythonByteArray::~PythonByteArray() {} - bool PythonByteArray::Check(PyObject *py_obj) { if (!py_obj) return false; return PyByteArray_Check(py_obj); } -void PythonByteArray::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonByteArray::Check(py_obj)) { - PythonObject::Reset(); - return; - } - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); -} - llvm::ArrayRef<uint8_t> PythonByteArray::GetBytes() const { if (!IsValid()) return llvm::ArrayRef<uint8_t>(); @@ -334,21 +313,18 @@ StructuredData::StringSP PythonByteArray::CreateStructuredString() const { // PythonString -PythonString::PythonString(PyRefType type, PyObject *py_obj) : PythonObject() { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a string -} - -PythonString::PythonString(llvm::StringRef string) : PythonObject() { - SetString(string); -} - -PythonString::PythonString(const char *string) : PythonObject() { - SetString(llvm::StringRef(string)); +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() : PythonObject() {} - -PythonString::~PythonString() {} +PythonString::PythonString(llvm::StringRef string) { SetString(string); } bool PythonString::Check(PyObject *py_obj) { if (!py_obj) @@ -363,30 +339,40 @@ bool PythonString::Check(PyObject *py_obj) { return false; } -void PythonString::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonString::Check(py_obj)) { - PythonObject::Reset(); - return; - } +void PythonString::Convert(PyRefType &type, PyObject *&py_obj) { #if PY_MAJOR_VERSION < 3 // 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(py_obj); + if (s == nullptr) { + PyErr_Clear(); + if (type == PyRefType::Owned) + Py_DECREF(py_obj); + return; + } + if (type == PyRefType::Owned) + Py_DECREF(py_obj); + else + type = PyRefType::Owned; + py_obj = s; + } #endif - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); } 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 +380,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 +405,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 { + *this = std::move(s.get()); + } } StructuredData::StringSP PythonString::CreateStructuredString() const { @@ -430,18 +422,7 @@ StructuredData::StringSP PythonString::CreateStructuredString() const { // PythonInteger -PythonInteger::PythonInteger() : PythonObject() {} - -PythonInteger::PythonInteger(PyRefType type, PyObject *py_obj) - : PythonObject() { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a integer type -} - -PythonInteger::PythonInteger(int64_t value) : PythonObject() { - SetInteger(value); -} - -PythonInteger::~PythonInteger() {} +PythonInteger::PythonInteger(int64_t value) { SetInteger(value); } bool PythonInteger::Check(PyObject *py_obj) { if (!py_obj) @@ -456,16 +437,7 @@ bool PythonInteger::Check(PyObject *py_obj) { #endif } -void PythonInteger::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonInteger::Check(py_obj)) { - PythonObject::Reset(); - return; - } - +void PythonInteger::Convert(PyRefType &type, PyObject *&py_obj) { #if PY_MAJOR_VERSION < 3 // Always store this as a PyLong, which makes interoperability between Python // 2.x and Python 3.x easier. This is only necessary in 2.x, since 3.x @@ -474,16 +446,23 @@ void PythonInteger::Reset(PyRefType type, PyObject *py_obj) { // Since we converted the original object to a different type, the new // object is an owned object regardless of the ownership semantics // requested by the user. - result.Reset(PyRefType::Owned, PyLong_FromLongLong(PyInt_AsLong(py_obj))); + long long value = PyInt_AsLong(py_obj); + PyObject *l = nullptr; + if (!PyErr_Occurred()) + l = PyLong_FromLongLong(value); + if (l == nullptr) { + PyErr_Clear(); + if (type == PyRefType::Owned) + Py_DECREF(py_obj); + return; + } + if (type == PyRefType::Owned) + Py_DECREF(py_obj); + else + type = PyRefType::Owned; + py_obj = l; } #endif - - assert(PyLong_Check(result.get()) && - "Couldn't get a PyLong from this PyObject"); - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); } int64_t PythonInteger::GetInteger() const { @@ -506,7 +485,7 @@ int64_t PythonInteger::GetInteger() const { } void PythonInteger::SetInteger(int64_t value) { - PythonObject::Reset(PyRefType::Owned, PyLong_FromLongLong(value)); + *this = Take<PythonInteger>(PyLong_FromLongLong(value)); } StructuredData::IntegerSP PythonInteger::CreateStructuredInteger() const { @@ -517,11 +496,6 @@ StructuredData::IntegerSP PythonInteger::CreateStructuredInteger() const { // PythonBoolean -PythonBoolean::PythonBoolean(PyRefType type, PyObject *py_obj) - : PythonObject() { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a boolean type -} - PythonBoolean::PythonBoolean(bool value) { SetValue(value); } @@ -530,27 +504,12 @@ bool PythonBoolean::Check(PyObject *py_obj) { return py_obj ? PyBool_Check(py_obj) : false; } -void PythonBoolean::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonBoolean::Check(py_obj)) { - PythonObject::Reset(); - return; - } - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); -} - bool PythonBoolean::GetValue() const { return m_py_obj ? PyObject_IsTrue(m_py_obj) : false; } void PythonBoolean::SetValue(bool value) { - PythonObject::Reset(PyRefType::Owned, PyBool_FromLong(value)); + *this = Take<PythonBoolean>(PyBool_FromLong(value)); } StructuredData::BooleanSP PythonBoolean::CreateStructuredBoolean() const { @@ -561,42 +520,21 @@ StructuredData::BooleanSP PythonBoolean::CreateStructuredBoolean() const { // PythonList -PythonList::PythonList(PyInitialValue value) : PythonObject() { +PythonList::PythonList(PyInitialValue value) { if (value == PyInitialValue::Empty) - Reset(PyRefType::Owned, PyList_New(0)); -} - -PythonList::PythonList(int list_size) : PythonObject() { - Reset(PyRefType::Owned, PyList_New(list_size)); + *this = Take<PythonList>(PyList_New(0)); } -PythonList::PythonList(PyRefType type, PyObject *py_obj) : PythonObject() { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a list +PythonList::PythonList(int list_size) { + *this = Take<PythonList>(PyList_New(list_size)); } -PythonList::~PythonList() {} - bool PythonList::Check(PyObject *py_obj) { if (!py_obj) return false; return PyList_Check(py_obj); } -void PythonList::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonList::Check(py_obj)) { - PythonObject::Reset(); - return; - } - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); -} - uint32_t PythonList::GetSize() const { if (IsValid()) return PyList_GET_SIZE(m_py_obj); @@ -638,17 +576,13 @@ StructuredData::ArraySP PythonList::CreateStructuredArray() const { // PythonTuple -PythonTuple::PythonTuple(PyInitialValue value) : PythonObject() { +PythonTuple::PythonTuple(PyInitialValue value) { if (value == PyInitialValue::Empty) - Reset(PyRefType::Owned, PyTuple_New(0)); -} - -PythonTuple::PythonTuple(int tuple_size) : PythonObject() { - Reset(PyRefType::Owned, PyTuple_New(tuple_size)); + *this = Take<PythonTuple>(PyTuple_New(0)); } -PythonTuple::PythonTuple(PyRefType type, PyObject *py_obj) : PythonObject() { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a tuple +PythonTuple::PythonTuple(int tuple_size) { + *this = Take<PythonTuple>(PyTuple_New(tuple_size)); } PythonTuple::PythonTuple(std::initializer_list<PythonObject> objects) { @@ -674,29 +608,12 @@ PythonTuple::PythonTuple(std::initializer_list<PyObject *> objects) { } } -PythonTuple::~PythonTuple() {} - bool PythonTuple::Check(PyObject *py_obj) { if (!py_obj) return false; return PyTuple_Check(py_obj); } -void PythonTuple::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonTuple::Check(py_obj)) { - PythonObject::Reset(); - return; - } - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); -} - uint32_t PythonTuple::GetSize() const { if (IsValid()) return PyTuple_GET_SIZE(m_py_obj); @@ -730,18 +647,11 @@ StructuredData::ArraySP PythonTuple::CreateStructuredArray() const { // PythonDictionary -PythonDictionary::PythonDictionary(PyInitialValue value) : PythonObject() { +PythonDictionary::PythonDictionary(PyInitialValue value) { if (value == PyInitialValue::Empty) - Reset(PyRefType::Owned, PyDict_New()); + *this = Take<PythonDictionary>(PyDict_New()); } -PythonDictionary::PythonDictionary(PyRefType type, PyObject *py_obj) - : PythonObject() { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a dictionary -} - -PythonDictionary::~PythonDictionary() {} - bool PythonDictionary::Check(PyObject *py_obj) { if (!py_obj) return false; @@ -749,21 +659,6 @@ bool PythonDictionary::Check(PyObject *py_obj) { return PyDict_Check(py_obj); } -void PythonDictionary::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonDictionary::Check(py_obj)) { - PythonObject::Reset(); - return; - } - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); -} - uint32_t PythonDictionary::GetSize() const { if (IsValid()) return PyDict_Size(m_py_obj); @@ -777,16 +672,66 @@ PythonList PythonDictionary::GetKeys() const { } PythonObject PythonDictionary::GetItemForKey(const PythonObject &key) const { - if (IsAllocated() && key.IsValid()) - return PythonObject(PyRefType::Borrowed, - PyDict_GetItem(m_py_obj, key.get())); - return PythonObject(); + auto item = GetItem(key); + if (!item) { + llvm::consumeError(item.takeError()); + return PythonObject(); + } + return std::move(item.get()); +} + +Expected<PythonObject> +PythonDictionary::GetItem(const PythonObject &key) const { + if (!IsValid()) + return nullDeref(); +#if PY_MAJOR_VERSION >= 3 + PyObject *o = PyDict_GetItemWithError(m_py_obj, key.get()); + if (PyErr_Occurred()) + return exception(); +#else + PyObject *o = PyDict_GetItem(m_py_obj, key.get()); +#endif + if (!o) + return keyError(); + return Retain<PythonObject>(o); +} + +Expected<PythonObject> PythonDictionary::GetItem(const Twine &key) const { + if (!IsValid()) + return nullDeref(); + PyObject *o = PyDict_GetItemString(m_py_obj, NullTerminated(key)); + if (PyErr_Occurred()) + return exception(); + if (!o) + return keyError(); + return Retain<PythonObject>(o); +} + +Error PythonDictionary::SetItem(const PythonObject &key, + const PythonObject &value) const { + if (!IsValid() || !value.IsValid()) + return nullDeref(); + int r = PyDict_SetItem(m_py_obj, key.get(), value.get()); + if (r < 0) + return exception(); + return Error::success(); +} + +Error PythonDictionary::SetItem(const Twine &key, + const PythonObject &value) const { + if (!IsValid() || !value.IsValid()) + return nullDeref(); + int r = PyDict_SetItemString(m_py_obj, NullTerminated(key), value.get()); + if (r < 0) + return exception(); + return Error::success(); } void PythonDictionary::SetItemForKey(const PythonObject &key, const PythonObject &value) { - if (IsAllocated() && key.IsValid() && value.IsValid()) - PyDict_SetItem(m_py_obj, key.get(), value.get()); + Error error = SetItem(key, value); + if (error) + llvm::consumeError(std::move(error)); } StructuredData::DictionarySP @@ -803,14 +748,6 @@ PythonDictionary::CreateStructuredDictionary() const { return result; } -PythonModule::PythonModule() : PythonObject() {} - -PythonModule::PythonModule(PyRefType type, PyObject *py_obj) { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a module -} - -PythonModule::~PythonModule() {} - PythonModule PythonModule::BuiltinsModule() { #if PY_MAJOR_VERSION >= 3 return AddModule("builtins"); @@ -826,9 +763,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 Twine &name) { + PyObject *mod = PyImport_ImportModule(NullTerminated(name)); + if (!mod) + return exception(); + return Take<PythonModule>(mod); +} + +Expected<PythonObject> PythonModule::Get(const Twine &name) { + if (!IsValid()) + return nullDeref(); + PyObject *dict = PyModule_GetDict(m_py_obj); + if (!dict) + return exception(); + PyObject *item = PyDict_GetItemString(dict, NullTerminated(name)); + if (!item) + return exception(); + return Retain<PythonObject>(item); } bool PythonModule::Check(PyObject *py_obj) { @@ -838,33 +789,12 @@ bool PythonModule::Check(PyObject *py_obj) { return PyModule_Check(py_obj); } -void PythonModule::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonModule::Check(py_obj)) { - PythonObject::Reset(); - return; - } - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); -} - PythonDictionary PythonModule::GetDictionary() const { - return PythonDictionary(PyRefType::Borrowed, PyModule_GetDict(m_py_obj)); -} - -PythonCallable::PythonCallable() : PythonObject() {} - -PythonCallable::PythonCallable(PyRefType type, PyObject *py_obj) { - Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a callable + if (!IsValid()) + return PythonDictionary(); + return Retain<PythonDictionary>(PyModule_GetDict(m_py_obj)); } -PythonCallable::~PythonCallable() {} - bool PythonCallable::Check(PyObject *py_obj) { if (!py_obj) return false; @@ -872,32 +802,80 @@ bool PythonCallable::Check(PyObject *py_obj) { return PyCallable_Check(py_obj); } -void PythonCallable::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonCallable::Check(py_obj)) { - PythonObject::Reset(); - return; +PythonCallable::ArgInfo PythonCallable::GetNumInitArguments() const { + auto arginfo = GetInitArgInfo(); + if (!arginfo) { + llvm::consumeError(arginfo.takeError()); + return ArgInfo{}; } - - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); + return arginfo.get(); } -PythonCallable::ArgInfo PythonCallable::GetNumArguments() const { - ArgInfo result = {0, false, false, false}; +Expected<PythonCallable::ArgInfo> PythonCallable::GetInitArgInfo() const { if (!IsValid()) - return result; + return nullDeref(); + auto init = As<PythonCallable>(GetAttribute("__init__")); + if (!init) + return init.takeError(); + return init.get().GetArgInfo(); +} + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 +static const char get_arg_info_script[] = R"( +from inspect import signature, Parameter, ismethod +from collections import namedtuple +ArgInfo = namedtuple('ArgInfo', ['count', 'has_varargs', 'is_bound_method']) +def main(f): + count = 0 + varargs = False + for parameter in signature(f).parameters.values(): + kind = parameter.kind + if kind in (Parameter.POSITIONAL_ONLY, + Parameter.POSITIONAL_OR_KEYWORD): + count += 1 + elif kind == Parameter.VAR_POSITIONAL: + varargs = True + elif kind in (Parameter.KEYWORD_ONLY, + Parameter.VAR_KEYWORD): + pass + else: + raise Exception(f'unknown parameter kind: {kind}') + return ArgInfo(count, varargs, ismethod(f)) +)"; +#endif + +Expected<PythonCallable::ArgInfo> PythonCallable::GetArgInfo() const { + ArgInfo result = {}; + if (!IsValid()) + return nullDeref(); + +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + + // no need to synchronize access to this global, we already have the GIL + static PythonScript get_arg_info(get_arg_info_script); + Expected<PythonObject> pyarginfo = get_arg_info(*this); + if (!pyarginfo) + return pyarginfo.takeError(); + result.count = cantFail(As<long long>(pyarginfo.get().GetAttribute("count"))); + result.has_varargs = + cantFail(As<bool>(pyarginfo.get().GetAttribute("has_varargs"))); + bool is_method = + cantFail(As<bool>(pyarginfo.get().GetAttribute("is_bound_method"))); + result.max_positional_args = + result.has_varargs ? ArgInfo::UNBOUNDED : result.count; + + // FIXME emulate old broken behavior + if (is_method) + result.count++; +#else + bool is_bound_method = false; PyObject *py_func_obj = m_py_obj; if (PyMethod_Check(py_func_obj)) { py_func_obj = PyMethod_GET_FUNCTION(py_func_obj); PythonObject im_self = GetAttributeValue("im_self"); if (im_self.IsValid() && !im_self.IsNone()) - result.is_bound_method = true; + is_bound_method = true; } else { // see if this is a callable object with an __call__ method if (!PyFunction_Check(py_func_obj)) { @@ -906,9 +884,9 @@ PythonCallable::ArgInfo PythonCallable::GetNumArguments() const { auto __callable__ = __call__.AsType<PythonCallable>(); if (__callable__.IsValid()) { py_func_obj = PyMethod_GET_FUNCTION(__callable__.get()); - PythonObject im_self = GetAttributeValue("im_self"); + PythonObject im_self = __callable__.GetAttributeValue("im_self"); if (im_self.IsValid() && !im_self.IsNone()) - result.is_bound_method = true; + is_bound_method = true; } } } @@ -923,10 +901,27 @@ PythonCallable::ArgInfo PythonCallable::GetNumArguments() const { result.count = code->co_argcount; result.has_varargs = !!(code->co_flags & CO_VARARGS); - result.has_kwargs = !!(code->co_flags & CO_VARKEYWORDS); + result.max_positional_args = result.has_varargs + ? ArgInfo::UNBOUNDED + : (result.count - (int)is_bound_method); + +#endif + return result; } +constexpr unsigned + PythonCallable::ArgInfo::UNBOUNDED; // FIXME delete after c++17 + +PythonCallable::ArgInfo PythonCallable::GetNumArguments() const { + auto arginfo = GetArgInfo(); + if (!arginfo) { + llvm::consumeError(arginfo.takeError()); + return ArgInfo{}; + } + return arginfo.get(); +} + PythonObject PythonCallable::operator()() { return PythonObject(PyRefType::Owned, PyObject_CallObject(m_py_obj, nullptr)); } @@ -945,21 +940,9 @@ operator()(std::initializer_list<PythonObject> args) { PyObject_CallObject(m_py_obj, arg_tuple.get())); } -PythonFile::PythonFile() : PythonObject() {} - -PythonFile::PythonFile(File &file, const char *mode) { Reset(file, mode); } - -PythonFile::PythonFile(const char *path, const char *mode) { - lldb_private::File file; - FileSystem::Instance().Open(file, FileSpec(path), GetOptionsFromMode(mode)); - Reset(file, mode); -} - -PythonFile::PythonFile(PyRefType type, PyObject *o) { Reset(type, o); } - -PythonFile::~PythonFile() {} - bool PythonFile::Check(PyObject *py_obj) { + if (!py_obj) + return false; #if PY_MAJOR_VERSION < 3 return PyFile_Check(py_obj); #else @@ -967,86 +950,633 @@ bool PythonFile::Check(PyObject *py_obj) { // first-class object type anymore. `PyFile_FromFd` is just a thin wrapper // over `io.open()`, which returns some object derived from `io.IOBase`. As a // result, the only way to detect a file in Python 3 is to check whether it - // inherits from `io.IOBase`. Since it is possible for non-files to also - // inherit from `io.IOBase`, we additionally verify that it has the `fileno` - // attribute, which should guarantee that it is backed by the file system. - PythonObject io_module(PyRefType::Owned, PyImport_ImportModule("io")); - PythonDictionary io_dict(PyRefType::Borrowed, - PyModule_GetDict(io_module.get())); - PythonObject io_base_class = io_dict.GetItemForKey(PythonString("IOBase")); - - PythonObject object_type(PyRefType::Owned, PyObject_Type(py_obj)); - - if (1 != PyObject_IsSubclass(object_type.get(), io_base_class.get())) + // inherits from `io.IOBase`. + auto io_module = PythonModule::Import("io"); + if (!io_module) { + llvm::consumeError(io_module.takeError()); return false; - if (!object_type.HasAttribute("fileno")) + } + auto iobase = io_module.get().Get("IOBase"); + if (!iobase) { + llvm::consumeError(iobase.takeError()); return false; - - return true; + } + int r = PyObject_IsInstance(py_obj, iobase.get().get()); + if (r < 0) { + llvm::consumeError(exception()); // clear the exception and log it. + return false; + } + return !!r; #endif } -void PythonFile::Reset(PyRefType type, PyObject *py_obj) { - // Grab the desired reference type so that if we end up rejecting `py_obj` it - // still gets decremented if necessary. - PythonObject result(type, py_obj); - - if (!PythonFile::Check(py_obj)) { - PythonObject::Reset(); - return; +namespace { +class GIL { +public: + GIL() { + m_state = PyGILState_Ensure(); + assert(!PyErr_Occurred()); } + ~GIL() { PyGILState_Release(m_state); } + +protected: + PyGILState_STATE m_state; +}; +} // namespace + +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; +} - // Calling PythonObject::Reset(const PythonObject&) will lead to stack - // overflow since it calls back into the virtual implementation. - PythonObject::Reset(PyRefType::Borrowed, result.get()); +PythonException::~PythonException() { + Py_XDECREF(m_exception_type); + Py_XDECREF(m_exception); + Py_XDECREF(m_traceback); + Py_XDECREF(m_repr_bytes); } -void PythonFile::Reset(File &file, const char *mode) { - if (!file.IsValid()) { - Reset(); - return; +void PythonException::log(llvm::raw_ostream &OS) const { OS << toCString(); } + +std::error_code PythonException::convertToErrorCode() const { + 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 main(exc_type, exc_value, tb): + f = StringIO() + print_exception(exc_type, exc_value, tb, file=f) + return f.getvalue() +)"; + +std::string PythonException::ReadBacktrace() const { + + if (!m_traceback) + return toCString(); + + // no need to synchronize access to this global, we already have the GIL + static PythonScript read_exception(read_exception_script); + + Expected<std::string> backtrace = As<std::string>( + read_exception(m_exception_type, m_exception, m_traceback)); + + if (!backtrace) { + std::string message = + std::string(toCString()) + "\n" + + "Traceback unavailble, an error occurred while reading it:\n"; + return (message + llvm::toString(backtrace.takeError())); } - char *cmode = const_cast<char *>(mode); + return std::move(backtrace.get()); +} + +char PythonException::ID = 0; + +llvm::Expected<File::OpenOptions> +GetOptionsForPyObject(const PythonObject &obj) { #if PY_MAJOR_VERSION >= 3 - Reset(PyRefType::Owned, PyFile_FromFd(file.GetDescriptor(), nullptr, cmode, - -1, nullptr, "ignore", nullptr, 0)); + auto options = File::OpenOptions(0); + auto readable = As<bool>(obj.CallMethod("readable")); + if (!readable) + return readable.takeError(); + auto writable = As<bool>(obj.CallMethod("writable")); + if (!writable) + return writable.takeError(); + if (readable.get()) + options |= File::eOpenOptionRead; + if (writable.get()) + options |= File::eOpenOptionWrite; + return options; #else - // Read through the Python source, doesn't seem to modify these strings - Reset(PyRefType::Owned, - PyFile_FromFile(file.GetStream(), const_cast<char *>(""), cmode, - nullptr)); + PythonString py_mode = obj.GetAttributeValue("mode").AsType<PythonString>(); + return File::GetOptionsFromMode(py_mode.GetString()); #endif } -uint32_t PythonFile::GetOptionsFromMode(llvm::StringRef mode) { - if (mode.empty()) - return 0; +// Base class template for python files. All it knows how to do +// is hold a reference to the python object and close or flush it +// when the File is closed. +namespace { +template <typename Base> class OwnedPythonFile : public Base { +public: + template <typename... Args> + OwnedPythonFile(const PythonFile &file, bool borrowed, Args... args) + : Base(args...), m_py_obj(file), m_borrowed(borrowed) { + assert(m_py_obj); + } + + ~OwnedPythonFile() override { + assert(m_py_obj); + GIL takeGIL; + Close(); + // we need to ensure the python object is released while we still + // hold the GIL + m_py_obj.Reset(); + } + + bool IsPythonSideValid() const { + GIL takeGIL; + auto closed = As<bool>(m_py_obj.GetAttribute("closed")); + if (!closed) { + llvm::consumeError(closed.takeError()); + return false; + } + return !closed.get(); + } + + bool IsValid() const override { + return IsPythonSideValid() && Base::IsValid(); + } + + Status Close() override { + assert(m_py_obj); + Status py_error, base_error; + GIL takeGIL; + if (!m_borrowed) { + auto r = m_py_obj.CallMethod("close"); + if (!r) + py_error = Status(r.takeError()); + } + base_error = Base::Close(); + if (py_error.Fail()) + return py_error; + return base_error; + }; + + PyObject *GetPythonObject() const { + assert(m_py_obj.IsValid()); + return m_py_obj.get(); + } + + static bool classof(const File *file) = delete; + +protected: + PythonFile m_py_obj; + bool m_borrowed; +}; +} // namespace + +// A SimplePythonFile is a OwnedPythonFile that just does all I/O as +// a NativeFile +namespace { +class SimplePythonFile : public OwnedPythonFile<NativeFile> { +public: + SimplePythonFile(const PythonFile &file, bool borrowed, int fd, + File::OpenOptions options) + : OwnedPythonFile(file, borrowed, fd, options, false) {} + + static char ID; + bool isA(const void *classID) const override { + return classID == &ID || NativeFile::isA(classID); + } + static bool classof(const File *file) { return file->isA(&ID); } +}; +char SimplePythonFile::ID = 0; +} // namespace + +#if PY_MAJOR_VERSION >= 3 + +namespace { +class PythonBuffer { +public: + PythonBuffer &operator=(const PythonBuffer &) = delete; + PythonBuffer(const PythonBuffer &) = delete; + + static Expected<PythonBuffer> Create(PythonObject &obj, + int flags = PyBUF_SIMPLE) { + Py_buffer py_buffer = {}; + PyObject_GetBuffer(obj.get(), &py_buffer, flags); + if (!py_buffer.obj) + return llvm::make_error<PythonException>(); + return PythonBuffer(py_buffer); + } + + PythonBuffer(PythonBuffer &&other) { + m_buffer = other.m_buffer; + other.m_buffer.obj = nullptr; + } - return llvm::StringSwitch<uint32_t>(mode.str()) - .Case("r", File::eOpenOptionRead) - .Case("w", File::eOpenOptionWrite) - .Case("a", File::eOpenOptionWrite | File::eOpenOptionAppend | - File::eOpenOptionCanCreate) - .Case("r+", File::eOpenOptionRead | File::eOpenOptionWrite) - .Case("w+", File::eOpenOptionRead | File::eOpenOptionWrite | - File::eOpenOptionCanCreate | File::eOpenOptionTruncate) - .Case("a+", File::eOpenOptionRead | File::eOpenOptionWrite | - File::eOpenOptionAppend | File::eOpenOptionCanCreate) - .Default(0); + ~PythonBuffer() { + if (m_buffer.obj) + PyBuffer_Release(&m_buffer); + } + + Py_buffer &get() { return m_buffer; } + +private: + // takes ownership of the buffer. + PythonBuffer(const Py_buffer &py_buffer) : m_buffer(py_buffer) {} + Py_buffer m_buffer; +}; +} // namespace + +// Shared methods between TextPythonFile and BinaryPythonFile +namespace { +class PythonIOFile : public OwnedPythonFile<File> { +public: + PythonIOFile(const PythonFile &file, bool borrowed) + : OwnedPythonFile(file, borrowed) {} + + ~PythonIOFile() override { Close(); } + + bool IsValid() const override { return IsPythonSideValid(); } + + Status Close() override { + assert(m_py_obj); + GIL takeGIL; + if (m_borrowed) + return Flush(); + auto r = m_py_obj.CallMethod("close"); + if (!r) + return Status(r.takeError()); + return Status(); + } + + Status Flush() override { + GIL takeGIL; + auto r = m_py_obj.CallMethod("flush"); + if (!r) + return Status(r.takeError()); + return Status(); + } + + Expected<File::OpenOptions> GetOptions() const override { + GIL takeGIL; + return GetOptionsForPyObject(m_py_obj); + } + + static char ID; + bool isA(const void *classID) const override { + return classID == &ID || File::isA(classID); + } + static bool classof(const File *file) { return file->isA(&ID); } +}; +char PythonIOFile::ID = 0; +} // namespace + +namespace { +class BinaryPythonFile : public PythonIOFile { +protected: + int m_descriptor; + +public: + BinaryPythonFile(int fd, const PythonFile &file, bool borrowed) + : PythonIOFile(file, borrowed), + m_descriptor(File::DescriptorIsValid(fd) ? fd + : File::kInvalidDescriptor) {} + + int GetDescriptor() const override { return m_descriptor; } + + Status Write(const void *buf, size_t &num_bytes) override { + GIL takeGIL; + PyObject *pybuffer_p = PyMemoryView_FromMemory( + const_cast<char *>((const char *)buf), num_bytes, PyBUF_READ); + if (!pybuffer_p) + return Status(llvm::make_error<PythonException>()); + auto pybuffer = Take<PythonObject>(pybuffer_p); + num_bytes = 0; + auto bytes_written = As<long long>(m_py_obj.CallMethod("write", pybuffer)); + if (!bytes_written) + return Status(bytes_written.takeError()); + if (bytes_written.get() < 0) + return Status(".write() method returned a negative number!"); + static_assert(sizeof(long long) >= sizeof(size_t), "overflow"); + num_bytes = bytes_written.get(); + return Status(); + } + + Status Read(void *buf, size_t &num_bytes) override { + GIL takeGIL; + static_assert(sizeof(long long) >= sizeof(size_t), "overflow"); + auto pybuffer_obj = + m_py_obj.CallMethod("read", (unsigned long long)num_bytes); + if (!pybuffer_obj) + return Status(pybuffer_obj.takeError()); + num_bytes = 0; + if (pybuffer_obj.get().IsNone()) { + // EOF + num_bytes = 0; + return Status(); + } + auto pybuffer = PythonBuffer::Create(pybuffer_obj.get()); + if (!pybuffer) + return Status(pybuffer.takeError()); + memcpy(buf, pybuffer.get().get().buf, pybuffer.get().get().len); + num_bytes = pybuffer.get().get().len; + return Status(); + } +}; +} // namespace + +namespace { +class TextPythonFile : public PythonIOFile { +protected: + int m_descriptor; + +public: + TextPythonFile(int fd, const PythonFile &file, bool borrowed) + : PythonIOFile(file, borrowed), + m_descriptor(File::DescriptorIsValid(fd) ? fd + : File::kInvalidDescriptor) {} + + int GetDescriptor() const override { return m_descriptor; } + + Status Write(const void *buf, size_t &num_bytes) override { + GIL takeGIL; + auto pystring = + PythonString::FromUTF8(llvm::StringRef((const char *)buf, num_bytes)); + if (!pystring) + return Status(pystring.takeError()); + num_bytes = 0; + auto bytes_written = + As<long long>(m_py_obj.CallMethod("write", pystring.get())); + if (!bytes_written) + return Status(bytes_written.takeError()); + if (bytes_written.get() < 0) + return Status(".write() method returned a negative number!"); + static_assert(sizeof(long long) >= sizeof(size_t), "overflow"); + num_bytes = bytes_written.get(); + return Status(); + } + + Status Read(void *buf, size_t &num_bytes) override { + GIL takeGIL; + size_t num_chars = num_bytes / 6; + size_t orig_num_bytes = num_bytes; + num_bytes = 0; + if (orig_num_bytes < 6) { + return Status("can't read less than 6 bytes from a utf8 text stream"); + } + auto pystring = As<PythonString>( + m_py_obj.CallMethod("read", (unsigned long long)num_chars)); + if (!pystring) + return Status(pystring.takeError()); + if (pystring.get().IsNone()) { + // EOF + return Status(); + } + auto stringref = pystring.get().AsUTF8(); + if (!stringref) + return Status(stringref.takeError()); + num_bytes = stringref.get().size(); + memcpy(buf, stringref.get().begin(), num_bytes); + return Status(); + } +}; +} // namespace + +#endif + +llvm::Expected<FileSP> PythonFile::ConvertToFile(bool borrowed) { + if (!IsValid()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid PythonFile"); + + int fd = PyObject_AsFileDescriptor(m_py_obj); + if (fd < 0) { + PyErr_Clear(); + return ConvertToFileForcingUseOfScriptingIOMethods(borrowed); + } + auto options = GetOptionsForPyObject(*this); + if (!options) + return options.takeError(); + + // LLDB and python will not share I/O buffers. We should probably + // flush the python buffers now. + auto r = CallMethod("flush"); + if (!r) + return r.takeError(); + + FileSP file_sp; + if (borrowed) { + // In this case we we don't need to retain the python + // object at all. + file_sp = std::make_shared<NativeFile>(fd, options.get(), false); + } else { + file_sp = std::static_pointer_cast<File>( + std::make_shared<SimplePythonFile>(*this, borrowed, fd, options.get())); + } + if (!file_sp->IsValid()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid File"); + + return file_sp; } -bool PythonFile::GetUnderlyingFile(File &file) const { +llvm::Expected<FileSP> +PythonFile::ConvertToFileForcingUseOfScriptingIOMethods(bool borrowed) { + + assert(!PyErr_Occurred()); + if (!IsValid()) - return false; + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid PythonFile"); + +#if PY_MAJOR_VERSION < 3 + + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "not supported on python 2"); + +#else + + int fd = PyObject_AsFileDescriptor(m_py_obj); + if (fd < 0) { + PyErr_Clear(); + fd = File::kInvalidDescriptor; + } + + auto io_module = PythonModule::Import("io"); + if (!io_module) + return io_module.takeError(); + auto textIOBase = io_module.get().Get("TextIOBase"); + if (!textIOBase) + return textIOBase.takeError(); + auto rawIOBase = io_module.get().Get("RawIOBase"); + if (!rawIOBase) + return rawIOBase.takeError(); + auto bufferedIOBase = io_module.get().Get("BufferedIOBase"); + if (!bufferedIOBase) + return bufferedIOBase.takeError(); + + FileSP file_sp; + + auto isTextIO = IsInstance(textIOBase.get()); + if (!isTextIO) + return isTextIO.takeError(); + if (isTextIO.get()) + file_sp = std::static_pointer_cast<File>( + std::make_shared<TextPythonFile>(fd, *this, borrowed)); + + auto isRawIO = IsInstance(rawIOBase.get()); + if (!isRawIO) + return isRawIO.takeError(); + auto isBufferedIO = IsInstance(bufferedIOBase.get()); + if (!isBufferedIO) + return isBufferedIO.takeError(); + + if (isRawIO.get() || isBufferedIO.get()) { + file_sp = std::static_pointer_cast<File>( + std::make_shared<BinaryPythonFile>(fd, *this, borrowed)); + } + + if (!file_sp) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "python file is neither text nor binary"); + + if (!file_sp->IsValid()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid File"); + + return file_sp; + +#endif +} + +Expected<PythonFile> PythonFile::FromFile(File &file, const char *mode) { + if (!file.IsValid()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid file"); + + if (auto *simple = llvm::dyn_cast<SimplePythonFile>(&file)) + return Retain<PythonFile>(simple->GetPythonObject()); +#if PY_MAJOR_VERSION >= 3 + if (auto *pythonio = llvm::dyn_cast<PythonIOFile>(&file)) + return Retain<PythonFile>(pythonio->GetPythonObject()); +#endif + + if (!mode) { + auto m = file.GetOpenMode(); + if (!m) + return m.takeError(); + mode = m.get(); + } + + PyObject *file_obj; +#if PY_MAJOR_VERSION >= 3 + file_obj = PyFile_FromFd(file.GetDescriptor(), nullptr, mode, -1, nullptr, + "ignore", nullptr, 0); +#else + // Read through the Python source, doesn't seem to modify these strings + char *cmode = const_cast<char *>(mode); + // We pass ::flush instead of ::fclose here so we borrow the FILE* -- + // the lldb_private::File still owns it. + file_obj = + PyFile_FromFile(file.GetStream(), const_cast<char *>(""), cmode, ::fflush); +#endif + + if (!file_obj) + return exception(); + + return Take<PythonFile>(file_obj); +} + +Error PythonScript::Init() { + if (function.IsValid()) + return Error::success(); + + PythonDictionary globals(PyInitialValue::Empty); + auto builtins = PythonModule::BuiltinsModule(); + if (Error error = globals.SetItem("__builtins__", builtins)) + 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("main")); + 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); +} - file.Close(); - // We don't own the file descriptor returned by this function, make sure the - // File object knows about that. - file.SetDescriptor(PyObject_AsFileDescriptor(m_py_obj), false); - PythonString py_mode = GetAttributeValue("mode").AsType<PythonString>(); - file.SetOptions(PythonFile::GetOptionsFromMode(py_mode.GetString())); - return file.IsValid(); +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 diff --git a/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h index 049ce90bb1b1..373d3212697d 100644 --- a/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -6,6 +6,45 @@ // //===----------------------------------------------------------------------===// +// +// !! FIXME FIXME FIXME !! +// +// 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 all the code in this header does not handle python exceptions +// correctly. It should all be converted to return Expected<> or +// Error types to capture the exception. +// +// Everything in this file except functions that return Error or +// Expected<> is considered deprecated and should not be +// used in new code. If you need to use it, fix it first. +// +// +// TODOs for this file +// +// * Make all methods safe for exceptions. +// +// * Eliminate method signatures that must translate exceptions into +// empty objects or NULLs. Almost everything here should return +// Expected<>. It should be acceptable for certain operations that +// can never fail to assert instead, such as the creation of +// PythonString from a string literal. +// +// * Elimintate Reset(), and make all non-default constructors private. +// Python objects should be created with Retain<> or Take<>, and they +// should be assigned with operator= +// +// * Eliminate default constructors, make python objects always +// nonnull, and use optionals where necessary. +// + + #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H @@ -20,12 +59,15 @@ #include "llvm/ADT/ArrayRef.h" namespace lldb_private { +namespace python { +class PythonObject; class PythonBytes; class PythonString; class PythonList; class PythonDictionary; class PythonInteger; +class PythonException; class StructuredPythonObject : public StructuredData::Generic { public: @@ -43,7 +85,7 @@ public: bool IsValid() const override { return GetValue() && GetValue() != Py_None; } - void Dump(Stream &s, bool pretty_print = true) const override; + void Serialize(llvm::json::OStream &s) const override; private: DISALLOW_COPY_AND_ASSIGN(StructuredPythonObject); @@ -72,61 +114,137 @@ enum class PyRefType { // not call Py_INCREF. }; -enum class PyInitialValue { Invalid, Empty }; -class PythonObject { -public: - PythonObject() : m_py_obj(nullptr) {} +// Take a reference that you already own, and turn it into +// a PythonObject. +// +// Most python API methods will return a +1 reference +// if they succeed or NULL if and only if +// they set an exception. Use this to collect such return +// values, after checking for NULL. +// +// If T is not just PythonObject, then obj must be already be +// checked to be of the correct type. +template <typename T> T Take(PyObject *obj) { + assert(obj); + assert(!PyErr_Occurred()); + T thing(PyRefType::Owned, obj); + assert(thing.IsValid()); + return std::move(thing); +} + +// Retain a reference you have borrowed, and turn it into +// a PythonObject. +// +// A minority of python APIs return a borrowed reference +// instead of a +1. They will also return NULL if and only +// if they set an exception. Use this to collect such return +// values, after checking for NULL. +// +// If T is not just PythonObject, then obj must be already be +// checked to be of the correct type. +template <typename T> T Retain(PyObject *obj) { + assert(obj); + assert(!PyErr_Occurred()); + T thing(PyRefType::Borrowed, obj); + assert(thing.IsValid()); + return std::move(thing); +} + +// This class can be used like a utility function to convert from +// a llvm-friendly Twine into a null-terminated const char *, +// which is the form python C APIs want their strings in. +// +// Example: +// const llvm::Twine &some_twine; +// PyFoo_Bar(x, y, z, NullTerminated(some_twine)); +// +// Why a class instead of a function? If the twine isn't already null +// terminated, it will need a temporary buffer to copy the string +// into. We need that buffer to stick around for the lifetime of the +// statement. +class NullTerminated { + const char *str; + llvm::SmallString<32> storage; - PythonObject(PyRefType type, PyObject *py_obj) : m_py_obj(nullptr) { - Reset(type, py_obj); +public: + NullTerminated(const llvm::Twine &twine) { + llvm::StringRef ref = twine.toNullTerminatedStringRef(storage); + str = ref.begin(); } + operator const char *() { return str; } +}; - PythonObject(const PythonObject &rhs) : m_py_obj(nullptr) { Reset(rhs); } +inline llvm::Error nullDeref() { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "A NULL PyObject* was dereferenced"); +} - virtual ~PythonObject() { Reset(); } +inline llvm::Error exception(const char *s = nullptr) { + return llvm::make_error<PythonException>(s); +} - void Reset() { - // Avoid calling the virtual method since it's not necessary - // to actually validate the type of the PyObject if we're - // just setting to null. - if (Py_IsInitialized()) - Py_XDECREF(m_py_obj); - m_py_obj = nullptr; - } +inline llvm::Error keyError() { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "key not in dict"); +} - void Reset(const PythonObject &rhs) { - // Avoid calling the virtual method if it's not necessary - // to actually validate the type of the PyObject. - if (!rhs.IsValid()) - Reset(); - else - Reset(PyRefType::Borrowed, rhs.m_py_obj); - } +enum class PyInitialValue { Invalid, Empty }; - // PythonObject is implicitly convertible to PyObject *, which will call the - // wrong overload. We want to explicitly disallow this, since a PyObject - // *always* owns its reference. Therefore the overload which takes a - // PyRefType doesn't make sense, and the copy constructor should be used. - void Reset(PyRefType type, const PythonObject &ref) = delete; +template <typename T, typename Enable = void> struct PythonFormat; - virtual void Reset(PyRefType type, PyObject *py_obj) { - if (py_obj == m_py_obj) - return; +template <> struct PythonFormat<unsigned long long> { + static constexpr char format = 'K'; + static auto get(unsigned long long value) { return value; } +}; - if (Py_IsInitialized()) - Py_XDECREF(m_py_obj); +template <> struct PythonFormat<long long> { + static constexpr char format = 'L'; + static auto get(long long value) { return value; } +}; - m_py_obj = py_obj; +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> { + static constexpr char format = 'O'; + static auto get(const T &value) { return value.get(); } +}; + +class PythonObject { +public: + PythonObject() : m_py_obj(nullptr) {} + 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 (Py_IsInitialized() && type == PyRefType::Borrowed) + if (m_py_obj && Py_IsInitialized() && type == PyRefType::Borrowed) Py_XINCREF(m_py_obj); } + PythonObject(const PythonObject &rhs) + : PythonObject(PyRefType::Borrowed, rhs.m_py_obj) {} + + PythonObject(PythonObject &&rhs) { + m_py_obj = rhs.m_py_obj; + rhs.m_py_obj = nullptr; + } + + ~PythonObject() { Reset(); } + + void Reset() { + if (m_py_obj && Py_IsInitialized()) + Py_DECREF(m_py_obj); + m_py_obj = nullptr; + } + void Dump() const { if (m_py_obj) _PyObject_Dump(m_py_obj); @@ -144,8 +262,9 @@ public: return result; } - PythonObject &operator=(const PythonObject &other) { - Reset(PyRefType::Borrowed, other.get()); + PythonObject &operator=(PythonObject other) { + Reset(); + m_py_obj = std::exchange(other.m_py_obj, nullptr); return *this; } @@ -174,11 +293,13 @@ public: PythonObject GetAttributeValue(llvm::StringRef attribute) const; - bool IsValid() const; + bool IsNone() const { return m_py_obj == Py_None; } + + bool IsValid() const { return m_py_obj != nullptr; } - bool IsAllocated() const; + bool IsAllocated() const { return IsValid() && !IsNone(); } - bool IsNone() const; + explicit operator bool() const { return IsValid() && !IsNone(); } template <typename T> T AsType() const { if (!T::Check(m_py_obj)) @@ -189,24 +310,125 @@ public: StructuredData::ObjectSP CreateStructuredObject() const; protected: + +#if PY_MAJOR_VERSION < 3 + // The python 2 API declares some arguments as char* that should + // be const char *, but it doesn't actually modify them. + static char *py2_const_cast(const char *s) { return const_cast<char *>(s); } +#else + static const char *py2_const_cast(const char *s) { return s; } +#endif + +public: + template <typename... T> + llvm::Expected<PythonObject> CallMethod(const char *name, + const T &... t) const { + const char format[] = {'(', PythonFormat<T>::format..., ')', 0}; + PyObject *obj = + PyObject_CallMethod(m_py_obj, py2_const_cast(name), + py2_const_cast(format), PythonFormat<T>::get(t)...); + if (!obj) + return exception(); + return python::Take<PythonObject>(obj); + } + + template <typename... T> + llvm::Expected<PythonObject> Call(const T &... t) const { + const char format[] = {'(', PythonFormat<T>::format..., ')', 0}; + PyObject *obj = PyObject_CallFunction(m_py_obj, py2_const_cast(format), + PythonFormat<T>::get(t)...); + if (!obj) + return exception(); + return python::Take<PythonObject>(obj); + } + + llvm::Expected<PythonObject> GetAttribute(const llvm::Twine &name) const { + if (!m_py_obj) + return nullDeref(); + PyObject *obj = PyObject_GetAttrString(m_py_obj, NullTerminated(name)); + if (!obj) + return exception(); + return python::Take<PythonObject>(obj); + } + + llvm::Expected<bool> IsTrue() { + if (!m_py_obj) + return nullDeref(); + int r = PyObject_IsTrue(m_py_obj); + if (r < 0) + return exception(); + return !!r; + } + + llvm::Expected<long long> AsLongLong() { + if (!m_py_obj) + return nullDeref(); + assert(!PyErr_Occurred()); + long long r = PyLong_AsLongLong(m_py_obj); + if (PyErr_Occurred()) + return exception(); + return r; + } + + llvm::Expected<bool> IsInstance(const PythonObject &cls) { + if (!m_py_obj || !cls.IsValid()) + return nullDeref(); + int r = PyObject_IsInstance(m_py_obj, cls.get()); + if (r < 0) + return exception(); + return !!r; + } + +protected: PyObject *m_py_obj; }; -class PythonBytes : public PythonObject { + +// This is why C++ needs monads. +template <typename T> llvm::Expected<T> As(llvm::Expected<PythonObject> &&obj) { + if (!obj) + return obj.takeError(); + if (!T::Check(obj.get().get())) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "type error"); + return T(PyRefType::Borrowed, std::move(obj.get().get())); +} + +template <> llvm::Expected<bool> As<bool>(llvm::Expected<PythonObject> &&obj); + +template <> +llvm::Expected<long long> As<long long>(llvm::Expected<PythonObject> &&obj); + +template <> +llvm::Expected<std::string> As<std::string>(llvm::Expected<PythonObject> &&obj); + + +template <class T> class TypedPythonObject : public PythonObject { public: - PythonBytes(); - explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes); - PythonBytes(const uint8_t *bytes, size_t length); - PythonBytes(PyRefType type, PyObject *o); + // override to perform implicit type conversions on Reset + // This can be eliminated once we drop python 2 support. + static void Convert(PyRefType &type, PyObject *&py_obj) {} - ~PythonBytes() override; + TypedPythonObject(PyRefType type, PyObject *py_obj) { + if (!py_obj) + return; + T::Convert(type, py_obj); + if (T::Check(py_obj)) + PythonObject::operator=(PythonObject(type, py_obj)); + else if (type == PyRefType::Owned) + Py_DECREF(py_obj); + } - static bool Check(PyObject *py_obj); + TypedPythonObject() {} +}; - // Bring in the no-argument base class version - using PythonObject::Reset; +class PythonBytes : public TypedPythonObject<PythonBytes> { +public: + using TypedPythonObject::TypedPythonObject; + explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes); + PythonBytes(const uint8_t *bytes, size_t length); - void Reset(PyRefType type, PyObject *py_obj) override; + static bool Check(PyObject *py_obj); llvm::ArrayRef<uint8_t> GetBytes() const; @@ -217,23 +439,15 @@ public: StructuredData::StringSP CreateStructuredString() const; }; -class PythonByteArray : public PythonObject { +class PythonByteArray : public TypedPythonObject<PythonByteArray> { public: - PythonByteArray(); + using TypedPythonObject::TypedPythonObject; explicit PythonByteArray(llvm::ArrayRef<uint8_t> bytes); PythonByteArray(const uint8_t *bytes, size_t length); - PythonByteArray(PyRefType type, PyObject *o); PythonByteArray(const PythonBytes &object); - ~PythonByteArray() override; - static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; - - void Reset(PyRefType type, PyObject *py_obj) override; - llvm::ArrayRef<uint8_t> GetBytes() const; size_t GetSize() const; @@ -243,45 +457,39 @@ public: StructuredData::StringSP CreateStructuredString() const; }; -class PythonString : public PythonObject { +class PythonString : public TypedPythonObject<PythonString> { public: - PythonString(); - explicit PythonString(llvm::StringRef string); - explicit PythonString(const char *string); - PythonString(PyRefType type, PyObject *o); + using TypedPythonObject::TypedPythonObject; + static llvm::Expected<PythonString> FromUTF8(llvm::StringRef string); - ~PythonString() override; + PythonString() : TypedPythonObject() {} // MSVC requires this for some reason - static bool Check(PyObject *py_obj); + explicit PythonString(llvm::StringRef string); // safe, null on error - // Bring in the no-argument base class version - using PythonObject::Reset; + static bool Check(PyObject *py_obj); + static void Convert(PyRefType &type, PyObject *&py_obj); - void Reset(PyRefType type, PyObject *py_obj) override; + llvm::StringRef GetString() const; // safe, empty string on error - llvm::StringRef GetString() const; + llvm::Expected<llvm::StringRef> AsUTF8() const; size_t GetSize() const; - void SetString(llvm::StringRef string); + void SetString(llvm::StringRef string); // safe, null on error StructuredData::StringSP CreateStructuredString() const; }; -class PythonInteger : public PythonObject { +class PythonInteger : public TypedPythonObject<PythonInteger> { public: - PythonInteger(); - explicit PythonInteger(int64_t value); - PythonInteger(PyRefType type, PyObject *o); - - ~PythonInteger() override; + using TypedPythonObject::TypedPythonObject; - static bool Check(PyObject *py_obj); + PythonInteger() : TypedPythonObject() {} // MSVC requires this for some reason - // Bring in the no-argument base class version - using PythonObject::Reset; + explicit PythonInteger(int64_t value); - void Reset(PyRefType type, PyObject *py_obj) override; + static bool Check(PyObject *py_obj); + static void Convert(PyRefType &type, PyObject *&py_obj); int64_t GetInteger() const; @@ -290,21 +498,14 @@ public: StructuredData::IntegerSP CreateStructuredInteger() const; }; -class PythonBoolean : public PythonObject { +class PythonBoolean : public TypedPythonObject<PythonBoolean> { public: - PythonBoolean() = default; - explicit PythonBoolean(bool value); - PythonBoolean(PyRefType type, PyObject *o); + using TypedPythonObject::TypedPythonObject; - ~PythonBoolean() override = default; + explicit PythonBoolean(bool value); static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; - - void Reset(PyRefType type, PyObject *py_obj) override; - bool GetValue() const; void SetValue(bool value); @@ -312,22 +513,17 @@ public: StructuredData::BooleanSP CreateStructuredBoolean() const; }; -class PythonList : public PythonObject { +class PythonList : public TypedPythonObject<PythonList> { public: - PythonList() {} + using TypedPythonObject::TypedPythonObject; + + PythonList() : TypedPythonObject() {} // MSVC requires this for some reason + explicit PythonList(PyInitialValue value); explicit PythonList(int list_size); - PythonList(PyRefType type, PyObject *o); - - ~PythonList() override; static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; - - void Reset(PyRefType type, PyObject *py_obj) override; - uint32_t GetSize() const; PythonObject GetItemAtIndex(uint32_t index) const; @@ -339,24 +535,17 @@ public: StructuredData::ArraySP CreateStructuredArray() const; }; -class PythonTuple : public PythonObject { +class PythonTuple : public TypedPythonObject<PythonTuple> { public: - PythonTuple() {} + using TypedPythonObject::TypedPythonObject; + explicit PythonTuple(PyInitialValue value); explicit PythonTuple(int tuple_size); - PythonTuple(PyRefType type, PyObject *o); PythonTuple(std::initializer_list<PythonObject> objects); PythonTuple(std::initializer_list<PyObject *> objects); - ~PythonTuple() override; - static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; - - void Reset(PyRefType type, PyObject *py_obj) override; - uint32_t GetSize() const; PythonObject GetItemAtIndex(uint32_t index) const; @@ -366,37 +555,35 @@ public: StructuredData::ArraySP CreateStructuredArray() const; }; -class PythonDictionary : public PythonObject { +class PythonDictionary : public TypedPythonObject<PythonDictionary> { public: - PythonDictionary() {} - explicit PythonDictionary(PyInitialValue value); - PythonDictionary(PyRefType type, PyObject *o); + using TypedPythonObject::TypedPythonObject; - ~PythonDictionary() override; - - static bool Check(PyObject *py_obj); + PythonDictionary() : TypedPythonObject() {} // MSVC requires this for some reason - // Bring in the no-argument base class version - using PythonObject::Reset; + explicit PythonDictionary(PyInitialValue value); - void Reset(PyRefType type, PyObject *py_obj) override; + static bool Check(PyObject *py_obj); uint32_t GetSize() const; PythonList GetKeys() const; - PythonObject GetItemForKey(const PythonObject &key) const; - void SetItemForKey(const PythonObject &key, const PythonObject &value); + PythonObject GetItemForKey(const PythonObject &key) const; // DEPRECATED + void SetItemForKey(const PythonObject &key, + const PythonObject &value); // DEPRECATED + + llvm::Expected<PythonObject> GetItem(const PythonObject &key) const; + llvm::Expected<PythonObject> GetItem(const llvm::Twine &key) const; + llvm::Error SetItem(const PythonObject &key, const PythonObject &value) const; + llvm::Error SetItem(const llvm::Twine &key, const PythonObject &value) const; StructuredData::DictionarySP CreateStructuredDictionary() const; }; -class PythonModule : public PythonObject { +class PythonModule : public TypedPythonObject<PythonModule> { public: - PythonModule(); - PythonModule(PyRefType type, PyObject *o); - - ~PythonModule() override; + using TypedPythonObject::TypedPythonObject; static bool Check(PyObject *py_obj); @@ -406,38 +593,57 @@ public: static PythonModule AddModule(llvm::StringRef module); - static PythonModule ImportModule(llvm::StringRef module); + // safe, returns invalid on error; + static PythonModule ImportModule(llvm::StringRef name) { + std::string s = name; + auto mod = Import(s.c_str()); + if (!mod) { + llvm::consumeError(mod.takeError()); + return PythonModule(); + } + return std::move(mod.get()); + } - // Bring in the no-argument base class version - using PythonObject::Reset; + static llvm::Expected<PythonModule> Import(const llvm::Twine &name); - void Reset(PyRefType type, PyObject *py_obj) override; + llvm::Expected<PythonObject> Get(const llvm::Twine &name); PythonDictionary GetDictionary() const; }; -class PythonCallable : public PythonObject { +class PythonCallable : public TypedPythonObject<PythonCallable> { public: + using TypedPythonObject::TypedPythonObject; + struct ArgInfo { - size_t count; - bool is_bound_method : 1; - bool has_varargs : 1; - bool has_kwargs : 1; + /* the largest number of positional arguments this callable + * can accept, or UNBOUNDED, ie UINT_MAX if it's a varargs + * function and can accept an arbitrary number */ + unsigned max_positional_args; + static constexpr unsigned UNBOUNDED = UINT_MAX; // FIXME c++17 inline + /* the number of positional arguments, including optional ones, + * and excluding varargs. If this is a bound method, then the + * count will still include a +1 for self. + * + * FIXME. That's crazy. This should be replaced with + * an accurate min and max for positional args. + */ + int count; + /* does the callable have positional varargs? */ + bool has_varargs : 1; // FIXME delete this }; - PythonCallable(); - PythonCallable(PyRefType type, PyObject *o); - - ~PythonCallable() override; - static bool Check(PyObject *py_obj); - // Bring in the no-argument base class version - using PythonObject::Reset; + llvm::Expected<ArgInfo> GetArgInfo() const; - void Reset(PyRefType type, PyObject *py_obj) override; + llvm::Expected<ArgInfo> GetInitArgInfo() const; - ArgInfo GetNumArguments() const; + ArgInfo GetNumArguments() const; // DEPRECATED + + // If the callable is a Py_Class, then find the number of arguments + // of the __init__ method. + ArgInfo GetNumInitArguments() const; // DEPRECATED PythonObject operator()(); @@ -451,27 +657,123 @@ public: } }; -class PythonFile : public PythonObject { +class PythonFile : public TypedPythonObject<PythonFile> { public: - PythonFile(); - PythonFile(File &file, const char *mode); - PythonFile(const char *path, const char *mode); - PythonFile(PyRefType type, PyObject *o); + using TypedPythonObject::TypedPythonObject; - ~PythonFile() override; + PythonFile() : TypedPythonObject() {} // MSVC requires this for some reason static bool Check(PyObject *py_obj); - using PythonObject::Reset; + static llvm::Expected<PythonFile> FromFile(File &file, + const char *mode = nullptr); - void Reset(PyRefType type, PyObject *py_obj) override; - void Reset(File &file, const char *mode); + llvm::Expected<lldb::FileSP> ConvertToFile(bool borrowed = false); + llvm::Expected<lldb::FileSP> + ConvertToFileForcingUseOfScriptingIOMethods(bool borrowed = false); +}; - static uint32_t GetOptionsFromMode(llvm::StringRef mode); +class PythonException : public llvm::ErrorInfo<PythonException> { +private: + PyObject *m_exception_type, *m_exception, *m_traceback; + PyObject *m_repr_bytes; - bool GetUnderlyingFile(File &file) const; +public: + static char ID; + const char *toCString() const; + PythonException(const char *caller = nullptr); + void Restore(); + ~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. +// If the Expected is an Error instead of a T, that error will be converted +// into a python exception, and this will return a default-constructed T. +// +// This is appropriate for use right at the boundary of python calling into +// C++, such as in a SWIG typemap. In such a context you should simply +// check if the returned T is valid, and if it is, return a NULL back +// to python. This will result in the Error being raised as an exception +// from python code's point of view. +// +// For example: +// ``` +// Expected<Foo *> efoop = some_cpp_function(); +// Foo *foop = unwrapOrSetPythonException(efoop); +// if (!foop) +// return NULL; +// do_something(*foop); +// +// If the Error returned was itself created because a python exception was +// raised when C++ code called into python, then the original exception +// will be restored. Otherwise a simple string exception will be raised. +template <typename T> T unwrapOrSetPythonException(llvm::Expected<T> expected) { + if (expected) + return expected.get(); + llvm::handleAllErrors( + expected.takeError(), [](PythonException &E) { E.Restore(); }, + [](const llvm::ErrorInfoBase &E) { + PyErr_SetString(PyExc_Exception, E.message().c_str()); + }); + return T(); +} + +// This is only here to help incrementally migrate old, exception-unsafe +// code. +template <typename T> T unwrapIgnoringErrors(llvm::Expected<T> expected) { + if (expected) + return std::move(expected.get()); + 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); + +// Sometimes the best way to interact with a python interpreter is +// to run some python code. You construct a PythonScript with +// script string. The script assigns some function to `_function_` +// and you get a C++ callable object that calls the python function. +// +// Example: +// +// const char script[] = R"( +// def main(x, y): +// .... +// )"; +// +// Expected<PythonObject> cpp_foo_wrapper(PythonObject x, PythonObject y) { +// // no need to synchronize access to this global, we already have the GIL +// static PythonScript foo(script) +// return foo(x, y); +// } +class PythonScript { + const char *script; + PythonCallable function; + + llvm::Error Init(); + +public: + PythonScript(const char *script) : script(script), function() {} + + template <typename... Args> + llvm::Expected<PythonObject> operator()(Args &&... args) { + if (llvm::Error error = Init()) + return std::move(error); + return function.Call(std::forward<Args>(args)...); + } }; +} // namespace python } // namespace lldb_private #endif diff --git a/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp b/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp deleted file mode 100644 index c9d834ce6868..000000000000 --- a/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 diff --git a/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h b/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h deleted file mode 100644 index 3a88aa037776..000000000000 --- a/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 diff --git a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 2d2b68ceaaa6..3eee52184142 100644 --- a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/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" @@ -45,6 +44,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" #include <memory> #include <mutex> @@ -54,6 +54,8 @@ 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 @@ -96,6 +98,8 @@ LLDBSwigPythonCreateCommandObject(const char *python_class_name, extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan( const char *python_class_name, const char *session_dictionary_name, + StructuredDataImpl *args_data, + std::string &error_string, const lldb::ThreadPlanSP &thread_plan_sp); extern "C" bool LLDBSWIGPythonCallThreadPlan(void *implementor, @@ -302,39 +306,26 @@ void ScriptInterpreterPython::ComputePythonDirForApple( auto rend = llvm::sys::path::rend(path_ref); auto framework = std::find(rbegin, rend, "LLDB.framework"); if (framework == rend) { - ComputePythonDirForPosix(path); + ComputePythonDir(path); return; } path.resize(framework - rend); llvm::sys::path::append(path, style, "LLDB.framework", "Resources", "Python"); } -void ScriptInterpreterPython::ComputePythonDirForPosix( +void ScriptInterpreterPython::ComputePythonDir( llvm::SmallVectorImpl<char> &path) { - auto style = llvm::sys::path::Style::posix; -#if defined(LLDB_PYTHON_RELATIVE_LIBDIR) // Build the path by backing out of the lib dir, then building with whatever // the real python interpreter uses. (e.g. lib for most, lib64 on RHEL - // x86_64). - llvm::sys::path::remove_filename(path, style); - llvm::sys::path::append(path, style, LLDB_PYTHON_RELATIVE_LIBDIR); -#else - llvm::sys::path::append(path, style, - "python" + llvm::Twine(PY_MAJOR_VERSION) + "." + - llvm::Twine(PY_MINOR_VERSION), - "site-packages"); -#endif -} - -void ScriptInterpreterPython::ComputePythonDirForWindows( - llvm::SmallVectorImpl<char> &path) { - auto style = llvm::sys::path::Style::windows; - llvm::sys::path::remove_filename(path, style); - llvm::sys::path::append(path, style, "lib", "site-packages"); + // x86_64, or bin on Windows). + llvm::sys::path::remove_filename(path); + llvm::sys::path::append(path, LLDB_PYTHON_RELATIVE_LIBDIR); +#if defined(_WIN32) // This will be injected directly through FileSpec.GetDirectory().SetString(), // so we need to normalize manually. std::replace(path.begin(), path.end(), '\\', '/'); +#endif } FileSpec ScriptInterpreterPython::GetPythonDir() { @@ -347,10 +338,8 @@ FileSpec ScriptInterpreterPython::GetPythonDir() { #if defined(__APPLE__) ComputePythonDirForApple(path); -#elif defined(_WIN32) - ComputePythonDirForWindows(path); #else - ComputePythonDirForPosix(path); + ComputePythonDir(path); #endif spec.GetDirectory().SetString(path); return spec; @@ -382,7 +371,7 @@ void ScriptInterpreterPython::Terminate() {} ScriptInterpreterPythonImpl::Locker::Locker( ScriptInterpreterPythonImpl *py_interpreter, uint16_t on_entry, - uint16_t on_leave, FILE *in, FILE *out, FILE *err) + uint16_t on_leave, FileSP in, FileSP out, FileSP err) : ScriptInterpreterLocker(), m_teardown_session((on_leave & TearDownSession) == TearDownSession), m_python_interpreter(py_interpreter) { @@ -412,8 +401,8 @@ bool ScriptInterpreterPythonImpl::Locker::DoAcquireLock() { } bool ScriptInterpreterPythonImpl::Locker::DoInitSession(uint16_t on_entry_flags, - FILE *in, FILE *out, - FILE *err) { + FileSP in, FileSP out, + FileSP err) { if (!m_python_interpreter) return false; return m_python_interpreter->EnterSession(on_entry_flags, in, out, err); @@ -448,9 +437,9 @@ ScriptInterpreterPythonImpl::ScriptInterpreterPythonImpl(Debugger &debugger) m_sys_module_dict(PyInitialValue::Invalid), m_run_one_line_function(), m_run_one_line_str_global(), m_dictionary_name(m_debugger.GetInstanceName().AsCString()), - m_terminal_state(), m_active_io_handler(eIOHandlerNone), - m_session_is_active(false), m_pty_slave_is_open(false), - m_valid_session(true), m_lock_count(0), m_command_thread_state(nullptr) { + m_active_io_handler(eIOHandlerNone), m_session_is_active(false), + m_pty_slave_is_open(false), m_valid_session(true), m_lock_count(0), + m_command_thread_state(nullptr) { InitializePrivate(); m_dictionary_name.append("_dict"); @@ -535,7 +524,7 @@ def function (frame, bp_loc, internal_dict): } if (instructions) { - StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); if (output_sp && interactive) { output_sp->PutCString(instructions); output_sp->Flush(); @@ -558,7 +547,7 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler, if (!bp_options) continue; - auto data_up = llvm::make_unique<CommandDataPython>(); + auto data_up = std::make_unique<CommandDataPython>(); if (!data_up) break; data_up->user_source.SplitIntoLines(data); @@ -571,7 +560,7 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler, bp_options->SetCallback( ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp); } else if (!batch_mode) { - StreamFileSP error_sp = io_handler.GetErrorStreamFile(); + StreamFileSP error_sp = io_handler.GetErrorStreamFileSP(); if (error_sp) { error_sp->Printf("Warning: No command attached to breakpoint.\n"); error_sp->Flush(); @@ -583,7 +572,7 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler, case eIOHandlerWatchpoint: { WatchpointOptions *wp_options = (WatchpointOptions *)io_handler.GetUserData(); - auto data_up = llvm::make_unique<WatchpointOptions::CommandData>(); + auto data_up = std::make_unique<WatchpointOptions::CommandData>(); data_up->user_source.SplitIntoLines(data); if (GenerateWatchpointCommandCallbackData(data_up->user_source, @@ -593,7 +582,7 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler, wp_options->SetCallback( ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp); } else if (!batch_mode) { - StreamFileSP error_sp = io_handler.GetErrorStreamFile(); + StreamFileSP error_sp = io_handler.GetErrorStreamFileSP(); if (error_sp) { error_sp->Printf("Warning: No command attached to breakpoint.\n"); error_sp->Flush(); @@ -609,29 +598,15 @@ ScriptInterpreterPythonImpl::CreateInstance(Debugger &debugger) { return std::make_shared<ScriptInterpreterPythonImpl>(debugger); } -void ScriptInterpreterPythonImpl::ResetOutputFileHandle(FILE *fh) {} - -void ScriptInterpreterPythonImpl::SaveTerminalState(int fd) { - // Python mucks with the terminal state of STDIN. If we can possibly avoid - // this by setting the file handles up correctly prior to entering the - // interpreter we should. For now we save and restore the terminal state on - // the input file handle. - m_terminal_state.Save(fd, false); -} - -void ScriptInterpreterPythonImpl::RestoreTerminalState() { - // Python mucks with the terminal state of STDIN. If we can possibly avoid - // this by setting the file handles up correctly prior to entering the - // interpreter we should. For now we save and restore the terminal state on - // the input file handle. - m_terminal_state.Restore(); -} - void ScriptInterpreterPythonImpl::LeaveSession() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT)); if (log) log->PutCString("ScriptInterpreterPythonImpl::LeaveSession()"); + // Unset the LLDB global variables. + PyRun_SimpleString("lldb.debugger = None; lldb.target = None; lldb.process " + "= None; lldb.thread = None; lldb.frame = None"); + // checking that we have a valid thread state - since we use our own // threading and locking in some (rare) cases during cleanup Python may end // up believing we have no thread state and PyImport_AddModule will crash if @@ -662,45 +637,52 @@ void ScriptInterpreterPythonImpl::LeaveSession() { m_session_is_active = false; } -bool ScriptInterpreterPythonImpl::SetStdHandle(File &file, const char *py_name, - PythonFile &save_file, +bool ScriptInterpreterPythonImpl::SetStdHandle(FileSP file_sp, + const char *py_name, + PythonObject &save_file, const char *mode) { - if (file.IsValid()) { - // Flush the file before giving it to python to avoid interleaved output. - file.Flush(); + if (!file_sp || !*file_sp) { + save_file.Reset(); + return false; + } + File &file = *file_sp; - PythonDictionary &sys_module_dict = GetSysModuleDictionary(); + // Flush the file before giving it to python to avoid interleaved output. + file.Flush(); - save_file = sys_module_dict.GetItemForKey(PythonString(py_name)) - .AsType<PythonFile>(); + PythonDictionary &sys_module_dict = GetSysModuleDictionary(); - PythonFile new_file(file, mode); - sys_module_dict.SetItemForKey(PythonString(py_name), new_file); - return true; - } else - save_file.Reset(); - return false; + auto new_file = PythonFile::FromFile(file, mode); + if (!new_file) { + llvm::consumeError(new_file.takeError()); + return false; + } + + save_file = sys_module_dict.GetItemForKey(PythonString(py_name)); + + sys_module_dict.SetItemForKey(PythonString(py_name), new_file.get()); + return true; } bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags, - FILE *in, FILE *out, FILE *err) { + FileSP in_sp, FileSP out_sp, + FileSP err_sp) { // If we have already entered the session, without having officially 'left' // it, then there is no need to 'enter' it again. Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT)); if (m_session_is_active) { - if (log) - log->Printf( - "ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16 - ") session is already active, returning without doing anything", - on_entry_flags); + LLDB_LOGF( + log, + "ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16 + ") session is already active, returning without doing anything", + on_entry_flags); return false; } - if (log) - log->Printf( - "ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16 - ")", - on_entry_flags); + LLDB_LOGF( + log, + "ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16 ")", + on_entry_flags); m_session_is_active = true; @@ -733,33 +715,29 @@ bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags, PythonDictionary &sys_module_dict = GetSysModuleDictionary(); if (sys_module_dict.IsValid()) { - File in_file(in, false); - File out_file(out, false); - File err_file(err, false); - - lldb::StreamFileSP in_sp; - lldb::StreamFileSP out_sp; - lldb::StreamFileSP err_sp; - if (!in_file.IsValid() || !out_file.IsValid() || !err_file.IsValid()) - m_debugger.AdoptTopIOHandlerFilesIfInvalid(in_sp, out_sp, err_sp); + lldb::FileSP top_in_sp; + lldb::StreamFileSP top_out_sp, top_err_sp; + if (!in_sp || !out_sp || !err_sp || !*in_sp || !*out_sp || !*err_sp) + m_debugger.AdoptTopIOHandlerFilesIfInvalid(top_in_sp, top_out_sp, + top_err_sp); if (on_entry_flags & Locker::NoSTDIN) { m_saved_stdin.Reset(); } else { - if (!SetStdHandle(in_file, "stdin", m_saved_stdin, "r")) { - if (in_sp) - SetStdHandle(in_sp->GetFile(), "stdin", m_saved_stdin, "r"); + if (!SetStdHandle(in_sp, "stdin", m_saved_stdin, "r")) { + if (top_in_sp) + SetStdHandle(top_in_sp, "stdin", m_saved_stdin, "r"); } } - if (!SetStdHandle(out_file, "stdout", m_saved_stdout, "w")) { - if (out_sp) - SetStdHandle(out_sp->GetFile(), "stdout", m_saved_stdout, "w"); + if (!SetStdHandle(out_sp, "stdout", m_saved_stdout, "w")) { + if (top_out_sp) + SetStdHandle(top_out_sp->GetFileSP(), "stdout", m_saved_stdout, "w"); } - if (!SetStdHandle(err_file, "stderr", m_saved_stderr, "w")) { - if (err_sp) - SetStdHandle(err_sp->GetFile(), "stderr", m_saved_stderr, "w"); + if (!SetStdHandle(err_sp, "stderr", m_saved_stderr, "w")) { + if (top_err_sp) + SetStdHandle(top_err_sp->GetFileSP(), "stderr", m_saved_stderr, "w"); } } @@ -769,9 +747,9 @@ bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags, 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; } @@ -788,19 +766,16 @@ PythonDictionary &ScriptInterpreterPythonImpl::GetSessionDictionary() { if (!main_dict.IsValid()) return m_session_dict; - PythonObject item = main_dict.GetItemForKey(PythonString(m_dictionary_name)); - m_session_dict.Reset(PyRefType::Borrowed, item.get()); + m_session_dict = unwrapIgnoringErrors( + As<PythonDictionary>(main_dict.GetItem(m_dictionary_name))); return m_session_dict; } PythonDictionary &ScriptInterpreterPythonImpl::GetSysModuleDictionary() { if (m_sys_module_dict.IsValid()) return m_sys_module_dict; - - PythonObject sys_module(PyRefType::Borrowed, PyImport_AddModule("sys")); - if (sys_module.IsValid()) - m_sys_module_dict.Reset(PyRefType::Borrowed, - PyModule_GetDict(sys_module.get())); + PythonModule sys_module = unwrapIgnoringErrors(PythonModule::Import("sys")); + m_sys_module_dict = sys_module.GetDictionary(); return m_sys_module_dict; } @@ -867,7 +842,7 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLine( // directly down to Python. Debugger &debugger = m_debugger; - StreamFileSP input_file_sp; + FileSP input_file_sp; StreamFileSP output_file_sp; StreamFileSP error_file_sp; Communication output_comm( @@ -875,7 +850,7 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLine( bool join_read_thread = false; if (options.GetEnableIO()) { if (result) { - input_file_sp = debugger.GetInputFile(); + input_file_sp = debugger.GetInputFileSP(); // Set output to a temporary file so we can forward the results on to // the result object @@ -906,9 +881,9 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLine( ::setbuf(outfile_handle, nullptr); result->SetImmediateOutputFile( - debugger.GetOutputFile()->GetFile().GetStream()); + debugger.GetOutputStream().GetFileSP()); result->SetImmediateErrorFile( - debugger.GetErrorFile()->GetFile().GetStream()); + debugger.GetErrorStream().GetFileSP()); } } } @@ -916,22 +891,26 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLine( debugger.AdoptTopIOHandlerFilesIfInvalid(input_file_sp, output_file_sp, error_file_sp); } else { - input_file_sp = std::make_shared<StreamFile>(); - FileSystem::Instance().Open(input_file_sp->GetFile(), + auto nullin = FileSystem::Instance().Open( FileSpec(FileSystem::DEV_NULL), File::eOpenOptionRead); - - output_file_sp = std::make_shared<StreamFile>(); - FileSystem::Instance().Open(output_file_sp->GetFile(), + auto nullout = FileSystem::Instance().Open( FileSpec(FileSystem::DEV_NULL), File::eOpenOptionWrite); - - error_file_sp = output_file_sp; + if (!nullin) { + result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n", + llvm::fmt_consume(nullin.takeError())); + return false; + } + if (!nullout) { + result->AppendErrorWithFormatv("failed to open /dev/null: {0}\n", + llvm::fmt_consume(nullout.takeError())); + return false; + } + input_file_sp = std::move(nullin.get()); + error_file_sp = output_file_sp = std::make_shared<StreamFile>(std::move(nullout.get())); } - FILE *in_file = input_file_sp->GetFile().GetStream(); - FILE *out_file = output_file_sp->GetFile().GetStream(); - FILE *err_file = error_file_sp->GetFile().GetStream(); bool success = false; { // WARNING! It's imperative that this RAII scope be as tight as @@ -947,8 +926,8 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLine( Locker::AcquireLock | Locker::InitSession | (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) | ((result && result->GetInteractive()) ? 0 : Locker::NoSTDIN), - Locker::FreeAcquiredLock | Locker::TearDownSession, in_file, out_file, - err_file); + Locker::FreeAcquiredLock | Locker::TearDownSession, input_file_sp, + output_file_sp->GetFileSP(), error_file_sp->GetFileSP()); // Find the correct script interpreter dictionary in the main module. PythonDictionary &session_dict = GetSessionDictionary(); @@ -975,9 +954,8 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLine( } // Flush our output and error file handles - ::fflush(out_file); - if (out_file != err_file) - ::fflush(err_file); + output_file_sp->Flush(); + error_file_sp->Flush(); } if (join_read_thread) { @@ -1020,7 +998,7 @@ void ScriptInterpreterPythonImpl::ExecuteInterpreterLoop() { // a running interpreter loop inside the already running Python interpreter // loop, so we won't do it. - if (!debugger.GetInputFile()->GetFile().IsValid()) + if (!debugger.GetInputFile().IsValid()) return; IOHandlerSP io_handler_sp(new IOHandlerPythonInterpreter(debugger, this)); @@ -1040,19 +1018,19 @@ bool ScriptInterpreterPythonImpl::Interrupt() { long tid = state->thread_id; PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); - if (log) - log->Printf("ScriptInterpreterPythonImpl::Interrupt() sending " - "PyExc_KeyboardInterrupt (tid = %li, num_threads = %i)...", - tid, num_threads); + LLDB_LOGF(log, + "ScriptInterpreterPythonImpl::Interrupt() sending " + "PyExc_KeyboardInterrupt (tid = %li, num_threads = %i)...", + tid, num_threads); return true; } } - if (log) - log->Printf( - "ScriptInterpreterPythonImpl::Interrupt() python code not running, " - "can't interrupt"); + LLDB_LOGF(log, + "ScriptInterpreterPythonImpl::Interrupt() python code not running, " + "can't interrupt"); return false; } + bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn( llvm::StringRef in_string, ScriptInterpreter::ScriptReturnType return_type, void *ret_value, const ExecuteScriptOptions &options) { @@ -1063,152 +1041,111 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn( 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()) { - locals.Reset( - PyRefType::Owned, - PyObject_GetAttrString(globals.get(), m_dictionary_name.c_str())); - } - + 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 | @@ -1216,52 +1153,32 @@ Status ScriptInterpreterPythonImpl::ExecuteMultipleLines( 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.Reset( - PyRefType::Owned, - PyObject_GetAttrString(globals.get(), m_dictionary_name.c_str())); - + 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(); + Expected<PythonObject> return_value = + runStringMultiLine(in_string, globals, locals); - 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())); - } + if (!return_value) { + llvm::Error error = + llvm::handleErrors(return_value.takeError(), [&](PythonException &E) { + llvm::Error error = llvm::createStringError( + llvm::inconvertibleErrorCode(), E.ReadBacktrace()); + if (!options.GetMaskoutErrors()) + E.Restore(); + return error; + }); + return Status(std::move(error)); } - PythonExceptionState exception_state(!options.GetMaskoutErrors()); - if (exception_state.IsError()) - error.SetErrorString(exception_state.Format().c_str()); - - return error; + return Status(); } void ScriptInterpreterPythonImpl::CollectDataForBreakpointCommandCallback( @@ -1308,7 +1225,7 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( // Set a Python one-liner as the callback for the breakpoint. Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( BreakpointOptions *bp_options, const char *command_body_text) { - auto data_up = llvm::make_unique<CommandDataPython>(); + auto data_up = std::make_unique<CommandDataPython>(); // Split the command_body_text into lines, and pass that to // GenerateBreakpointCommandCallbackData. That will wrap the body in an @@ -1331,7 +1248,7 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( // Set a Python one-liner as the callback for the watchpoint. void ScriptInterpreterPythonImpl::SetWatchpointCommandCallback( WatchpointOptions *wp_options, const char *oneliner) { - auto data_up = llvm::make_unique<WatchpointOptions::CommandData>(); + auto data_up = std::make_unique<WatchpointOptions::CommandData>(); // It's necessary to set both user_source and script_source to the oneliner. // The former is used to generate callback description (as in watchpoint @@ -1853,12 +1770,14 @@ StructuredData::DictionarySP ScriptInterpreterPythonImpl::OSPlugin_CreateThread( } StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( - const char *class_name, lldb::ThreadPlanSP thread_plan_sp) { + const char *class_name, StructuredDataImpl *args_data, + std::string &error_str, + lldb::ThreadPlanSP thread_plan_sp) { if (class_name == nullptr || class_name[0] == '\0') return StructuredData::ObjectSP(); if (!thread_plan_sp.get()) - return StructuredData::ObjectSP(); + return {}; Debugger &debugger = thread_plan_sp->GetTarget().GetDebugger(); ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter(); @@ -1866,17 +1785,18 @@ StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( static_cast<ScriptInterpreterPythonImpl *>(script_interpreter); if (!script_interpreter) - return StructuredData::ObjectSP(); + return {}; void *ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - ret_val = LLDBSwigPythonCreateScriptedThreadPlan( class_name, python_interpreter->m_dictionary_name.c_str(), - thread_plan_sp); + args_data, error_str, thread_plan_sp); + if (!ret_val) + return {}; } return StructuredData::ObjectSP(new StructuredPythonObject(ret_val)); @@ -2052,15 +1972,22 @@ StructuredData::DictionarySP ScriptInterpreterPythonImpl::GetDynamicSettings( if (!generic) return StructuredData::DictionarySP(); - PythonObject reply_pyobj; 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)); - PythonDictionary py_dict(PyRefType::Borrowed, reply_pyobj.get()); + auto setting = (PyObject *)LLDBSWIGPython_GetDynamicSetting( + generic->GetValue(), setting_name, target_sp); + + if (!setting) + return StructuredData::DictionarySP(); + + PythonDictionary py_dict = + unwrapIgnoringErrors(As<PythonDictionary>(Take<PythonObject>(setting))); + + if (!py_dict) + return StructuredData::DictionarySP(); + return py_dict.CreateStructuredDictionary(); } @@ -2230,18 +2157,6 @@ bool ScriptInterpreterPythonImpl::GetScriptedSummary( return ret_val; } -void ScriptInterpreterPythonImpl::Clear() { - // Release any global variables that might have strong references to - // LLDB objects when clearing the python script interpreter. - Locker locker(this, Locker::AcquireLock, Locker::FreeAcquiredLock); - - // This may be called as part of Py_Finalize. In that case the modules are - // destroyed in random order and we can't guarantee that we can access these. - if (Py_IsInitialized()) - PyRun_SimpleString("lldb.debugger = None; lldb.target = None; lldb.process " - "= None; lldb.thread = None; lldb.frame = None"); -} - bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction( void *baton, StoppointCallbackContext *context, user_id_t break_id, user_id_t break_loc_id) { diff --git a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h index 24941ec77452..33ae308041b2 100644 --- a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h +++ b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h @@ -48,8 +48,7 @@ public: protected: static void ComputePythonDirForApple(llvm::SmallVectorImpl<char> &path); - static void ComputePythonDirForPosix(llvm::SmallVectorImpl<char> &path); - static void ComputePythonDirForWindows(llvm::SmallVectorImpl<char> &path); + static void ComputePythonDir(llvm::SmallVectorImpl<char> &path); }; } // namespace lldb_private diff --git a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index a9993c4068a2..929567e579d8 100644 --- a/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -78,6 +78,8 @@ public: StructuredData::ObjectSP CreateScriptedThreadPlan(const char *class_name, + StructuredDataImpl *args_data, + std::string &error_str, lldb::ThreadPlanSP thread_plan) override; bool ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp, @@ -188,8 +190,6 @@ public: const TypeSummaryOptions &options, std::string &retval) override; - void Clear() override; - bool GetDocumentationForItem(const char *item, std::string &dest) override; bool GetShortHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp, @@ -256,8 +256,6 @@ public: void SetWatchpointCommandCallback(WatchpointOptions *wp_options, const char *oneliner) override; - void ResetOutputFileHandle(FILE *new_fh) override; - const char *GetDictionaryName() { return m_dictionary_name.c_str(); } PyThreadState *GetThreadState() { return m_command_thread_state; } @@ -296,17 +294,19 @@ public: TearDownSession = 0x0004 }; - Locker(ScriptInterpreterPythonImpl *py_interpreter = nullptr, + Locker(ScriptInterpreterPythonImpl *py_interpreter, uint16_t on_entry = AcquireLock | InitSession, - uint16_t on_leave = FreeLock | TearDownSession, FILE *in = nullptr, - FILE *out = nullptr, FILE *err = nullptr); + uint16_t on_leave = FreeLock | TearDownSession, + lldb::FileSP in = nullptr, lldb::FileSP out = nullptr, + lldb::FileSP err = nullptr); ~Locker() override; private: bool DoAcquireLock(); - bool DoInitSession(uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err); + bool DoInitSession(uint16_t on_entry_flags, lldb::FileSP in, + lldb::FileSP out, lldb::FileSP err); bool DoFreeLock(); @@ -314,7 +314,6 @@ public: bool m_teardown_session; ScriptInterpreterPythonImpl *m_python_interpreter; - // FILE* m_tmp_fh; PyGILState_STATE m_GILState; }; @@ -343,14 +342,11 @@ public: static void AddToSysPath(AddLocation location, std::string path); - bool EnterSession(uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err); + bool EnterSession(uint16_t on_entry_flags, lldb::FileSP in, lldb::FileSP out, + lldb::FileSP err); void LeaveSession(); - void SaveTerminalState(int fd); - - void RestoreTerminalState(); - uint32_t IsExecutingPython() const { return m_lock_count > 0; } uint32_t IncrementLockCount() { return ++m_lock_count; } @@ -367,27 +363,26 @@ public: eIOHandlerWatchpoint }; - PythonObject &GetMainModule(); + python::PythonModule &GetMainModule(); - PythonDictionary &GetSessionDictionary(); + python::PythonDictionary &GetSessionDictionary(); - PythonDictionary &GetSysModuleDictionary(); + python::PythonDictionary &GetSysModuleDictionary(); bool GetEmbeddedInterpreterModuleObjects(); - bool SetStdHandle(File &file, const char *py_name, PythonFile &save_file, - const char *mode); - - PythonFile m_saved_stdin; - PythonFile m_saved_stdout; - PythonFile m_saved_stderr; - PythonObject m_main_module; - PythonDictionary m_session_dict; - PythonDictionary m_sys_module_dict; - PythonObject m_run_one_line_function; - PythonObject m_run_one_line_str_global; + bool SetStdHandle(lldb::FileSP file, const char *py_name, + python::PythonObject &save_file, const char *mode); + + python::PythonObject m_saved_stdin; + python::PythonObject m_saved_stdout; + python::PythonObject m_saved_stderr; + python::PythonModule m_main_module; + python::PythonDictionary m_session_dict; + python::PythonDictionary m_sys_module_dict; + python::PythonObject m_run_one_line_function; + python::PythonObject m_run_one_line_str_global; std::string m_dictionary_name; - TerminalState m_terminal_state; ActiveIOHandler m_active_io_handler; bool m_session_is_active; bool m_pty_slave_is_open; |