Fix swig typemap for SBEvent.
authorZachary Turner <zturner@google.com>
Mon, 25 Jan 2016 23:21:09 +0000 (23:21 +0000)
committerZachary Turner <zturner@google.com>
Mon, 25 Jan 2016 23:21:09 +0000 (23:21 +0000)
This needs to be able to handle bytes, strings, and bytearray objects.
In Python 2 this was easy because bytes and strings are the same thing,
but in Python 3 the 2 cases need to be handled separately.  So as not
to mix raw Python C API code with PythonDataObjects code, I've also
introduced a PythonByteArray class to PythonDataObjects to make the
paradigm used here consistent.

llvm-svn: 258741

lldb/scripts/Python/python-typemaps.swig
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
lldb/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp

index a09a0b7..566da4d 100644 (file)
 // typemap for an outgoing buffer
 // See also SBEvent::SBEvent(uint32_t event, const char *cstr, uint32_t cstr_len).
 %typemap(in) (const char *cstr, uint32_t cstr_len) {
-   if (PyString_Check($input)) {
-      $1 = (char *) PyString_AsString($input);
-      $2 = PyString_Size($input);
+   using namespace lldb_private;
+   if (PythonString::Check($input)) {
+      PythonString str(PyRefType::Borrowed, $input);
+      $1 = (char*)str.GetString().data();
+      $2 = str.GetSize();
    }
-   else if(PyByteArray_Check($input)) {
-      $1 = (char *) PyByteArray_AsString($input);
-      $2 = PyByteArray_Size($input);
+   else if(PythonByteArray::Check($input)) {
+      PythonByteArray bytearray(PyRefType::Borrowed, $input);
+      $1 = (char*)bytearray.GetBytes().data();
+      $2 = bytearray.GetSize();
+   }
+   else if (PythonBytes::Check($input)) {
+      PythonBytes bytes(PyRefType::Borrowed, $input);
+      $1 = (char*)bytes.GetBytes().data();
+      $2 = bytes.GetSize();
    }
    else {
       PyErr_SetString(PyExc_ValueError, "Expecting a string");
index f0db8a6..5c7d79e 100644 (file)
@@ -83,6 +83,8 @@ PythonObject::GetObjectType() const
     if (PythonBytes::Check(m_py_obj))
         return PyObjectType::Bytes;
 #endif
+    if (PythonByteArray::Check(m_py_obj))
+        return PyObjectType::ByteArray;
     if (PythonInteger::Check(m_py_obj))
         return PyObjectType::Integer;
     if (PythonFile::Check(m_py_obj))
@@ -218,6 +220,8 @@ PythonObject::CreateStructuredObject() const
             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:
@@ -323,6 +327,87 @@ PythonBytes::CreateStructuredString() const
     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);
+    Reset(PyRefType::Owned, PyByteArray_FromStringAndSize(str, length));
+}
+
+PythonByteArray::PythonByteArray(PyRefType type, PyObject *o)
+{
+    Reset(type, o);
+}
+
+PythonByteArray::PythonByteArray(const PythonBytes &object) : PythonObject(object)
+{
+}
+
+PythonByteArray::~PythonByteArray()
+{
+}
+
+bool
+PythonByteArray::Check(PyObject *py_obj)
+{
+    if (!py_obj)
+        return false;
+    if (PyByteArray_Check(py_obj))
+        return true;
+    return false;
+}
+
+void
+PythonByteArray::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 (!PythonByteArray::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());
+}
+
+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
 //----------------------------------------------------------------------
index 6d5a11b..2bcb967 100644 (file)
@@ -75,6 +75,7 @@ enum class PyObjectType
     List,
     String,
     Bytes,
+    ByteArray,
     Module,
     Callable,
     Tuple,
@@ -293,6 +294,39 @@ public:
     CreateStructuredString() const;
 };
 
+class PythonByteArray : public PythonObject
+{
+public:
+    PythonByteArray();
+    explicit PythonByteArray(llvm::ArrayRef<uint8_t> bytes);
+    PythonByteArray(const uint8_t *bytes, size_t length);
+    PythonByteArray(PyRefType type, PyObject *o);
+    PythonByteArray(const PythonBytes &object);
+
+    ~PythonByteArray() override;
+
+    static bool
+    Check(PyObject *py_obj);
+
+    // Bring in the no-argument base class version
+    using PythonObject::Reset;
+
+    void
+    Reset(PyRefType type, PyObject *py_obj) override;
+
+    llvm::ArrayRef<uint8_t>
+    GetBytes() const;
+
+    size_t
+    GetSize() const;
+
+    void
+    SetBytes(llvm::ArrayRef<uint8_t> stringbytes);
+
+    StructuredData::StringSP
+    CreateStructuredString() const;
+};
+
 class PythonString : public PythonObject
 {
 public:
index 605f023..e2b40d5 100644 (file)
@@ -224,6 +224,20 @@ TEST_F(PythonDataObjectsTest, TestPythonBytes)
     EXPECT_EQ(0, ::memcmp(bytes.data(), test_bytes, bytes.size()));
 }
 
+TEST_F(PythonDataObjectsTest, TestPythonByteArray)
+{
+    static const char *test_bytes = "PythonDataObjectsTest::TestPythonByteArray";
+    llvm::StringRef orig_bytes(test_bytes);
+    PyObject *py_bytes = PyByteArray_FromStringAndSize(test_bytes, orig_bytes.size());
+    EXPECT_TRUE(PythonByteArray::Check(py_bytes));
+    PythonByteArray python_bytes(PyRefType::Owned, py_bytes);
+    EXPECT_EQ(PyObjectType::ByteArray, python_bytes.GetObjectType());
+
+    llvm::ArrayRef<uint8_t> after_bytes = python_bytes.GetBytes();
+    EXPECT_EQ(after_bytes.size(), orig_bytes.size());
+    EXPECT_EQ(0, ::memcmp(orig_bytes.data(), test_bytes, orig_bytes.size()));
+}
+
 TEST_F(PythonDataObjectsTest, TestPythonString)
 {
     // Test that strings behave correctly when wrapped by a PythonString.