Add a `PythonModule` class, and a root-level method for resolving names.

llvm-svn: 252765
This commit is contained in:
Zachary Turner 2015-11-11 17:59:49 +00:00
parent 7d7814ae8a
commit 7841efbb92
3 changed files with 196 additions and 12 deletions

View File

@ -67,6 +67,8 @@ 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 (PythonDictionary::Check(m_py_obj))
@ -81,7 +83,7 @@ PythonObject::GetObjectType() const
}
PythonString
PythonObject::Repr()
PythonObject::Repr() const
{
if (!m_py_obj)
return PythonString();
@ -92,7 +94,7 @@ PythonObject::Repr()
}
PythonString
PythonObject::Str()
PythonObject::Str() const
{
if (!m_py_obj)
return PythonString();
@ -102,6 +104,43 @@ PythonObject::Str()
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 @@ PythonDictionary::CreateStructuredDictionary() const
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()
{

View File

@ -69,6 +69,7 @@ enum class PyObjectType
Dictionary,
List,
String,
Module,
File
};
@ -185,15 +186,6 @@ public:
return result;
}
PyObjectType
GetObjectType() const;
PythonString
Repr ();
PythonString
Str ();
PythonObject &
operator=(const PythonObject &other)
{
@ -201,6 +193,21 @@ public:
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;
@ -224,7 +231,8 @@ public:
return T(PyRefType::Borrowed, m_py_obj);
}
StructuredData::ObjectSP CreateStructuredObject() const;
StructuredData::ObjectSP
CreateStructuredObject() const;
protected:
PyObject* m_py_obj;
@ -338,6 +346,27 @@ public:
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;
};
class PythonFile : public PythonObject
{
public:

View File

@ -96,6 +96,66 @@ TEST_F(PythonDataObjectsTest, TestBorrowedReferences)
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.