1622 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1622 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- PythonDataObjects.cpp ---------------------------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "lldb/Host/Config.h"
 | |
| 
 | |
| #if LLDB_ENABLE_PYTHON
 | |
| 
 | |
| #include "PythonDataObjects.h"
 | |
| #include "ScriptInterpreterPython.h"
 | |
| 
 | |
| #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"
 | |
| 
 | |
| #include <stdio.h>
 | |
| 
 | |
| using namespace lldb_private;
 | |
| using namespace lldb;
 | |
| using namespace lldb_private::python;
 | |
| using llvm::cantFail;
 | |
| using llvm::Error;
 | |
| using llvm::Expected;
 | |
| using llvm::Twine;
 | |
| 
 | |
| 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->AsLongLong();
 | |
| }
 | |
| 
 | |
| template <>
 | |
| Expected<unsigned long long>
 | |
| python::As<unsigned long long>(Expected<PythonObject> &&obj) {
 | |
|   if (!obj)
 | |
|     return obj.takeError();
 | |
|   return obj->AsUnsignedLongLong();
 | |
| }
 | |
| 
 | |
| 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 std::string(utf8.get());
 | |
| }
 | |
| 
 | |
| Expected<long long> PythonObject::AsLongLong() const {
 | |
|   if (!m_py_obj)
 | |
|     return nullDeref();
 | |
| #if PY_MAJOR_VERSION < 3
 | |
|   if (!PyLong_Check(m_py_obj)) {
 | |
|     PythonInteger i(PyRefType::Borrowed, m_py_obj);
 | |
|     return i.AsLongLong();
 | |
|   }
 | |
| #endif
 | |
|   assert(!PyErr_Occurred());
 | |
|   long long r = PyLong_AsLongLong(m_py_obj);
 | |
|   if (PyErr_Occurred())
 | |
|     return exception();
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| Expected<long long> PythonObject::AsUnsignedLongLong() const {
 | |
|   if (!m_py_obj)
 | |
|     return nullDeref();
 | |
| #if PY_MAJOR_VERSION < 3
 | |
|   if (!PyLong_Check(m_py_obj)) {
 | |
|     PythonInteger i(PyRefType::Borrowed, m_py_obj);
 | |
|     return i.AsUnsignedLongLong();
 | |
|   }
 | |
| #endif
 | |
|   assert(!PyErr_Occurred());
 | |
|   long long r = PyLong_AsUnsignedLongLong(m_py_obj);
 | |
|   if (PyErr_Occurred())
 | |
|     return exception();
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| // wraps on overflow, instead of raising an error.
 | |
| Expected<unsigned long long> PythonObject::AsModuloUnsignedLongLong() const {
 | |
|   if (!m_py_obj)
 | |
|     return nullDeref();
 | |
| #if PY_MAJOR_VERSION < 3
 | |
|   if (!PyLong_Check(m_py_obj)) {
 | |
|     PythonInteger i(PyRefType::Borrowed, m_py_obj);
 | |
|     return i.AsModuloUnsignedLongLong();
 | |
|   }
 | |
| #endif
 | |
|   assert(!PyErr_Occurred());
 | |
|   unsigned long long r = PyLong_AsUnsignedLongLongMask(m_py_obj);
 | |
|   if (PyErr_Occurred())
 | |
|     return exception();
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| void StructuredPythonObject::Serialize(llvm::json::OStream &s) const {
 | |
|   s.value(llvm::formatv("Python Obj: {0:X}", GetValue()).str());
 | |
| }
 | |
| 
 | |
| // PythonObject
 | |
| 
 | |
| void PythonObject::Dump(Stream &strm) const {
 | |
|   if (m_py_obj) {
 | |
|     FILE *file = llvm::sys::RetryAfterSignal(nullptr, ::tmpfile);
 | |
|     if (file) {
 | |
|       ::PyObject_Print(m_py_obj, file, 0);
 | |
|       const long length = ftell(file);
 | |
|       if (length) {
 | |
|         ::rewind(file);
 | |
|         std::vector<char> file_contents(length, '\0');
 | |
|         const size_t length_read =
 | |
|             ::fread(file_contents.data(), 1, file_contents.size(), file);
 | |
|         if (length_read > 0)
 | |
|           strm.Write(file_contents.data(), length_read);
 | |
|       }
 | |
|       ::fclose(file);
 | |
|     }
 | |
|   } else
 | |
|     strm.PutCString("NULL");
 | |
| }
 | |
| 
 | |
| PyObjectType PythonObject::GetObjectType() const {
 | |
|   if (!IsAllocated())
 | |
|     return PyObjectType::None;
 | |
| 
 | |
|   if (PythonModule::Check(m_py_obj))
 | |
|     return PyObjectType::Module;
 | |
|   if (PythonList::Check(m_py_obj))
 | |
|     return PyObjectType::List;
 | |
|   if (PythonTuple::Check(m_py_obj))
 | |
|     return PyObjectType::Tuple;
 | |
|   if (PythonDictionary::Check(m_py_obj))
 | |
|     return PyObjectType::Dictionary;
 | |
|   if (PythonString::Check(m_py_obj))
 | |
|     return PyObjectType::String;
 | |
| #if PY_MAJOR_VERSION >= 3
 | |
|   if (PythonBytes::Check(m_py_obj))
 | |
|     return PyObjectType::Bytes;
 | |
| #endif
 | |
|   if (PythonByteArray::Check(m_py_obj))
 | |
|     return PyObjectType::ByteArray;
 | |
|   if (PythonBoolean::Check(m_py_obj))
 | |
|     return PyObjectType::Boolean;
 | |
|   if (PythonInteger::Check(m_py_obj))
 | |
|     return PyObjectType::Integer;
 | |
|   if (PythonFile::Check(m_py_obj))
 | |
|     return PyObjectType::File;
 | |
|   if (PythonCallable::Check(m_py_obj))
 | |
|     return PyObjectType::Callable;
 | |
|   return PyObjectType::Unknown;
 | |
| }
 | |
| 
 | |
| PythonString PythonObject::Repr() const {
 | |
|   if (!m_py_obj)
 | |
|     return PythonString();
 | |
|   PyObject *repr = PyObject_Repr(m_py_obj);
 | |
|   if (!repr)
 | |
|     return PythonString();
 | |
|   return PythonString(PyRefType::Owned, repr);
 | |
| }
 | |
| 
 | |
| PythonString PythonObject::Str() const {
 | |
|   if (!m_py_obj)
 | |
|     return PythonString();
 | |
|   PyObject *str = PyObject_Str(m_py_obj);
 | |
|   if (!str)
 | |
|     return PythonString();
 | |
|   return PythonString(PyRefType::Owned, str);
 | |
| }
 | |
| 
 | |
| PythonObject
 | |
| PythonObject::ResolveNameWithDictionary(llvm::StringRef name,
 | |
|                                         const PythonDictionary &dict) {
 | |
|   size_t dot_pos = name.find('.');
 | |
|   llvm::StringRef piece = name.substr(0, dot_pos);
 | |
|   PythonObject result = dict.GetItemForKey(PythonString(piece));
 | |
|   if (dot_pos == llvm::StringRef::npos) {
 | |
|     // There was no dot, we're done.
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   // There was a dot.  The remaining portion of the name should be looked up in
 | |
|   // the context of the object that was found in the dictionary.
 | |
|   return result.ResolveName(name.substr(dot_pos + 1));
 | |
| }
 | |
| 
 | |
| PythonObject PythonObject::ResolveName(llvm::StringRef name) const {
 | |
|   // Resolve the name in the context of the specified object.  If, for example,
 | |
|   // `this` refers to a PyModule, then this will look for `name` in this
 | |
|   // module.  If `this` refers to a PyType, then it will resolve `name` as an
 | |
|   // attribute of that type.  If `this` refers to an instance of an object,
 | |
|   // then it will resolve `name` as the value of the specified field.
 | |
|   //
 | |
|   // This function handles dotted names so that, for example, if `m_py_obj`
 | |
|   // refers to the `sys` module, and `name` == "path.append", then it will find
 | |
|   // the function `sys.path.append`.
 | |
| 
 | |
|   size_t dot_pos = name.find('.');
 | |
|   if (dot_pos == llvm::StringRef::npos) {
 | |
|     // No dots in the name, we should be able to find the value immediately as
 | |
|     // an attribute of `m_py_obj`.
 | |
|     return GetAttributeValue(name);
 | |
|   }
 | |
| 
 | |
|   // Look up the first piece of the name, and resolve the rest as a child of
 | |
|   // that.
 | |
|   PythonObject parent = ResolveName(name.substr(0, dot_pos));
 | |
|   if (!parent.IsAllocated())
 | |
|     return PythonObject();
 | |
| 
 | |
|   // Tail recursion.. should be optimized by the compiler
 | |
|   return parent.ResolveName(name.substr(dot_pos + 1));
 | |
| }
 | |
| 
 | |
| bool PythonObject::HasAttribute(llvm::StringRef attr) const {
 | |
|   if (!IsValid())
 | |
|     return false;
 | |
|   PythonString py_attr(attr);
 | |
|   return !!PyObject_HasAttr(m_py_obj, py_attr.get());
 | |
| }
 | |
| 
 | |
| PythonObject PythonObject::GetAttributeValue(llvm::StringRef attr) const {
 | |
|   if (!IsValid())
 | |
|     return PythonObject();
 | |
| 
 | |
|   PythonString py_attr(attr);
 | |
|   if (!PyObject_HasAttr(m_py_obj, py_attr.get()))
 | |
|     return PythonObject();
 | |
| 
 | |
|   return PythonObject(PyRefType::Owned,
 | |
|                       PyObject_GetAttr(m_py_obj, py_attr.get()));
 | |
| }
 | |
| 
 | |
| StructuredData::ObjectSP PythonObject::CreateStructuredObject() const {
 | |
|   switch (GetObjectType()) {
 | |
|   case PyObjectType::Dictionary:
 | |
|     return PythonDictionary(PyRefType::Borrowed, m_py_obj)
 | |
|         .CreateStructuredDictionary();
 | |
|   case PyObjectType::Boolean:
 | |
|     return PythonBoolean(PyRefType::Borrowed, m_py_obj)
 | |
|         .CreateStructuredBoolean();
 | |
|   case PyObjectType::Integer:
 | |
|     return PythonInteger(PyRefType::Borrowed, m_py_obj)
 | |
|         .CreateStructuredInteger();
 | |
|   case PyObjectType::List:
 | |
|     return PythonList(PyRefType::Borrowed, m_py_obj).CreateStructuredArray();
 | |
|   case PyObjectType::String:
 | |
|     return PythonString(PyRefType::Borrowed, m_py_obj).CreateStructuredString();
 | |
|   case PyObjectType::Bytes:
 | |
|     return PythonBytes(PyRefType::Borrowed, m_py_obj).CreateStructuredString();
 | |
|   case PyObjectType::ByteArray:
 | |
|     return PythonByteArray(PyRefType::Borrowed, m_py_obj)
 | |
|         .CreateStructuredString();
 | |
|   case PyObjectType::None:
 | |
|     return StructuredData::ObjectSP();
 | |
|   default:
 | |
|     return StructuredData::ObjectSP(new StructuredPythonObject(m_py_obj));
 | |
|   }
 | |
| }
 | |
| 
 | |
| // PythonString
 | |
| 
 | |
| PythonBytes::PythonBytes(llvm::ArrayRef<uint8_t> bytes) { SetBytes(bytes); }
 | |
| 
 | |
| PythonBytes::PythonBytes(const uint8_t *bytes, size_t length) {
 | |
|   SetBytes(llvm::ArrayRef<uint8_t>(bytes, length));
 | |
| }
 | |
| 
 | |
| bool PythonBytes::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
|   return PyBytes_Check(py_obj);
 | |
| }
 | |
| 
 | |
| llvm::ArrayRef<uint8_t> PythonBytes::GetBytes() const {
 | |
|   if (!IsValid())
 | |
|     return llvm::ArrayRef<uint8_t>();
 | |
| 
 | |
|   Py_ssize_t size;
 | |
|   char *c;
 | |
| 
 | |
|   PyBytes_AsStringAndSize(m_py_obj, &c, &size);
 | |
|   return llvm::ArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(c), size);
 | |
| }
 | |
| 
 | |
| size_t PythonBytes::GetSize() const {
 | |
|   if (!IsValid())
 | |
|     return 0;
 | |
|   return PyBytes_Size(m_py_obj);
 | |
| }
 | |
| 
 | |
| void PythonBytes::SetBytes(llvm::ArrayRef<uint8_t> bytes) {
 | |
|   const char *data = reinterpret_cast<const char *>(bytes.data());
 | |
|   *this = Take<PythonBytes>(PyBytes_FromStringAndSize(data, bytes.size()));
 | |
| }
 | |
| 
 | |
| StructuredData::StringSP PythonBytes::CreateStructuredString() const {
 | |
|   StructuredData::StringSP result(new StructuredData::String);
 | |
|   Py_ssize_t size;
 | |
|   char *c;
 | |
|   PyBytes_AsStringAndSize(m_py_obj, &c, &size);
 | |
|   result->SetValue(std::string(c, size));
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| PythonByteArray::PythonByteArray(llvm::ArrayRef<uint8_t> bytes)
 | |
|     : PythonByteArray(bytes.data(), bytes.size()) {}
 | |
| 
 | |
| PythonByteArray::PythonByteArray(const uint8_t *bytes, size_t length) {
 | |
|   const char *str = reinterpret_cast<const char *>(bytes);
 | |
|   *this = Take<PythonByteArray>(PyByteArray_FromStringAndSize(str, length));
 | |
| }
 | |
| 
 | |
| bool PythonByteArray::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
|   return PyByteArray_Check(py_obj);
 | |
| }
 | |
| 
 | |
| llvm::ArrayRef<uint8_t> PythonByteArray::GetBytes() const {
 | |
|   if (!IsValid())
 | |
|     return llvm::ArrayRef<uint8_t>();
 | |
| 
 | |
|   char *c = PyByteArray_AsString(m_py_obj);
 | |
|   size_t size = GetSize();
 | |
|   return llvm::ArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(c), size);
 | |
| }
 | |
| 
 | |
| size_t PythonByteArray::GetSize() const {
 | |
|   if (!IsValid())
 | |
|     return 0;
 | |
| 
 | |
|   return PyByteArray_Size(m_py_obj);
 | |
| }
 | |
| 
 | |
| StructuredData::StringSP PythonByteArray::CreateStructuredString() const {
 | |
|   StructuredData::StringSP result(new StructuredData::String);
 | |
|   llvm::ArrayRef<uint8_t> bytes = GetBytes();
 | |
|   const char *str = reinterpret_cast<const char *>(bytes.data());
 | |
|   result->SetValue(std::string(str, bytes.size()));
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // PythonString
 | |
| 
 | |
| Expected<PythonString> PythonString::FromUTF8(llvm::StringRef string) {
 | |
| #if PY_MAJOR_VERSION >= 3
 | |
|   PyObject *str = PyUnicode_FromStringAndSize(string.data(), string.size());
 | |
| #else
 | |
|   PyObject *str = PyString_FromStringAndSize(string.data(), string.size());
 | |
| #endif
 | |
|   if (!str)
 | |
|     return llvm::make_error<PythonException>();
 | |
|   return Take<PythonString>(str);
 | |
| }
 | |
| 
 | |
| PythonString::PythonString(llvm::StringRef string) { SetString(string); }
 | |
| 
 | |
| bool PythonString::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
| 
 | |
|   if (PyUnicode_Check(py_obj))
 | |
|     return true;
 | |
| #if PY_MAJOR_VERSION < 3
 | |
|   if (PyString_Check(py_obj))
 | |
|     return true;
 | |
| #endif
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 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)) {
 | |
|     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
 | |
| }
 | |
| 
 | |
| 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 nullDeref();
 | |
| 
 | |
|   Py_ssize_t size;
 | |
|   const char *data;
 | |
| 
 | |
| #if PY_MAJOR_VERSION >= 3
 | |
|   data = PyUnicode_AsUTF8AndSize(m_py_obj, &size);
 | |
| #else
 | |
|   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);
 | |
| }
 | |
| 
 | |
| size_t PythonString::GetSize() const {
 | |
|   if (IsValid()) {
 | |
| #if PY_MAJOR_VERSION >= 3
 | |
| #if PY_MINOR_VERSION >= 3
 | |
|     return PyUnicode_GetLength(m_py_obj);
 | |
| #else
 | |
|     return PyUnicode_GetSize(m_py_obj);
 | |
| #endif
 | |
| #else
 | |
|     return PyString_Size(m_py_obj);
 | |
| #endif
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void PythonString::SetString(llvm::StringRef string) {
 | |
|   auto s = FromUTF8(string);
 | |
|   if (!s) {
 | |
|     llvm::consumeError(s.takeError());
 | |
|     Reset();
 | |
|   } else {
 | |
|     *this = std::move(s.get());
 | |
|   }
 | |
| }
 | |
| 
 | |
| StructuredData::StringSP PythonString::CreateStructuredString() const {
 | |
|   StructuredData::StringSP result(new StructuredData::String);
 | |
|   result->SetValue(GetString());
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // PythonInteger
 | |
| 
 | |
| PythonInteger::PythonInteger(int64_t value) { SetInteger(value); }
 | |
| 
 | |
| bool PythonInteger::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
| 
 | |
| #if PY_MAJOR_VERSION >= 3
 | |
|   // Python 3 does not have PyInt_Check.  There is only one type of integral
 | |
|   // value, long.
 | |
|   return PyLong_Check(py_obj);
 | |
| #else
 | |
|   return PyLong_Check(py_obj) || PyInt_Check(py_obj);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 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
 | |
|   // doesn't even have a PyInt.
 | |
|   if (PyInt_Check(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.
 | |
|     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
 | |
| }
 | |
| 
 | |
| void PythonInteger::SetInteger(int64_t value) {
 | |
|   *this = Take<PythonInteger>(PyLong_FromLongLong(value));
 | |
| }
 | |
| 
 | |
| StructuredData::IntegerSP PythonInteger::CreateStructuredInteger() const {
 | |
|   StructuredData::IntegerSP result(new StructuredData::Integer);
 | |
|   // FIXME this is really not ideal.   Errors are silently converted to 0
 | |
|   // and overflows are silently wrapped.   But we'd need larger changes
 | |
|   // to StructuredData to fix it, so that's how it is for now.
 | |
|   llvm::Expected<unsigned long long> value = AsModuloUnsignedLongLong();
 | |
|   if (!value) {
 | |
|     llvm::consumeError(value.takeError());
 | |
|     result->SetValue(0);
 | |
|   } else {
 | |
|     result->SetValue(value.get());
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // PythonBoolean
 | |
| 
 | |
| PythonBoolean::PythonBoolean(bool value) {
 | |
|   SetValue(value);
 | |
| }
 | |
| 
 | |
| bool PythonBoolean::Check(PyObject *py_obj) {
 | |
|   return py_obj ? PyBool_Check(py_obj) : false;
 | |
| }
 | |
| 
 | |
| bool PythonBoolean::GetValue() const {
 | |
|   return m_py_obj ? PyObject_IsTrue(m_py_obj) : false;
 | |
| }
 | |
| 
 | |
| void PythonBoolean::SetValue(bool value) {
 | |
|   *this = Take<PythonBoolean>(PyBool_FromLong(value));
 | |
| }
 | |
| 
 | |
| StructuredData::BooleanSP PythonBoolean::CreateStructuredBoolean() const {
 | |
|   StructuredData::BooleanSP result(new StructuredData::Boolean);
 | |
|   result->SetValue(GetValue());
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // PythonList
 | |
| 
 | |
| PythonList::PythonList(PyInitialValue value) {
 | |
|   if (value == PyInitialValue::Empty)
 | |
|     *this = Take<PythonList>(PyList_New(0));
 | |
| }
 | |
| 
 | |
| PythonList::PythonList(int list_size) {
 | |
|   *this = Take<PythonList>(PyList_New(list_size));
 | |
| }
 | |
| 
 | |
| bool PythonList::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
|   return PyList_Check(py_obj);
 | |
| }
 | |
| 
 | |
| uint32_t PythonList::GetSize() const {
 | |
|   if (IsValid())
 | |
|     return PyList_GET_SIZE(m_py_obj);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| PythonObject PythonList::GetItemAtIndex(uint32_t index) const {
 | |
|   if (IsValid())
 | |
|     return PythonObject(PyRefType::Borrowed, PyList_GetItem(m_py_obj, index));
 | |
|   return PythonObject();
 | |
| }
 | |
| 
 | |
| void PythonList::SetItemAtIndex(uint32_t index, const PythonObject &object) {
 | |
|   if (IsAllocated() && object.IsValid()) {
 | |
|     // PyList_SetItem is documented to "steal" a reference, so we need to
 | |
|     // convert it to an owned reference by incrementing it.
 | |
|     Py_INCREF(object.get());
 | |
|     PyList_SetItem(m_py_obj, index, object.get());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void PythonList::AppendItem(const PythonObject &object) {
 | |
|   if (IsAllocated() && object.IsValid()) {
 | |
|     // `PyList_Append` does *not* steal a reference, so do not call `Py_INCREF`
 | |
|     // here like we do with `PyList_SetItem`.
 | |
|     PyList_Append(m_py_obj, object.get());
 | |
|   }
 | |
| }
 | |
| 
 | |
| StructuredData::ArraySP PythonList::CreateStructuredArray() const {
 | |
|   StructuredData::ArraySP result(new StructuredData::Array);
 | |
|   uint32_t count = GetSize();
 | |
|   for (uint32_t i = 0; i < count; ++i) {
 | |
|     PythonObject obj = GetItemAtIndex(i);
 | |
|     result->AddItem(obj.CreateStructuredObject());
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // PythonTuple
 | |
| 
 | |
| PythonTuple::PythonTuple(PyInitialValue value) {
 | |
|   if (value == PyInitialValue::Empty)
 | |
|     *this = Take<PythonTuple>(PyTuple_New(0));
 | |
| }
 | |
| 
 | |
| PythonTuple::PythonTuple(int tuple_size) {
 | |
|   *this = Take<PythonTuple>(PyTuple_New(tuple_size));
 | |
| }
 | |
| 
 | |
| PythonTuple::PythonTuple(std::initializer_list<PythonObject> objects) {
 | |
|   m_py_obj = PyTuple_New(objects.size());
 | |
| 
 | |
|   uint32_t idx = 0;
 | |
|   for (auto object : objects) {
 | |
|     if (object.IsValid())
 | |
|       SetItemAtIndex(idx, object);
 | |
|     idx++;
 | |
|   }
 | |
| }
 | |
| 
 | |
| PythonTuple::PythonTuple(std::initializer_list<PyObject *> objects) {
 | |
|   m_py_obj = PyTuple_New(objects.size());
 | |
| 
 | |
|   uint32_t idx = 0;
 | |
|   for (auto py_object : objects) {
 | |
|     PythonObject object(PyRefType::Borrowed, py_object);
 | |
|     if (object.IsValid())
 | |
|       SetItemAtIndex(idx, object);
 | |
|     idx++;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool PythonTuple::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
|   return PyTuple_Check(py_obj);
 | |
| }
 | |
| 
 | |
| uint32_t PythonTuple::GetSize() const {
 | |
|   if (IsValid())
 | |
|     return PyTuple_GET_SIZE(m_py_obj);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| PythonObject PythonTuple::GetItemAtIndex(uint32_t index) const {
 | |
|   if (IsValid())
 | |
|     return PythonObject(PyRefType::Borrowed, PyTuple_GetItem(m_py_obj, index));
 | |
|   return PythonObject();
 | |
| }
 | |
| 
 | |
| void PythonTuple::SetItemAtIndex(uint32_t index, const PythonObject &object) {
 | |
|   if (IsAllocated() && object.IsValid()) {
 | |
|     // PyTuple_SetItem is documented to "steal" a reference, so we need to
 | |
|     // convert it to an owned reference by incrementing it.
 | |
|     Py_INCREF(object.get());
 | |
|     PyTuple_SetItem(m_py_obj, index, object.get());
 | |
|   }
 | |
| }
 | |
| 
 | |
| StructuredData::ArraySP PythonTuple::CreateStructuredArray() const {
 | |
|   StructuredData::ArraySP result(new StructuredData::Array);
 | |
|   uint32_t count = GetSize();
 | |
|   for (uint32_t i = 0; i < count; ++i) {
 | |
|     PythonObject obj = GetItemAtIndex(i);
 | |
|     result->AddItem(obj.CreateStructuredObject());
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // PythonDictionary
 | |
| 
 | |
| PythonDictionary::PythonDictionary(PyInitialValue value) {
 | |
|   if (value == PyInitialValue::Empty)
 | |
|     *this = Take<PythonDictionary>(PyDict_New());
 | |
| }
 | |
| 
 | |
| bool PythonDictionary::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
| 
 | |
|   return PyDict_Check(py_obj);
 | |
| }
 | |
| 
 | |
| uint32_t PythonDictionary::GetSize() const {
 | |
|   if (IsValid())
 | |
|     return PyDict_Size(m_py_obj);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| PythonList PythonDictionary::GetKeys() const {
 | |
|   if (IsValid())
 | |
|     return PythonList(PyRefType::Owned, PyDict_Keys(m_py_obj));
 | |
|   return PythonList(PyInitialValue::Invalid);
 | |
| }
 | |
| 
 | |
| PythonObject PythonDictionary::GetItemForKey(const PythonObject &key) const {
 | |
|   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) {
 | |
|   Error error = SetItem(key, value);
 | |
|   if (error)
 | |
|     llvm::consumeError(std::move(error));
 | |
| }
 | |
| 
 | |
| StructuredData::DictionarySP
 | |
| PythonDictionary::CreateStructuredDictionary() const {
 | |
|   StructuredData::DictionarySP result(new StructuredData::Dictionary);
 | |
|   PythonList keys(GetKeys());
 | |
|   uint32_t num_keys = keys.GetSize();
 | |
|   for (uint32_t i = 0; i < num_keys; ++i) {
 | |
|     PythonObject key = keys.GetItemAtIndex(i);
 | |
|     PythonObject value = GetItemForKey(key);
 | |
|     StructuredData::ObjectSP structured_value = value.CreateStructuredObject();
 | |
|     result->AddItem(key.Str().GetString(), structured_value);
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| PythonModule PythonModule::BuiltinsModule() {
 | |
| #if PY_MAJOR_VERSION >= 3
 | |
|   return AddModule("builtins");
 | |
| #else
 | |
|   return AddModule("__builtin__");
 | |
| #endif
 | |
| }
 | |
| 
 | |
| PythonModule PythonModule::MainModule() { return AddModule("__main__"); }
 | |
| 
 | |
| PythonModule PythonModule::AddModule(llvm::StringRef module) {
 | |
|   std::string str = module.str();
 | |
|   return PythonModule(PyRefType::Borrowed, PyImport_AddModule(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) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
| 
 | |
|   return PyModule_Check(py_obj);
 | |
| }
 | |
| 
 | |
| PythonDictionary PythonModule::GetDictionary() const {
 | |
|   if (!IsValid())
 | |
|     return PythonDictionary();
 | |
|   return Retain<PythonDictionary>(PyModule_GetDict(m_py_obj));
 | |
| }
 | |
| 
 | |
| bool PythonCallable::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
| 
 | |
|   return PyCallable_Check(py_obj);
 | |
| }
 | |
| 
 | |
| #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'])
 | |
| 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)
 | |
| )";
 | |
| #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();
 | |
|   long long count =
 | |
|       cantFail(As<long long>(pyarginfo.get().GetAttribute("count")));
 | |
|   bool has_varargs =
 | |
|       cantFail(As<bool>(pyarginfo.get().GetAttribute("has_varargs")));
 | |
|   result.max_positional_args = has_varargs ? ArgInfo::UNBOUNDED : count;
 | |
| 
 | |
| #else
 | |
|   PyObject *py_func_obj;
 | |
|   bool is_bound_method = false;
 | |
|   bool is_class = false;
 | |
| 
 | |
|   if (PyType_Check(m_py_obj) || PyClass_Check(m_py_obj)) {
 | |
|     auto init = GetAttribute("__init__");
 | |
|     if (!init)
 | |
|       return init.takeError();
 | |
|     py_func_obj = init.get().get();
 | |
|     is_class = true;
 | |
|   } else {
 | |
|     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())
 | |
|       is_bound_method = true;
 | |
|   } else {
 | |
|     // see if this is a callable object with an __call__ method
 | |
|     if (!PyFunction_Check(py_func_obj)) {
 | |
|       PythonObject __call__ = GetAttributeValue("__call__");
 | |
|       if (__call__.IsValid()) {
 | |
|         auto __callable__ = __call__.AsType<PythonCallable>();
 | |
|         if (__callable__.IsValid()) {
 | |
|           py_func_obj = PyMethod_GET_FUNCTION(__callable__.get());
 | |
|           PythonObject im_self = __callable__.GetAttributeValue("im_self");
 | |
|           if (im_self.IsValid() && !im_self.IsNone())
 | |
|             is_bound_method = true;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!py_func_obj)
 | |
|     return result;
 | |
| 
 | |
|   PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(py_func_obj);
 | |
|   if (!code)
 | |
|     return result;
 | |
| 
 | |
|   auto count = code->co_argcount;
 | |
|   bool has_varargs = !!(code->co_flags & CO_VARARGS);
 | |
|   result.max_positional_args =
 | |
|       has_varargs ? ArgInfo::UNBOUNDED
 | |
|                   : (count - (int)is_bound_method) - (int)is_class;
 | |
| 
 | |
| #endif
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| constexpr unsigned
 | |
|     PythonCallable::ArgInfo::UNBOUNDED; // FIXME delete after c++17
 | |
| 
 | |
| PythonObject PythonCallable::operator()() {
 | |
|   return PythonObject(PyRefType::Owned, PyObject_CallObject(m_py_obj, nullptr));
 | |
| }
 | |
| 
 | |
| PythonObject PythonCallable::
 | |
| operator()(std::initializer_list<PyObject *> args) {
 | |
|   PythonTuple arg_tuple(args);
 | |
|   return PythonObject(PyRefType::Owned,
 | |
|                       PyObject_CallObject(m_py_obj, arg_tuple.get()));
 | |
| }
 | |
| 
 | |
| PythonObject PythonCallable::
 | |
| operator()(std::initializer_list<PythonObject> args) {
 | |
|   PythonTuple arg_tuple(args);
 | |
|   return PythonObject(PyRefType::Owned,
 | |
|                       PyObject_CallObject(m_py_obj, arg_tuple.get()));
 | |
| }
 | |
| 
 | |
| bool PythonFile::Check(PyObject *py_obj) {
 | |
|   if (!py_obj)
 | |
|     return false;
 | |
| #if PY_MAJOR_VERSION < 3
 | |
|   return PyFile_Check(py_obj);
 | |
| #else
 | |
|   // In Python 3, there is no `PyFile_Check`, and in fact PyFile is not even a
 | |
|   // 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`.
 | |
|   auto io_module = PythonModule::Import("io");
 | |
|   if (!io_module) {
 | |
|     llvm::consumeError(io_module.takeError());
 | |
|     return false;
 | |
|   }
 | |
|   auto iobase = io_module.get().Get("IOBase");
 | |
|   if (!iobase) {
 | |
|     llvm::consumeError(iobase.takeError());
 | |
|     return false;
 | |
|   }
 | |
|   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
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| PythonException::~PythonException() {
 | |
|   Py_XDECREF(m_exception_type);
 | |
|   Py_XDECREF(m_exception);
 | |
|   Py_XDECREF(m_traceback);
 | |
|   Py_XDECREF(m_repr_bytes);
 | |
| }
 | |
| 
 | |
| void PythonException::log(llvm::raw_ostream &OS) const { OS << toCString(); }
 | |
| 
 | |
| std::error_code PythonException::convertToErrorCode() const {
 | |
|   return llvm::inconvertibleErrorCode();
 | |
| }
 | |
| 
 | |
| 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 unavailable, an error occurred while reading it:\n";
 | |
|     return (message + llvm::toString(backtrace.takeError()));
 | |
|   }
 | |
| 
 | |
|   return std::move(backtrace.get());
 | |
| }
 | |
| 
 | |
| char PythonException::ID = 0;
 | |
| 
 | |
| llvm::Expected<File::OpenOptions>
 | |
| GetOptionsForPyObject(const PythonObject &obj) {
 | |
| #if PY_MAJOR_VERSION >= 3
 | |
|   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
 | |
|   PythonString py_mode = obj.GetAttributeValue("mode").AsType<PythonString>();
 | |
|   return File::GetOptionsFromMode(py_mode.GetString());
 | |
| #endif
 | |
| }
 | |
| 
 | |
| // 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;
 | |
|   }
 | |
| 
 | |
|   ~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();
 | |
| 
 | |
|   if (options.get() & File::eOpenOptionWrite) {
 | |
|     // 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;
 | |
| }
 | |
| 
 | |
| llvm::Expected<FileSP>
 | |
| PythonFile::ConvertToFileForcingUseOfScriptingIOMethods(bool borrowed) {
 | |
| 
 | |
|   assert(!PyErr_Occurred());
 | |
| 
 | |
|   if (!IsValid())
 | |
|     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, /*closefd=*/0);
 | |
| #else
 | |
|   // I'd like to pass ::fflush here if the file is writable,  so that
 | |
|   // when the python side destructs the file object it will be flushed.
 | |
|   // However, this would be dangerous.    It can cause fflush to be called
 | |
|   // after fclose if the python program keeps a reference to the file after
 | |
|   // the original lldb_private::File has been destructed.
 | |
|   //
 | |
|   // It's all well and good to ask a python program not to use a closed file
 | |
|   // but asking a python program to make sure objects get released in a
 | |
|   // particular order is not safe.
 | |
|   //
 | |
|   // The tradeoff here is that if a python 2 program wants to make sure this
 | |
|   // file gets flushed, they'll have to do it explicitly or wait untill the
 | |
|   // original lldb File itself gets flushed.
 | |
|   file_obj = PyFile_FromFile(file.GetStream(), py2_const_cast(""),
 | |
|                              py2_const_cast(mode), [](FILE *) { return 0; });
 | |
| #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);
 | |
| }
 | |
| 
 | |
| 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
 |