From a140514733e0ab0310688c727b70ba3e2a4a8ddc Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Wed, 11 Nov 2015 19:42:27 +0000 Subject: Create `PythonTuple` and `PythonCallable` wrapper classes. This adds PythonTuple and PythonCallable classes to PythonDataObjects. Additionally, unit tests are provided that exercise this functionality, including invoking manipulating and checking for validity of tuples, and invoking and checking for validity of callables using a variety of different syntaxes. The goal here is to eventually replace the code in python-wrapper.swig that directly uses the Python C API to deal with callables and name resolution with this code that can be more easily tested and debugged. llvm-svn: 252787 --- .../ScriptInterpreter/Python/PythonDataObjects.cpp | 255 ++++++++++++++++++++- 1 file changed, 251 insertions(+), 4 deletions(-) (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp') diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index a621c05338d..0c5ba7d343a 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -71,6 +71,8 @@ PythonObject::GetObjectType() const 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)) @@ -79,6 +81,8 @@ PythonObject::GetObjectType() const return PyObjectType::Integer; if (PythonFile::Check(m_py_obj)) return PyObjectType::File; + if (PythonCallable::Check(m_py_obj)) + return PyObjectType::Callable; return PyObjectType::Unknown; } @@ -105,9 +109,20 @@ PythonObject::Str() const } PythonObject -PythonObject::ResolveNameGlobal(llvm::StringRef name) +PythonObject::ResolveNameWithDictionary(llvm::StringRef name, PythonDictionary dict) { - return PythonModule::MainModule().ResolveName(name); + size_t dot_pos = name.find_first_of('.'); + 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 @@ -128,7 +143,7 @@ PythonObject::ResolveName(llvm::StringRef name) const if (dot_pos == llvm::StringRef::npos) { // No dots in the name, we should be able to find the value immediately - // as an attribute of `use_object`. + // as an attribute of `m_py_obj`. return GetAttributeValue(name); } @@ -544,6 +559,132 @@ PythonList::CreateStructuredArray() const return result; } +//---------------------------------------------------------------------- +// PythonTuple +//---------------------------------------------------------------------- + +PythonTuple::PythonTuple(PyInitialValue value) + : PythonObject() +{ + if (value == PyInitialValue::Empty) + Reset(PyRefType::Owned, PyTuple_New(0)); +} + +PythonTuple::PythonTuple(int tuple_size) + : PythonObject() +{ + Reset(PyRefType::Owned, PyTuple_New(tuple_size)); +} + +PythonTuple::PythonTuple(PyRefType type, PyObject *py_obj) + : PythonObject() +{ + Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a tuple +} + +PythonTuple::PythonTuple(const PythonTuple &tuple) + : PythonObject(tuple) +{ +} + +PythonTuple::PythonTuple(std::initializer_list 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 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++; + } +} + +PythonTuple::~PythonTuple() +{ +} + +bool +PythonTuple::Check(PyObject *py_obj) +{ + if (!py_obj) + return false; + return PyTuple_Check(py_obj); +} + +void +PythonTuple::Reset(PyRefType type, PyObject *py_obj) +{ + // Grab the desired reference type so that if we end up rejecting + // `py_obj` it still gets decremented if necessary. + PythonObject result(type, py_obj); + + if (!PythonTuple::Check(py_obj)) + { + PythonObject::Reset(); + return; + } + + // Calling PythonObject::Reset(const PythonObject&) will lead to stack overflow since it calls + // back into the virtual implementation. + PythonObject::Reset(PyRefType::Borrowed, result.get()); +} + +uint32_t +PythonTuple::GetSize() const +{ + if (IsValid()) + return PyTuple_GET_SIZE(m_py_obj); + 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 //---------------------------------------------------------------------- @@ -661,10 +802,27 @@ PythonModule::~PythonModule() { } +PythonModule +PythonModule::BuiltinsModule() +{ +#if PY_MAJOR_VERSION >= 3 + return AddModule("builtins"); +#else + return AddModule("__builtin__"); +#endif +} + PythonModule PythonModule::MainModule() { - return PythonModule(PyRefType::Borrowed, PyImport_AddModule("__main__")); + return AddModule("__main__"); +} + +PythonModule +PythonModule::AddModule(llvm::StringRef module) +{ + std::string str = module.str(); + return PythonModule(PyRefType::Borrowed, PyImport_AddModule(str.c_str())); } bool @@ -700,6 +858,95 @@ PythonModule::GetDictionary() const return PythonDictionary(PyRefType::Borrowed, PyModule_GetDict(m_py_obj)); } +PythonCallable::PythonCallable() : PythonObject() +{ +} + +PythonCallable::PythonCallable(PyRefType type, PyObject *py_obj) +{ + Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a callable +} + +PythonCallable::PythonCallable(const PythonCallable &callable) + : PythonObject(callable) +{ +} + +PythonCallable::~PythonCallable() +{ +} + +bool +PythonCallable::Check(PyObject *py_obj) +{ + if (!py_obj) + return false; + + return PyCallable_Check(py_obj); +} + +void +PythonCallable::Reset(PyRefType type, PyObject *py_obj) +{ + // Grab the desired reference type so that if we end up rejecting + // `py_obj` it still gets decremented if necessary. + PythonObject result(type, py_obj); + + if (!PythonCallable::Check(py_obj)) + { + PythonObject::Reset(); + return; + } + + // Calling PythonObject::Reset(const PythonObject&) will lead to stack overflow since it calls + // back into the virtual implementation. + PythonObject::Reset(PyRefType::Borrowed, result.get()); +} + + +void +PythonCallable::GetNumArguments(size_t &num_args, bool &has_varargs, bool &has_kwargs) const +{ + num_args = 0; + has_varargs = false; + has_kwargs = false; + if (!IsValid()) + return; + + PyObject *py_func_obj = m_py_obj; + if (PyMethod_Check(py_func_obj)) + py_func_obj = PyMethod_GET_FUNCTION(py_func_obj); + + if (!py_func_obj) + return; + + PyCodeObject* code = (PyCodeObject*)PyFunction_GET_CODE(py_func_obj); + if (!code) + return; + + num_args = code->co_argcount; + if (code->co_flags & CO_VARARGS) + has_varargs = true; + if (code->co_flags & CO_VARKEYWORDS) + has_kwargs = true; +} + +PythonObject +PythonCallable::operator ()(std::initializer_list args) +{ + PythonTuple arg_tuple(args); + return PythonObject(PyRefType::Owned, + PyObject_CallObject(m_py_obj, arg_tuple.get())); +} + +PythonObject +PythonCallable::operator ()(std::initializer_list args) +{ + PythonTuple arg_tuple(args); + return PythonObject(PyRefType::Owned, + PyObject_CallObject(m_py_obj, arg_tuple.get())); +} + PythonFile::PythonFile() : PythonObject() { -- cgit v1.2.3