Please use GitHub pull requests for new patches. Avoid migrating existing patches. Phabricator shutdown timeline
Changeset View
Changeset View
Standalone View
Standalone View
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
Show All 16 Lines | |||||
#include "lldb/Host/File.h" | #include "lldb/Host/File.h" | ||||
#include "lldb/Host/FileSystem.h" | #include "lldb/Host/FileSystem.h" | ||||
#include "lldb/Interpreter/ScriptInterpreter.h" | #include "lldb/Interpreter/ScriptInterpreter.h" | ||||
#include "lldb/Utility/Log.h" | #include "lldb/Utility/Log.h" | ||||
#include "lldb/Utility/Stream.h" | #include "lldb/Utility/Stream.h" | ||||
#include "llvm/ADT/StringSwitch.h" | #include "llvm/ADT/StringSwitch.h" | ||||
#include "llvm/Support/Casting.h" | |||||
#include "llvm/Support/ConvertUTF.h" | #include "llvm/Support/ConvertUTF.h" | ||||
#include "llvm/Support/Errno.h" | #include "llvm/Support/Errno.h" | ||||
#include <stdio.h> | #include <stdio.h> | ||||
using namespace lldb_private; | using namespace lldb_private; | ||||
using namespace lldb; | using namespace lldb; | ||||
using namespace lldb_private::python; | using namespace lldb_private::python; | ||||
▲ Show 20 Lines • Show All 974 Lines • ▼ Show 20 Lines | |||||
operator()(std::initializer_list<PythonObject> args) { | operator()(std::initializer_list<PythonObject> args) { | ||||
PythonTuple arg_tuple(args); | PythonTuple arg_tuple(args); | ||||
return PythonObject(PyRefType::Owned, | return PythonObject(PyRefType::Owned, | ||||
PyObject_CallObject(m_py_obj, arg_tuple.get())); | PyObject_CallObject(m_py_obj, arg_tuple.get())); | ||||
} | } | ||||
PythonFile::PythonFile() : PythonObject() {} | PythonFile::PythonFile() : PythonObject() {} | ||||
PythonFile::PythonFile(File &file, const char *mode) { Reset(file, mode); } | |||||
PythonFile::PythonFile(PyRefType type, PyObject *o) { Reset(type, o); } | PythonFile::PythonFile(PyRefType type, PyObject *o) { Reset(type, o); } | ||||
PythonFile::~PythonFile() {} | PythonFile::~PythonFile() {} | ||||
bool PythonFile::Check(PyObject *py_obj) { | bool PythonFile::Check(PyObject *py_obj) { | ||||
if (!py_obj) | if (!py_obj) | ||||
return false; | return false; | ||||
#if PY_MAJOR_VERSION < 3 | #if PY_MAJOR_VERSION < 3 | ||||
Show All 33 Lines | if (!PythonFile::Check(py_obj)) { | ||||
return; | return; | ||||
} | } | ||||
// Calling PythonObject::Reset(const PythonObject&) will lead to stack | // Calling PythonObject::Reset(const PythonObject&) will lead to stack | ||||
// overflow since it calls back into the virtual implementation. | // overflow since it calls back into the virtual implementation. | ||||
PythonObject::Reset(PyRefType::Borrowed, result.get()); | PythonObject::Reset(PyRefType::Borrowed, result.get()); | ||||
} | } | ||||
void PythonFile::Reset(File &file, const char *mode) { | |||||
if (!file.IsValid()) { | |||||
Reset(); | |||||
return; | |||||
} | |||||
char *cmode = const_cast<char *>(mode); | |||||
#if PY_MAJOR_VERSION >= 3 | |||||
Reset(PyRefType::Owned, PyFile_FromFd(file.GetDescriptor(), nullptr, cmode, | |||||
-1, nullptr, "ignore", nullptr, 0)); | |||||
#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)); | |||||
#endif | |||||
} | |||||
FileUP PythonFile::GetUnderlyingFile() const { | FileUP PythonFile::GetUnderlyingFile() const { | ||||
if (!IsValid()) | if (!IsValid()) | ||||
return nullptr; | return nullptr; | ||||
// We don't own the file descriptor returned by this function, make sure the | // We don't own the file descriptor returned by this function, make sure the | ||||
// File object knows about that. | // File object knows about that. | ||||
PythonString py_mode = GetAttributeValue("mode").AsType<PythonString>(); | PythonString py_mode = GetAttributeValue("mode").AsType<PythonString>(); | ||||
auto options = File::GetOptionsFromMode(py_mode.GetString()); | auto options = File::GetOptionsFromMode(py_mode.GetString()); | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | if (!m_borrowed) { | ||||
py_error = Status(r.takeError()); | py_error = Status(r.takeError()); | ||||
} | } | ||||
base_error = Base::Close(); | base_error = Base::Close(); | ||||
if (py_error.Fail()) | if (py_error.Fail()) | ||||
return py_error; | return py_error; | ||||
return base_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: | protected: | ||||
PythonFile m_py_obj; | PythonFile m_py_obj; | ||||
bool m_borrowed; | bool m_borrowed; | ||||
}; | }; | ||||
} // namespace | } // namespace | ||||
// A SimplePythonFile is a OwnedPythonFile that just does all I/O as | // A SimplePythonFile is a OwnedPythonFile that just does all I/O as | ||||
// a NativeFile | // a NativeFile | ||||
namespace { | namespace { | ||||
class SimplePythonFile : public OwnedPythonFile<NativeFile> { | class SimplePythonFile : public OwnedPythonFile<NativeFile> { | ||||
public: | public: | ||||
SimplePythonFile(const PythonFile &file, bool borrowed, int fd, | SimplePythonFile(const PythonFile &file, bool borrowed, int fd, | ||||
File::OpenOptions options) | File::OpenOptions options) | ||||
: OwnedPythonFile(file, borrowed, fd, options, false) {} | : 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 | } // namespace | ||||
#if PY_MAJOR_VERSION >= 3 | #if PY_MAJOR_VERSION >= 3 | ||||
namespace { | namespace { | ||||
class PythonBuffer { | class PythonBuffer { | ||||
public: | public: | ||||
PythonBuffer &operator=(const PythonBuffer &) = delete; | PythonBuffer &operator=(const PythonBuffer &) = delete; | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | public: | ||||
Status Flush() override { | Status Flush() override { | ||||
GIL takeGIL; | GIL takeGIL; | ||||
auto r = m_py_obj.CallMethod("flush"); | auto r = m_py_obj.CallMethod("flush"); | ||||
if (!r) | if (!r) | ||||
return Status(r.takeError()); | return Status(r.takeError()); | ||||
return Status(); | 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 | ||||
namespace { | namespace { | ||||
class BinaryPythonFile : public PythonIOFile { | class BinaryPythonFile : public PythonIOFile { | ||||
protected: | protected: | ||||
int m_descriptor; | int m_descriptor; | ||||
public: | public: | ||||
▲ Show 20 Lines • Show All 204 Lines • ▼ Show 20 Lines | if (!file_sp->IsValid()) | ||||
return llvm::createStringError(llvm::inconvertibleErrorCode(), | return llvm::createStringError(llvm::inconvertibleErrorCode(), | ||||
"invalid File"); | "invalid File"); | ||||
return file_sp; | return file_sp; | ||||
#endif | #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()); | |||||
labath: if (auto *simple = ...) | |||||
#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); | |||||
What would you say to passing fflush here directly? The signature is compatible with fclose, and fclose is documented to return (a superset of) the errors also reported by fflush. So, it seems like it might be useful to report flushing errors instead of swallowing them. Since accessing the FILE* after a fclose call (even if it fails) is UB, and the python code thinks it's calling fclose, it shouldn't mess up the FILE state in any way even if the flushing fails.. labath: What would you say to passing `fflush` here directly? The signature is compatible with fclose… | |||||
Oh yea good point lawrence_danna: Oh yea good point | |||||
#endif | |||||
if (!file_obj) | |||||
return exception(); | |||||
return Take<PythonFile>(file_obj); | |||||
} | |||||
#endif | #endif |
if (auto *simple = ...)