diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp | 1570 |
1 files changed, 1570 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/gnu/llvm/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp new file mode 100644 index 00000000000..e5a67653e33 --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -0,0 +1,1570 @@ +//===-- PythonDataObjects.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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.get().AsLongLong(); +} + +template <> +Expected<std::string> python::As<std::string>(Expected<PythonObject> &&obj) { + if (!obj) + return obj.takeError(); + PyObject *str_obj = PyObject_Str(obj.get().get()); + if (!obj) + return llvm::make_error<PythonException>(); + auto str = Take<PythonString>(str_obj); + auto utf8 = str.AsUTF8(); + if (!utf8) + return utf8.takeError(); + return utf8.get(); +} + +void StructuredPythonObject::Serialize(llvm::json::OStream &s) const { + s.value(llvm::formatv("Python Obj: {0:X}", GetValue()).str()); +} + +// PythonObject + +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 + return PyUnicode_GetSize(m_py_obj); +#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 +} + +int64_t PythonInteger::GetInteger() const { + if (m_py_obj) { + assert(PyLong_Check(m_py_obj) && + "PythonInteger::GetInteger has a PyObject that isn't a PyLong"); + + int overflow = 0; + int64_t result = PyLong_AsLongLongAndOverflow(m_py_obj, &overflow); + if (overflow != 0) { + // We got an integer that overflows, like 18446744072853913392L we can't + // use PyLong_AsLongLong() as it will return 0xffffffffffffffff. If we + // use the unsigned long long it will work as expected. + const uint64_t uval = PyLong_AsUnsignedLongLong(m_py_obj); + result = static_cast<int64_t>(uval); + } + return result; + } + return UINT64_MAX; +} + +void PythonInteger::SetInteger(int64_t value) { + *this = Take<PythonInteger>(PyLong_FromLongLong(value)); +} + +StructuredData::IntegerSP PythonInteger::CreateStructuredInteger() const { + StructuredData::IntegerSP result(new StructuredData::Integer); + result->SetValue(GetInteger()); + 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 unavailble, 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 |