Jonas Devlieghere 9053767330
Remove Python 2 support from the ScriptInterpreter plugin
We dropped downstream support for Python 2 in the previous release. Now
that we have branched for the next release the window where this kind of
change could introduce conflicts is closing too. Start by getting rid of
Python 2 support in the Script Interpreter plugin.

Differential revision: https://reviews.llvm.org/D124429
2022-04-27 08:26:25 -07:00

1487 lines
42 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/LLDBLog.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 <cstdio>
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());
}
static bool python_is_finalizing() {
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7
return _Py_Finalizing != nullptr;
#else
return _Py_IsFinalizing();
#endif
}
void PythonObject::Reset() {
if (m_py_obj && Py_IsInitialized()) {
if (python_is_finalizing()) {
// Leak m_py_obj rather than crashing the process.
// https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure
} else {
PyGILState_STATE state = PyGILState_Ensure();
Py_DECREF(m_py_obj);
PyGILState_Release(state);
}
}
m_py_obj = nullptr;
}
Expected<long long> PythonObject::AsLongLong() const {
if (!m_py_obj)
return nullDeref();
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();
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();
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 (PythonBytes::Check(m_py_obj))
return PyObjectType::Bytes;
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 {
assert(PyGILState_Check());
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(
PythonObject(PyRefType::Borrowed, 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) {
PyObject *str = PyUnicode_FromStringAndSize(string.data(), string.size());
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;
return false;
}
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;
data = PyUnicode_AsUTF8AndSize(m_py_obj, &size);
if (!data)
return exception();
return llvm::StringRef(data, size);
}
size_t PythonString::GetSize() const {
if (IsValid()) {
#if PY_MINOR_VERSION >= 3
return PyUnicode_GetLength(m_py_obj);
#else
return PyUnicode_GetSize(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;
// Python 3 does not have PyInt_Check. There is only one type of integral
// value, long.
return PyLong_Check(py_obj);
}
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();
PyObject *o = PyDict_GetItemWithError(m_py_obj, key.get());
if (PyErr_Occurred())
return exception();
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() { return AddModule("builtins"); }
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;
// 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;
}
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 = GetLog(LLDBLog::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) {
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() && writable.get())
options |= File::eOpenOptionReadWrite;
else if (writable.get())
options |= File::eOpenOptionWriteOnly;
else if (readable.get())
options |= File::eOpenOptionReadOnly;
return options;
}
// 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
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
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();
File::OpenOptions rw =
options.get() & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
File::eOpenOptionReadWrite);
if (rw == File::eOpenOptionWriteOnly || rw == File::eOpenOptionReadWrite) {
// 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");
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;
}
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 (auto *pythonio = llvm::dyn_cast<PythonIOFile>(&file))
return Retain<PythonFile>(pythonio->GetPythonObject());
if (!mode) {
auto m = file.GetOpenMode();
if (!m)
return m.takeError();
mode = m.get();
}
PyObject *file_obj;
file_obj = PyFile_FromFd(file.GetDescriptor(), nullptr, mode, -1, nullptr,
"ignore", nullptr, /*closefd=*/0);
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);
PyObject *result = PyEval_EvalCode(code, globals.get(), locals.get());
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