zturner created this revision.
zturner added reviewers: granata.enrico, clayborg, tfiala.
zturner added a subscriber: lldb-commits.

The goal here is to eventually replace some code in `python-wrapper.swig` with 
this code.  This way it can be properly unit tested and we can easily deal with 
Python version differences.

the first set of patches will focus on just getting the classes in with unit 
tests to confirm they work, and then I will replace the code in 
`python-wrapper.swig` with this code.

Note that the implementation of `ResolveName` here differs from the 
implementation of `ResolvePythonName` in `python-wrapper.swig`.  The two 
methods seem equivalent to me, with this one being more straightforward, 
generic, and efficient.

http://reviews.llvm.org/D14524

Files:
  source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
  source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
  unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp

Index: unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
===================================================================
--- unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
+++ unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
@@ -96,6 +96,66 @@
     EXPECT_EQ(original_refcnt + 1, borrowed_long.get()->ob_refcnt);
 }
 
+TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionNoDot)
+{
+    PythonObject sys_module = PythonObject::ResolveNameGlobal("sys");
+    EXPECT_TRUE(sys_module.IsAllocated());
+    EXPECT_TRUE(PythonModule::Check(sys_module.get()));
+}
+
+TEST_F(PythonDataObjectsTest, TestModuleNameResolutionNoDot)
+{
+    PythonObject sys_module = PythonObject::ResolveNameGlobal("sys");
+    PythonObject sys_path = sys_module.ResolveName("path");
+    PythonObject sys_version_info = sys_module.ResolveName("version_info");
+    EXPECT_TRUE(sys_path.IsAllocated());
+    EXPECT_TRUE(sys_version_info.IsAllocated());
+
+    EXPECT_TRUE(PythonList::Check(sys_path.get()));
+}
+
+TEST_F(PythonDataObjectsTest, TestTypeNameResolutionNoDot)
+{
+    PythonObject sys_module = PythonObject::ResolveNameGlobal("sys");
+    PythonObject sys_version_info = sys_module.ResolveName("version_info");
+
+    PythonObject version_info_type(PyRefType::Owned, PyObject_Type(sys_version_info.get()));
+    EXPECT_TRUE(version_info_type.IsAllocated());
+    PythonObject major_version_field = version_info_type.ResolveName("major");
+    EXPECT_TRUE(major_version_field.IsAllocated());
+}
+
+TEST_F(PythonDataObjectsTest, TestInstanceNameResolutionNoDot)
+{
+    PythonObject sys_module = PythonObject::ResolveNameGlobal("sys");
+    PythonObject sys_version_info = sys_module.ResolveName("version_info");
+    PythonObject major_version_field = sys_version_info.ResolveName("major");
+    PythonObject minor_version_field = sys_version_info.ResolveName("minor");
+
+    EXPECT_TRUE(major_version_field.IsAllocated());
+    EXPECT_TRUE(minor_version_field.IsAllocated());
+
+    PythonInteger major_version_value = major_version_field.AsType<PythonInteger>();
+    PythonInteger minor_version_value = minor_version_field.AsType<PythonInteger>();
+
+    EXPECT_EQ(PY_MAJOR_VERSION, major_version_value.GetInteger());
+    EXPECT_EQ(PY_MINOR_VERSION, minor_version_value.GetInteger());
+}
+
+TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionWithDot)
+{
+    PythonObject sys_path = PythonObject::ResolveNameGlobal("sys.path");
+    EXPECT_TRUE(sys_path.IsAllocated());
+    EXPECT_TRUE(PythonList::Check(sys_path.get()));
+
+    PythonInteger version_major = PythonObject::ResolveNameGlobal("sys.version_info.major").AsType<PythonInteger>();
+    PythonInteger version_minor = PythonObject::ResolveNameGlobal("sys.version_info.minor").AsType<PythonInteger>();
+    EXPECT_TRUE(version_major.IsAllocated());
+    EXPECT_TRUE(version_minor.IsAllocated());
+    EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger());
+    EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger());
+}
+
 TEST_F(PythonDataObjectsTest, TestPythonInteger)
 {
 // Test that integers behave correctly when wrapped by a PythonInteger.
Index: source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
===================================================================
--- source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
+++ source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
@@ -69,6 +69,7 @@
     Dictionary,
     List,
     String,
+    Module,
     File
 };
 
@@ -185,15 +186,6 @@
         return result;
     }
 
-    PyObjectType
-    GetObjectType() const;
-
-    PythonString
-    Repr ();
-        
-    PythonString
-    Str ();
-
     PythonObject &
     operator=(const PythonObject &other)
     {
@@ -201,6 +193,17 @@
         return *this;
     }
 
+    PyObjectType
+    GetObjectType() const;
+
+    PythonString Repr() const;
+
+    PythonString Str() const;
+
+    static PythonObject ResolveNameGlobal(llvm::StringRef name);
+
+    PythonObject ResolveName(llvm::StringRef name) const;
+
     bool
     HasAttribute(llvm::StringRef attribute) const;
 
@@ -338,6 +341,34 @@
     StructuredData::DictionarySP CreateStructuredDictionary() const;
 };
 
+class PythonModule : public PythonObject
+{
+  public:
+    PythonModule();
+    PythonModule(PyRefType type, PyObject *o);
+    PythonModule(const PythonModule &dict);
+
+    ~PythonModule() override;
+
+    static bool Check(PyObject *py_obj);
+
+    static PythonModule MainModule();
+
+    // Bring in the no-argument base class version
+    using PythonObject::Reset;
+
+    void Reset(PyRefType type, PyObject *py_obj) override;
+
+    PythonDictionary GetDictionary() const;
+
+    template <typename T>
+    T
+    ResolveNameAs(llvm::StringRef name) const
+    {
+        return ResolveName(name).AsType<T>();
+    }
+};
+
 class PythonFile : public PythonObject
 {
   public:
Index: source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
===================================================================
--- source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
+++ source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
@@ -67,6 +67,8 @@
     if (!IsAllocated())
         return PyObjectType::None;
 
+    if (PythonModule::Check(m_py_obj))
+        return PyObjectType::Module;
     if (PythonList::Check(m_py_obj))
         return PyObjectType::List;
     if (PythonDictionary::Check(m_py_obj))
@@ -81,7 +83,7 @@
 }
 
 PythonString
-PythonObject::Repr()
+PythonObject::Repr() const
 {
     if (!m_py_obj)
         return PythonString();
@@ -92,7 +94,7 @@
 }
 
 PythonString
-PythonObject::Str()
+PythonObject::Str() const
 {
     if (!m_py_obj)
         return PythonString();
@@ -102,6 +104,43 @@
     return PythonString(PyRefType::Owned, str);
 }
 
+PythonObject
+PythonObject::ResolveNameGlobal(llvm::StringRef name)
+{
+    return PythonModule::MainModule().ResolveName(name);
+}
+
+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_first_of('.');
+    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`.
+        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
 {
@@ -605,6 +644,62 @@
     return result;
 }
 
+PythonModule::PythonModule() : PythonObject()
+{
+}
+
+PythonModule::PythonModule(PyRefType type, PyObject *py_obj)
+{
+    Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a module
+}
+
+PythonModule::PythonModule(const PythonModule &dict) : PythonObject(dict)
+{
+}
+
+PythonModule::~PythonModule()
+{
+}
+
+PythonModule
+PythonModule::MainModule()
+{
+    return PythonModule(PyRefType::Borrowed, PyImport_AddModule("__main__"));
+}
+
+bool
+PythonModule::Check(PyObject *py_obj)
+{
+    if (!py_obj)
+        return false;
+
+    return PyModule_Check(py_obj);
+}
+
+void
+PythonModule::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 (!PythonModule::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());
+}
+
+PythonDictionary
+PythonModule::GetDictionary() const
+{
+    return PythonDictionary(PyRefType::Borrowed, PyModule_GetDict(m_py_obj));
+}
+
 PythonFile::PythonFile()
     : PythonObject()
 {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to